skills/flow-decompose-project/SKILL.md
Decompose a large project into GitHub issues with sub-issue and blocked-by relationships, milestones, and phase labels.
npx skillsauth add benkruger/flow flow-decompose-projectInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Decompose a large project into many GitHub issues with native sub-issue
relationships, blocked-by dependencies, milestones, and phase labels.
Produces a fully linked issue graph ready for autonomous execution via
/flow:flow-start or /flow:flow-orchestrate.
/flow:flow-decompose-project <project description>
/flow:flow-decompose-project --step 2 --id <id>
/flow:flow-decompose-project --step 3 --id <id>
/flow:flow-decompose-project --step 4 --id <id>
/flow:flow-decompose-project --step 5 --id <id>
/flow:flow-decompose-project --step 6 --id <id>
/flow:flow-decompose-project <project description> — start from Step 1/flow:flow-decompose-project --step N --id <id> — self-invocation: resume at Step N"Project description required. Usage:
/flow:flow-decompose-project <project description>"
No interactive prompt. The user re-runs the command with arguments.
</HARD-GATE>This skill creates shared GitHub state (issues, milestones, labels,
sub-issue relationships, dependencies). Session state is tracked in
.flow-states/decompose-project-<id>.json with a unique ID per session
to prevent concurrent collisions. Issue creation is idempotent by title.
If --step N --id <id> was passed, this is a self-invocation from a
previous step. The --id flag carries the session-scoped identifier
generated in Step 1. Skip the Announce banner and jump directly to the
Resume Check, using the provided <id> for all file paths.
If no --step flag was passed, proceed to Announce and then Step 1.
At the very start, output the following banner in your response (not via Bash) inside a fenced code block:
```text
──────────────────────────────────────────────────
FLOW v2.4.0 — flow:flow-decompose-project — STARTING
──────────────────────────────────────────────────
```
Immediately after the banner, write the per-session "utility skill in progress" marker so the Stop hook refuses turn-end while this skill is running. Without the marker the model returns control to the user when the decompose:decompose Skill tool returns mid-pipeline at Step 1, breaking the unattended-flow contract this skill promises across its six-step chain.
Rust resolves the active session_id at the CLI boundary by reading
the CLAUDE_CODE_SESSION_ID env var Claude Code supplies to every
Bash subprocess (Claude Code 2.1.132+); on older Claude Code
installs it falls back to the SessionStart capture file. On
2.1.132+ the per-subprocess env value matches what the Stop hook
receives in its stdin payload, so set-time and clear-time resolve
to the same id regardless of concurrent Claude Code activity. The
bash invocation below passes --skill only; Rust supplies the
session_id itself.
${CLAUDE_PLUGIN_ROOT}/bin/flow set-utility-in-progress --skill flow:flow-decompose-project
If the marker-write call returns status: error with
no session_id available (no env var AND no capture file — rare,
only on Claude Code installs without per-subprocess env support and
without a SessionStart capture file), the skill proceeds without
the marker. The Stop hook treats a missing marker as a non-block,
so the skill runs without protection but does not break.
The marker is held across the entire Step 1 → Step 2 → Step 3 →
Step 4 → Step 5 → Step 6 chain. Step Dispatch (above) skips the
Announce banner on --step N self-invocations, so the marker-set
call fires exactly once at the first invocation. The marker is
cleared at every skill-exit boundary: Step 1 Cancel, Step 2
Cancel, Step 3 epic Cancel-filing-this-issue, Step 3 epic
Cancel-whole-skill, Step 4 child Cancel-whole-skill, and the
Step 6 success path. Every other path holds the marker until
Step 6 completes.
On Claude Code installs without the per-subprocess env var, the
capture-file fallback resolves session_id independently at set and
clear time. A second Claude Code session whose SessionStart hook
overwrites the capture file between this skill's set and clear
calls can leave the marker orphaned at the original id. Recovery
is rm ~/.claude/flow/utility-in-progress-*.json after the skill
completes; the Stop hook treats a missing marker as a non-block.
Use the Read tool to read .flow-states/decompose-project-<id>.json, where
<id> is the session identifier from the --id flag. If no --id flag
was passed (first run), there is no file to read — proceed to Step 1.
Dispatch based on decompose_step:
0 or absent — proceed to Step 11 — skip to Step 22 — skip to Step 33 — skip to Step 44 — skip to Step 55 — skip to Step 6Output in your response (not via Bash) inside a fenced code block:
```text
── Step 1 of 6: Describe and Decompose ──
```
Take the user's project description and invoke the decompose:decompose
plugin via the Skill tool. The decomposition must include deep codebase
exploration using Glob, Grep, and Read to ground every finding.
Present the full DAG synthesis to the user.
After presenting the synthesis, generate a session ID using the Bash tool:
${CLAUDE_PLUGIN_ROOT}/bin/flow generate-id
Write {"decompose_step": 1} to
.flow-states/decompose-project-<id>.json using the Write tool.
Save the full decompose output to
.flow-states/decompose-project-<id>-dag.md using the Write tool.
Then invoke flow:flow-decompose-project --step 2 --id <id> using
the Skill tool as your final action.
The user's invocation of /flow:flow-decompose-project is the
single authorization for the decompose-and-file pipeline; the
DAG synthesis is unattended infrastructure that Step 2 builds
the issue list from. A second confirmation gate between Step 1
and Step 2 would break the single-signal contract these skills
promise.
Output in your response (not via Bash) inside a fenced code block:
```text
── Step 2 of 6: Review Issue List ──
```
Use the Read tool to read the DAG output from
.flow-states/decompose-project-<id>-dag.md.
From the DAG synthesis, build a complete issue list:
For both the parent epic AND each child issue, draft:
decomposed plus the auto-derived phase label
(child issues only; the parent epic is filed without these
labels in Step 3)Every issue body — parent epic AND every child — uses this section order:
## Summary heading) — what is broken, missing, or
inadequate. Include observable behavior, evidence from the
codebase (file paths, line numbers), and user impact. Grounded
in the exploration the decompose step already performed.### subsections in order:
#### Task N:
headers (these become ### Task N: headings in the
.flow-states/<branch>/plan.md file after bin/flow plan-from-issue extraction). The #### Task N: header
format — not a numbered list — is the heading shape
count_tasks recognises to populate code_tasks_total.Wrap the Implementation Plan in the FLOW-PLAN sentinel pair.
Place the literal HTML comment <!-- FLOW-PLAN-BEGIN --> on its
own line immediately before the ## Implementation Plan heading,
and the literal HTML comment <!-- FLOW-PLAN-END --> on its own
line immediately after the last Task entry (before the next ##
heading). The sentinel pair delimits the bytes that bin/flow plan-from-issue extracts verbatim and writes to
.flow-states/<branch>/plan.md when the issue is later picked up
via /flow:flow-start #N. Without the sentinel pair,
plan-from-issue rejects the issue with plan_markers_missing
and the flow halts.
Paraphrase every prose reference to the plan-sentinel pair.
The literal HTML-comment marker strings only appear in each body
at two positions — the opening sentinel and the closing sentinel.
They must never appear inside prose, headings, code blocks,
examples, or any other surface of the body. bin/flow plan-from-issue extracts the slice between the FIRST occurrence
of each marker, so a literal marker mid-prose silently redirects
the extraction to the wrong slice — exactly the failure mode
bin/flow validate-issue-body exists to detect. Whenever a body
needs to reference the marker pair (for example, when an issue's
topic is the sentinel protocol itself), paraphrase every
reference. Acceptable wording: "the FLOW-PLAN sentinel pair",
"the plan-extraction markers", "the canonical sentinels
delimiting the plan block". The validator's marker_count_wrong
branch catches violations downstream; this rule prevents them
upstream so the Revise loop in Step 3 or Step 4 is not entered
unnecessarily.
The wrapped block looks like this in each issue body:
<!-- FLOW-PLAN-BEGIN -->
## Implementation Plan
### Context
...
### Exploration
...
### Tasks
#### Task 1: ...
...
<!-- FLOW-PLAN-END -->
After composing each child issue body and before presenting the issue list, scan every child body for the following forbidden phrasings, which ground the current decomposition in a historical artifact rather than the code's current merits:
"PR #<N> decided", "the prior PR chose", "the previous commit" — historical decision cited as authority"kept for backward compatibility", "compat shim", "legacy alias for older" — preservation justified by inherited
reasoning rather than a current consumer"older plugin versions", "prior plugin" — plugin-version-
compat reasoning (the FLOW plugin auto-updates and has no
installed base)"as PR #<N> chose to", "following the prior PR" —
deferring to past decisionsEvaluate matches in context: a bare PR #<N> reference used for
forensic detection (linking blocked-by, naming a specific merge)
is fine; a PR #<N> reference used to justify the present design
is forbidden. If any match is justifying-shape rather than
identifier-shape in any child body, revise that body. Re-evaluate
the underlying decision on the code's current merits, not on
historical context. The scan applies to every child issue
produced by this skill, not just the first one.
See .claude/rules/no-backwards-reasoning.md.
After composing each child issue body and before presenting the issue list, scan every child body for the following forbidden phrasings, which signal defensive scope shrinkage rather than genuine exclusion grounded in a concrete blocker:
"Out of scope" — defensive enumeration of exclusions written
before concrete blockers have surfaced; the scan reads
case-flexibly, so common section-heading title-case forms in
issue bodies are also flagged"Non-goals" — same defensive-enumeration shape under a
different heading; a bulleted list of "things we are not
doing" is speculation, not analysis"would expand scope" — reflexive scope shrinkage that
bypasses the three-condition gate in
.claude/rules/scope-expansion.md"separate code surface" — code-shape framing used as an
exclusion criterion; "separate surface" describes the code,
not the workEvaluate matches in context: a passing mention that names a
concern is fine; an enumerated section or bulleted list of
exclusions is forbidden. The default is inclusion — every
adjacent concern surfaced during decomposition belongs as a
task in the appropriate child issue unless one of the narrow
valid exclusions (user explicitly rejected, requires different
design conversation, blocks primary completion) applies. If any
match is exclusion-shape rather than identifier-shape in any
child body, revise that body: convert the deferral into an
inclusion task, or name the concrete blocker in that issue's
Context section as one sentence. The scan applies to every
child issue produced by this skill, not just the first one. The
lifecycle cost of splitting a concern out of an issue is
multiples larger than including it in the current exploration
budget. See .claude/rules/include-bias-in-issues.md.
Present the full issue list as a table:
| # | Title | Phase | Depends On | |---|-------|-------|------------| | 1 | Epic: <project_name> | — | — | | 2 | <first leaf task> | Phase 1: API | — | | 3 | <second task> | Phase 1: API | 2 |
Below the table, show each issue's full body text so the user can review every detail.
After presenting the list, write {"decompose_step": 2} to
.flow-states/decompose-project-<id>.json using the Write tool.
Save the issue list to
.flow-states/decompose-project-<id>-issues.json using the Write
tool (array of {title, body, labels, depends_on_indices}
objects). Then invoke flow:flow-decompose-project --step 3 --id <id> using the Skill tool as your final action.
The decompose-and-file pipeline runs unattended after Step 1; a second confirmation gate here would break the single-signal contract. Step 3 (epic) and Step 4 (children) handle validator failures with bounded auto-fix loops so the skill never needs to return control to the user mid-pipeline.
Output in your response (not via Bash) inside a fenced code block:
```text
── Step 3 of 6: Create Epic ──
```
Use the Read tool to read the approved issue list from
.flow-states/decompose-project-<id>-issues.json.
Detect the repo:
git remote get-url origin
Parse owner/repo from the remote URL.
Write the epic body to
.flow-states/decompose-project-<id>-epic-body using the Write tool.
Validate the epic body through the pre-filing validator before
asking the filer subcommand to send it to GitHub. The validator
runs the same sentinel-extraction logic that bin/flow plan-from-issue applies at flow-start; any body that fails this
gate is unconsumable downstream and must NOT be filed:
${CLAUDE_PLUGIN_ROOT}/bin/flow validate-issue-body --body-file .flow-states/decompose-project-<id>-epic-body
Parse the JSON output. If status is ok, proceed to the filer
invocation below. If status is error, run the bounded
auto-fix loop:
When the validator returns status: error, the skill must NOT
prompt the user. The validator's message names a concrete
defect (missing FLOW-PLAN sentinel pair, missing required
subsection, ## Implementation Plan heading on the wrong nesting
level, etc.). Apply a mechanical fix that addresses the named
defect — adjust the sentinel placement, add the missing
subsection, normalize the heading — rewrite the body file at
.flow-states/decompose-project-<id>-epic-body with the Write
tool, and re-run the validator. Track the attempt count mentally
— the cap is 5 attempts including the first failure.
After 5 failed validator runs, the epic is the parent of every child issue, so the whole flow must halt: clear the utility-in- progress marker, emit the structured error envelope, print the COMPLETE-FAILED banner, and stop without filing any child issues either.
${CLAUDE_PLUGIN_ROOT}/bin/flow clear-utility-in-progress --skill flow:flow-decompose-project
```json
{"status":"error","reason":"validator_max_retries","attempts":5}
```
```text
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✗ FLOW v2.4.0 — flow:flow-decompose-project — COMPLETE-FAILED
Epic validator rejected the body 5 times. Flow halted.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Once the validator returns ok, file the epic:
${CLAUDE_PLUGIN_ROOT}/bin/flow issue --repo <repo> --title "Epic: <project_name>" --body-file .flow-states/decompose-project-<id>-epic-body
Parse the JSON output. Record the epic issue number and database ID.
Update the session state with epic info. Write the updated
state to .flow-states/decompose-project-<id>.json using the Write tool,
adding epic_number and epic_id fields.
Set decompose_step to 3.
Then invoke flow:flow-decompose-project --step 4 --id <id> using the
Skill tool as your final action.
Output in your response (not via Bash) inside a fenced code block:
```text
── Step 4 of 6: Create Child Issues ──
```
Use the Read tool to read the session state and approved issue list.
Create each child issue in topological order (leaves first). For each:
Write the child issue body to
.flow-states/decompose-project-<id>-issue-body using the Write
tool.
Validate the child body through the pre-filing validator before
asking the filer subcommand to send it to GitHub. The validator
runs the same sentinel-extraction logic that bin/flow plan-from-issue applies at flow-start; any body that fails this
gate is unconsumable downstream and must NOT be filed:
${CLAUDE_PLUGIN_ROOT}/bin/flow validate-issue-body --body-file .flow-states/decompose-project-<id>-issue-body
Parse the JSON output. If status is ok, proceed to the filer
invocation below. If status is error, run the bounded
auto-fix loop:
When the validator returns status: error, the skill must NOT
prompt the user. The validator's message names a concrete
defect — apply a mechanical fix that addresses the named defect,
rewrite the body file at
.flow-states/decompose-project-<id>-issue-body with the Write
tool, and re-run the validator. Track the attempt count mentally
— the cap is 5 attempts including the first failure.
After 5 failed validator runs for THIS child, record the skip
and continue to the next child in topological order. The whole
skill does NOT halt — children fail individually, and any
sibling child whose depends_on_indices references this skipped
child has no --blocking-number to pass to bin/flow link-blocked-by in Step 5, so that dependency edge is silently
dropped (Step 5 is best-effort by design). The Step 6 report
surfaces the partial coverage; the user can re-run decomposition
for the missing child later, but the blocked-by graph for
already-filed siblings will need manual repair via gh issue edit or a follow-up bin/flow link-blocked-by call.
Once the validator returns ok for this child, file it:
${CLAUDE_PLUGIN_ROOT}/bin/flow issue --repo <repo> --title "<title>" --body-file .flow-states/decompose-project-<id>-issue-body --label decomposed
Parse the JSON output and record {title, number, id} in the mapping.
Record the issue (no-op if no FLOW feature is active):
${CLAUDE_PLUGIN_ROOT}/bin/flow add-issue --label decomposed --title "<title>" --url "<issue_url>" --phase flow-decompose-project
After all issues are created, write the complete mapping to the session
state file (issues array with {title, number, id} objects).
Set decompose_step to 4.
Then invoke flow:flow-decompose-project --step 5 --id <id> using the
Skill tool as your final action.
Output in your response (not via Bash) inside a fenced code block:
```text
── Step 5 of 6: Link Relationships ──
```
Use the Read tool to read the session state to get epic_number and
the issues mapping.
For each child issue, link it as a sub-issue of the epic:
${CLAUDE_PLUGIN_ROOT}/bin/flow create-sub-issue --repo <repo> --parent-number <epic_number> --child-number <child_number>
Best-effort — log failures but continue.
For each child issue that has dependencies, create the blocked-by link:
${CLAUDE_PLUGIN_ROOT}/bin/flow link-blocked-by --repo <repo> --blocked-number <child_number> --blocking-number <dep_number>
Best-effort — log failures but continue.
Set decompose_step to 5 in the session state.
Then invoke flow:flow-decompose-project --step 6 --id <id> using the
Skill tool as your final action.
Output in your response (not via Bash) inside a fenced code block:
```text
── Step 6 of 6: Report ──
```
Use the Read tool to read the session state.
Present a summary table:
| # | Title | Issue | Phase | Dependencies | |---|-------|-------|-------|--------------| | — | Epic: <name> | #N | — | — | | 1 | <task> | #N | Phase 1 | — | | 2 | <task> | #N | Phase 1 | #N |
Include:
Clean up the session files:
rm .flow-states/decompose-project-<id>.json .flow-states/decompose-project-<id>-dag.md .flow-states/decompose-project-<id>-issues.json
Clear the utility-in-progress marker so the Stop hook stops refusing turn-end now that the skill has completed its work:
${CLAUDE_PLUGIN_ROOT}/bin/flow clear-utility-in-progress --skill flow:flow-decompose-project
Output the COMPLETE banner:
```text
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ FLOW v2.4.0 — flow:flow-decompose-project — COMPLETE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
bin/flow issue script handles cleanupbin/flow calls in skill bash blocks use the plugin root prefix<id> prefix to prevent concurrent collisionsbin/flow plan-from-issue can extract the plan at flow-start#### Task N: headers (not numbered list items) so count_tasks recognises the heading shape and populates code_tasks_totalbin/flow validate-issue-body (prepended with the plugin root prefix) before bin/flow issue — on validator error, route through the Revise loop in Step 3 (epic) or Step 4 (per child)data-ai
Clear the autonomous-flow halt set when the user spoke mid-flow. Invokes `bin/flow clear-halt` so the next assistant turn resumes execution. User-only: the model cannot invoke this skill.
data-ai
Open a problem-statement conversation. Stays in discussion mode with PM as default voice; on user signal, files a vanilla What/Why/Acceptance Criteria issue against the current repo. Usage: /flow:flow-explore <topic>
tools
Display the FLOW skill catalog grouped by user role. Maintainer and Private buckets render only when invoked inside the FLOW plugin repo.
documentation
Phase 3: Review — six tenants assessed by four cognitively isolated agents (reviewer, pre-mortem, adversarial, documentation) launched in parallel. Parent session gathers context, triages findings, and fixes.