Skip to main content

flow-next-deps

Show spec dependency graph and execution order. Use when asking 'what's blocking what', 'execution order', 'dependency graph', 'what order should specs run', 'critical path', 'which specs can run in parallel'.

跳到安装

来源信息

仓库
gmickel/flow-next
最近来源活动
2026年9月12日 17:27
检测到的 SKILL.md 语言
英语
星标
699
分支
55

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
2 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
flow-next-deps
description
Show spec dependency graph and execution order. Use when asking 'what's blocking what', 'execution order', 'dependency graph', 'what order should specs run', 'critical path', 'which specs can run in parallel'.
# Flow-Next Dependency Graph Visualize spec dependencies, blocking chains, and execution phases. ## Preamble flowctl is bundled with the plugin (not on PATH). Define once; subsequent blocks use `$FLOWCTL`: ```bash FLOWCTL="${CODEX_HOME:-$HOME/.codex}/scripts/flowctl" [ -x "$FLOWCTL" ] || FLOWCTL="<plugin-root>/scripts/flowctl" # <plugin-root> = the directory two levels above this skill's SKILL.md file (the harness gave you that file's absolute path when the skill loaded); substitute it literally [ -x "$FLOWCTL" ] || FLOWCTL=".flow/bin/flowctl" ``` ## Setup ```bash $FLOWCTL detect --json | jq -e '.exists' >/dev/null && echo "OK: .flow/ exists" || echo "ERROR: run $FLOWCTL init" command -v jq >/dev/null 2>&1 && echo "OK: jq installed" || echo "ERROR: brew install jq" ``` ## Step 1: Gather Spec Data Build a consolidated view of all specs with their dependencies — a single heavy per-spec loop for the whole skill. Steps 2 and 3 reuse the cached file (bash vars do not survive across prompt turns, so the cache is a file at a literal agent-composed path — compose `<suffix>` once, e.g. 4 random chars, and reuse the same literal path in every later block): ```bash # ONE gather — Steps 2 and 3 read this file; never re-run the per-spec loop SPECS_FILE="${TMPDIR:-/tmp}/flow-deps-specs-<suffix>.json" $FLOWCTL specs --json | jq -r '.specs[].id' | while read id; do $FLOWCTL show "$id" --json | jq -c '{ id: .id, title: .title, status: .status, plan_review: .plan_review_status, deps: (.depends_on_epics // []) }' done | jq -s '.' > "$SPECS_FILE" cat "$SPECS_FILE" ``` ### Done when - `$SPECS_FILE` exists at the composed literal path and parses as a JSON array with one object per spec returned by `flowctl specs --json`. - The per-spec `flowctl show` loop has run exactly once for this invocation. **Steps 2 and 3 read that file.** A second run of the gather loop has broken this. ## Step 2: Identify Blocking Chains Determine which specs are ready vs blocked (pure jq, works on any shell): ```bash # Reuse the Step 1 gather — same literal path, NO re-fetch (one heavy loop total) SPECS_FILE="${TMPDIR:-/tmp}/flow-deps-specs-<suffix>.json" # Compute blocking status jq -r ' # Build status lookup (map({(.id): .status}) | add // {}) as $status | # Check each non-done spec .[] | select(.status != "done") | .id as $id | .title as $title | # Find deps that are not done ([.deps[] | select($status[.] != "done")] | join(", ")) as $blocked_by | if ($blocked_by | length) == 0 then "READY: \($id) - \($title)" else "BLOCKED: \($id) - \($title) (by: \($blocked_by))" end ' "$SPECS_FILE" ``` ### Done when - Every non-`done` spec in `$SPECS_FILE` emitted exactly one `READY:` or `BLOCKED:` line, and each `BLOCKED:` line names the specific deps holding it. - The jq ran against the cached file, not a fresh `flowctl show` sweep. ## Step 3: Compute Execution Phases Group specs into parallel execution phases: ```bash # Reuse the Step 1 gather — same literal path, NO re-fetch (one heavy loop total) SPECS_FILE="${TMPDIR:-/tmp}/flow-deps-specs-<suffix>.json" # Phase assignment algorithm (run in jq for reliability) jq ' # Build status lookup (map({(.id): .status}) | add // {}) as $status | # Filter to non-done specs [.[] | select(.status != "done")] as $open | # Assign phases iteratively reduce range(10) as $phase ( {assigned: [], result: [], open: $open}; .assigned as $assigned | .open as $remaining | # Find specs not yet assigned whose deps are all done or in earlier phases ([.open[] | select( ([.id] | inside($assigned) | not) and ((.deps // []) | all(. as $d | $status[$d] == "done" or ($assigned | index($d)))) )] | map(.id)) as $ready | if ($ready | length) > 0 then .result += [{phase: ($phase + 1), specs: [.open[] | select(.id | IN($ready[]))]}] | .assigned += $ready else . end ) | # Emit the phases AND the residue: any open spec never assigned is UNRESOLVABLE — a # dependency cycle (A→B→A), a dep on a missing/closed spec, or a chain deeper than 10. # Dropping it silently is the one way /deps gives a WRONG answer (the graph it exists to # expose hides the deadlock). Surface it, with the offending deps for diagnosis. .assigned as $asg | { phases: .result, deadlocked: [ $open[] | select(.id as $i | ($asg | index($i)) | not) | { id, status, unresolved_deps: [ (.deps // [])[] | select(. as $d | ($asg | index($d)) or ($status[$d] == "done") | not) ] } ] } ' "$SPECS_FILE" ``` ### Done when - The jq result carries both `.phases` and `.deadlocked`, and every open spec appears in exactly one of them. - **The report is rendered from `.phases` and `.deadlocked`.** Phases narrated from a reading of the spec titles have broken this. ## Output Format Present results as: ```markdown ## Spec Dependency Graph ### Status Overview | Spec | Title | Status | Dependencies | Blocked By | |------|-------|--------|--------------|------------| | **fn-1-add-auth** | Add Authentication | **READY** | - | - | | fn-2-add-oauth | Add OAuth Login | blocked | fn-1-add-auth | fn-1-add-auth | | fn-3-user-profile | User Profile Page | blocked | fn-1-add-auth, fn-2-add-oauth | fn-2-add-oauth | ### Execution Phases Render from the jq result's `.phases`: | Phase | Specs | Can Start | |-------|-------|-----------| | **1** | fn-1-add-auth | **NOW** | | 2 | fn-2-add-oauth | After Phase 1 | | 3 | fn-3-user-profile | After Phase 2 | ### ⚠️ Deadlocked / Unresolvable **This section renders exactly when `.deadlocked` is non-empty** — and when it does, it is the most important part of the report. Each entry is an open spec that could not be placed in any phase: a dependency **cycle**, a dep on a **missing/closed** spec, or a chain deeper than 10. These are invisible to `ready`/`flow --auto` (they just never become ready) — this is the one place the graph surfaces them. | Spec | Status | Unresolved deps | Likely cause | |------|--------|-----------------|--------------| | fn-7-x | blocked | fn-9-y | fn-9-y not found / closed, or a cycle fn-7↔fn-9 | For each, state the likely cause: if two deadlocked specs list each other → **cycle** (fix with `flowctl spec rm-dep`); if an unresolved dep isn't among the open specs → **missing or closed dependency**. **Every entry of `.deadlocked` reaches the report.** A report that lists phases while silently dropping an open spec has broken this. ### Critical Path fn-1-add-auth → fn-2-add-oauth → fn-3-user-profile (3 phases) ``` **This skill is read-only inspection.** Edge changes are reported as commands for the operator to run — `flowctl spec add-dep <spec> <dep>` / `rm-dep <spec> <dep>`. A run that executes either command itself, or edits a spec file, has broken this. ## Quick One-Liner For a fast dependency check: ```bash $FLOWCTL specs --json | jq -r '.specs[] | select(.status != "done") | "\(.id): \(.title) [\(.status)]"' ``` ## When to Use - "What's the execution order for specs?" - "What's blocking progress?" - "Show me the dependency graph" - "What's the critical path?" - "Which specs can run in parallel?" - "Why is Ralph working on X?" - "What should I work on next?"
在 GitHub 查看