| name | cfn-megaplan |
| description | Tiered planning orchestrator. Runs the full SPARC+ pipeline (research, spec, decide, pseudo, data, arch, ux, design, test, ops) as a parallel DAG, scaled by build stage (mvp/beta/enterprise) via inclusion profiles. Enforces two gates: every success criterion is executable (verifiable-done) and every step is unambiguous (haiku-executable). Use as the entry point for any non-trivial build instead of cfn-spa-plan. |
| version | 1.2.0 |
| tags | ["planning","orchestrator","sparc","tiered","mvp","beta","enterprise","dag"] |
| status | production |
CFN MegaPlan Orchestrator
Purpose: One entry point that produces an implementation plan detailed enough that a haiku-level agent can execute it and cfn-loop-task can mechanically verify it is done. Scales the planning depth to the build stage so an MVP is not burdened with enterprise ceremony and an enterprise build is not shipped with MVP gaps.
Supersedes: cfn-spa-plan (which ran spec+pseudo+arch only, untiered, sequential-ish). MegaPlan is the strict superset. cfn-spa-plan remains for callers that only want the three SPARC artifacts.
When to Use
Entry point for any non-trivial build: multi-file, shared state (DB/API/types), new feature, security/auth, cross-project. Skip only for single-line fixes, renames, or a bug fix with a reproducing test (those go straight to /cfn-loop-task).
Invocation
/cfn-megaplan "<task>" [--tier=mvp|beta|enterprise] # forward: plan a build
/cfn-megaplan --review <path(s)> # reverse: audit shipped code
If --tier omitted, infer from the spec (see Step 2) and confirm with the user via AskUserQuestion when ambiguous.
Reverse mode (audit already-implemented work)
--review runs the phases BACKWARD against existing code instead of planning forward. It chains the three review-capable phases as the single entry point (one-entry-point rule):
cfn-data --review: recover the real schema, audit floor (RLS/unscoped-delete/PII), emit the true field-bindings.
cfn-ux --review: read shipped UI, diff each field's rendered control vs the affordance map (catches FK-field-as-textbox post-hoc). Consumes step 1's bindings, so it does not guess.
cfn-arch --review: recover component boundaries + contracts, audit DRY / typed-boundary / retry-timeout / failure handling.
Each emits planning/AUDIT_<PHASE>_<slug>.md (findings table, file:line | issue | severity | fix). Synthesis (Step 7) merges them into planning/AUDIT_<slug>.md with a single severity-ranked list. Skip a phase when its surface is absent (no UI → skip ux; no DB → skip data). This is the catch for defects that already shipped; the forward pipeline prevents them, this finds the ones that slipped.
Pipeline shape (8-level DAG)
Only spec is a hard gate. After it, branches fan out. Critical path = 9 levels at beta+ (8 at mvp, where ops is skipped and test_plan collapses back up a level), not 12 sequential.
L1 research (conditional: unknowns)
L2 spec HARD BARRIER
L3 decide ∥ pseudo
L4 data (conditional: db)
L5 arch ∥ ux (ux conditional: frontend)
└─ WIREFRAME GATE (frontend only: cfn-ux emits a low-fi wireframe; user Approve/Revise
BLOCKS before L6 — the visual wrong-path catch, before design/test/ops)
L6 design ∥ ops (design conditional: frontend; ops conditional: beta+)
L7 test_plan consumes OPS §2 (observability signals) + DATA §6 (concurrency);
at mvp `ops` is skipped, so the skipped-dep rule lets test_plan
run in parallel with design at L6 (no extra level)
L8 write_plan JOIN: synthesizes all branches; runs Bar A
L9 plan_review runs Bar B; loops failing phase, not whole pipeline
Why test_plan moved below ops (G49). ops (Phase 2) names the observability signals that must be verified (OBS-n, verify: required); test_plan turns each into an AC. When both sat at L6 in parallel, test_plan could not read OPS. Making the dependency explicit (test_plan deps += ops, data) is correct scheduling; the rejected alternative — a Bar A back-fill loop — would guarantee a wasted iteration, and loops should signal defects, not schedule known work.
Node dependencies (orchestrator must honor; do not spawn a node before its deps return). The Consumes column names which sections of each input artifact the consumer needs; put those paths and section names in the phase prompt:
| Node | Deps | Consumes (sections of each input) | Phase skill |
|---|
| research | (none) | (none) | cfn-research |
| spec | research | RESEARCH: feasibility verdicts, prior-art findings, resolved unknowns | cfn-spec |
| decide | spec | SPEC: FR/EC ids, constraints, [OPEN] items, Build Flags block | cfn-decide |
| pseudo | spec | SPEC: FR/EC ids, pre/post conditions, invariants | cfn-pseudo |
| data | spec, decide | SPEC: FR ids, entities, pii flag; DECISIONS: resolved forks that pick storage/shape | cfn-data |
| arch | spec, pseudo, data | SPEC: FR ids, constraints; PSEUDO: module + branch structure; DATA: schema, field-bindings table | cfn-arch |
| ux | spec, data | SPEC: FR ids, user-facing flows; DATA: field-bindings table (drives control derivation) | cfn-ux |
| design | ux | UX: affordance map, state enumeration, flows | cfn-design |
| ops | spec, arch, data | SPEC: FR ids, audience/tier signals; ARCH: components, external calls; DATA: schema, migration plan | cfn-ops |
| test_plan | spec, arch, ux, data, ops | SPEC: FR/EC ids, [core] flags; ARCH: component boundaries, contracts, state machines (§9); UX: state enumeration; DATA: concurrency table (§6), migration invocation (§5); OPS: observability signals (§2, verify: required) — ops absent at mvp, then test_plan runs at L6 | cfn-test-plan |
| write_plan | all above | every active artifact in full (synthesis join) | /write-plan + Bar A |
| plan_review | write_plan | the assembled plan in full, plus VERIFY_<slug>.md | cfn-plan-review + Bar B |
Skipped-dep rule: A dependency dropped in Step 3 counts as satisfied immediately. In the dependent phase's prompt, replace its artifact path with the literal line Input <PHASE>: ABSENT (phase skipped: <flag>=false) so the phase does not go looking for it.
Open-item triage (BLOCKING vs deferrable)
A mid-pipeline AskUserQuestion stalls every downstream level on human latency. Most stalls are not worth it: a question whose answer no downstream phase reads cannot change the plan's structure, only its content. Those get answered once, at the end, in one batch.
Triage rule (mechanical — the phase does NOT judge importance). An [OPEN] item is BLOCKING iff either:
- It lives in a section of the phase's own artifact that appears in the Downstream-consumed sections table below, or
- It touches a profile
floor item (rls, auth_boundaries, secrets_handling, no_unscoped_delete, pii_if_present).
Otherwise it is deferrable: the phase picks a default, self-parks the item as [PARKED: <default> | deferred: <section> is not downstream-consumed], and keeps running. It never reaches the user mid-pipeline.
This reuses the existing [PARKED: <accepted default>] marker (cfn-spec §Open Questions; already honored by cfn-pseudo), rather than adding a second vocabulary. The only change: a phase may now park an item itself under this rule, instead of only after the user accepts a deferral. [PARKED] still travels downstream as a stated assumption and still does not set the unknowns build flag.
Downstream-consumed sections (inverts the Deps table above; this is the triage input). The orchestrator pastes the row for the phase being spawned into that phase's prompt. write_plan (L8) consumes every artifact in full — that join does not count here; if it did, every item would be blocking and the rule would be dead. Only phase-to-phase deps (L1→L7) count.
| Artifact | Read by | Downstream-consumed sections (an [OPEN] here BLOCKS) |
|---|
| RESEARCH | spec | feasibility verdicts, prior-art findings, resolved unknowns |
| SPEC | decide, pseudo, data, arch, ux, ops, test_plan | FR/EC ids, [core] flags, constraints, pre/post conditions, invariants, entities, pii flag, user-facing flows, audience/tier signals, Build Flags (§8), Actors (§1a), Interaction Intent (§1b) |
| DECISIONS | data | resolved forks that pick storage/shape |
| PSEUDO | arch | module + branch structure |
| DATA | arch, ux, ops, test_plan | schema, field-bindings table, migration plan (§5), concurrency table (§6) |
| ARCH | ops, test_plan | components, external calls, component boundaries, contracts, state machines (§9) |
| UX | design, test_plan | affordance map, state enumeration, flows |
| OPS | test_plan | observability signals (§2, verify: required) |
| DESIGN | — (terminal) | none — every non-floor [OPEN] here is deferrable |
| TEST | — (terminal) | none — every non-floor [OPEN] here is deferrable |
Picking the default. The parked default is the conservative side, never the convenient one: include the check rather than skip it, keep the stricter validation, keep the narrower access. A default that reduces coverage or loosens a boundary is not eligible for parking — raise it as BLOCKING instead. Rationale: a deferred item the user never gets to must fail safe.
Never deferrable, regardless of section: any floor item; anything that changes the schema, a contract, the FR/EC set, or a [core] flag. cfn-spec §1b Interaction Intent items stay hard-blocking (Step 2) — a richness decision taken after the schema locks is a migration, not an edit.
Worked example. cfn-test-plan returns "should the e2e run include the invite-resend path?". TEST is terminal (no downstream reader) and the item touches no floor concern → deferrable. The phase parks it as [PARKED: include invite-resend in e2e | deferred: TEST is not downstream-consumed] (conservative side = include), the pipeline never stops, and the item surfaces in the Step 7 batch for the user to override.
Protocol
Step 0: Scope check
/codebase-search "<task keywords>": if existing capability covers the task, abort and point to it.
- If estimate is 8+ files, pause and negotiate scope via
AskUserQuestion before continuing.
- Query prior art (gap G06):
~/.claude/skills/cfn-knowledge-base, ~/.claude/skills/decision-log/query.sh '<entities>' 5 <project> (conversation FTS), and ~/.claude/skills/decision-log/decisions.sh search '<entities>' (structured register of settled forks from past plans). Inject any prior playbook / failed-assumption / RESOLVED fork into the spec prompt so it is not re-litigated.
- Pull recent retro signal (gap G36): If
.cfn-cache/retro-latest.md exists, read it and flag hotspot overlap; if absent, skip this sub-step silently (do not search for retro output elsewhere). When the task touches a known hotspot file, flag it in the spec prompt so the plan accounts for the churn/fragility already observed there.
- Ingest the tech-debt ledger (closes the
cfn-tech-debt feedback loop). If .cfn-cache/tech-debt-ledger.json exists, READ it (never re-harvest) and list any open cfn: shortcut that lives in the files/area in scope as candidate backlog entries. Inject them into the spec prompt and carry them to Step 7 so deliberate shortcuts surface for the user instead of silently rotting. no_trigger rows (rot risk) rank first.
LEDGER=".cfn-cache/tech-debt-ledger.json"
[ -f "$LEDGER" ] && jq -r '.markers[] | "\(.file):\(.line) ceiling: \(.ceiling). upgrade: \(.upgrade_trigger // "NONE")."' "$LEDGER"
Step 1: Build the slug
SLUG=$(echo "$TASK" | tr '[:upper:] ' '[:lower:]_' | tr -cd '[:alnum:]_-' | cut -c1-60)
All artifacts land in planning/ named <PHASE>_<SLUG>.md (e.g. SPEC_<slug>.md, UX_<slug>.md, OPS_<slug>.md).
Step 2: Run spec, read tier + build flags
Spawn cfn-spec (L2). It is the hard barrier: nothing else starts until it returns.
When the task has a user-facing surface, cfn-spec runs its Interaction Intent Walk (§1b) and may return [OPEN] intent items (richness ceiling, value-type inheritance, composition depth, lifecycle). These MUST be surfaced via AskUserQuestion and resolved BEFORE L4 cfn-data runs — a richness decision taken after the schema locks is a migration, not an edit. They ride the same [OPEN]-batching + 3-round bound as any spec open question (see the spec has [OPEN] failure-mode row).
Parse the ## 8. Build Flags block from planning/SPEC_<slug>.md. Do NOT re-infer flags from spec prose. The block has this exact format (cfn-spec's template emits it):
## 8. Build Flags
- frontend: yes|no
- db: yes|no
- pii: yes|no
- unknowns: yes|no
- tier-hint: mvp|beta|enterprise
If the block is missing, the spec failed its contract: re-run cfn-spec with a directive to emit section 8.
§1a presence gate (deterministic, same class as the Build Flags check): if frontend: yes OR db: yes, the spec MUST contain a ## 1a. Actors section with at least one row, no blank cells, and every FR touched by at least one actor. A spec missing it failed its contract: re-run cfn-spec with a directive to emit §1a. Do not accept prose assurances that the roles are "obvious" — cfn-data §4 derives the RLS policy set from this table at L4 (a floor item) and cfn-arch §6 derives its AuthZ columns from it at L5. With no §1a, both invent a role set independently and the two do not have to agree.
§1b presence gate (deterministic, same class as the Build Flags check): if frontend: yes, the spec MUST contain a ## 1b. Interaction Intent section with at least one row per interactive feature and no leverage dimension left blank (each row resolved, [OPEN], or N/A: <reason>). A frontend: yes spec with no §1b section — or a §1b that skipped dimensions — failed its contract: re-run cfn-spec with a directive to run the Interaction Intent Walk. Do not let the pipeline advance past L2 on prose assurances that intent was covered; the section either exists with full dimension coverage or the spec is rejected.
Tier = tier-hint unless the user passed --tier; if tier-hint is absent or the audience is ambiguous, ask the user with AskUserQuestion (one question, plain English, recommend based on the spec).
Load the matching profile: .claude/skills/cfn-megaplan/profiles/<tier>.json.
Step 3: Resolve the active phase set
For each phase in the profile, mechanical resolution: if condition is present and the named build flag is false, drop the phase; else use directive verbatim.
directive: skip → drop.
directive: full / light → keep; pass the directive + drops/extras into the phase prompt so the phase knows what to include or omit.
- Every dropped phase triggers the skipped-dep rule above for its dependents.
- Floor override: every item in the profile
floor array is forced into the relevant phase regardless of tier or directive. A light data phase still authors RLS, auth boundaries, secrets handling. A skip-level concern that is in floor (e.g. pii_if_present when pii flag is true) is forced on.
Step 3a: Resolve the model per phase
Each profile phase may carry a model key (opus | sonnet | haiku). It is a wall-clock lever, not a quality knob: phases whose job is transcription and enumeration against an already-decided structure run sonnet; phases that decide structure run opus.
Rules:
model present → spawn that phase at that model.
model absent → inherit the session model. Every write_plan / plan_review step is main-chat and always inherits; never assign them a model.
- Never downgrade a phase that owns a decision downstream phases consume:
spec, data, arch, and decide at full stay opus in every profile. Downgrading them moves the error upstream of everything, where it is most expensive.
- A
directive: light phase is the safe downgrade candidate — light already dropped the parts that needed judgment.
- Never downgrade a phase carrying a
floor item.
- If a downgraded phase fails Bar A/Bar B twice on the same finding, re-run it at
opus and record it: the profile's model assignment is wrong, not the phase.
Step 4: Walk the DAG, batch by level
For levels L3 → L7, spawn every active phase at that level in a single message (true parallel; they are independent within a level). Wait for the whole level to return before advancing (join). Note test_plan (L7) depends on ops (L6) at beta+; at mvp ops is skipped and test_plan joins the L6 message.
Agent selection: spawn each phase as the profile's agent key. A phase with no agent key → spawn general-purpose with the SKILL.md path in the prompt. Pass the profile's model key as the spawn model when present; a phase with no model key inherits the session model (see Step 3a).
Each phase prompt carries:
Follow .claude/skills/<phase-skill>/SKILL.md exactly. Read the skill file first.
Task: <task>
Tier: <tier> Directive: <full|light> Include extras: <extras> Omit: <drops>
Floor (forced on, never skip): <applicable floor items>
Read inputs: <dep artifact paths>
Write artifact: planning/<PHASE>_<slug>.md
Open-item triage (apply to EVERY [OPEN] you would raise):
Downstream-consumed sections of your artifact: <row from the Downstream-consumed table, or "none — terminal">
An [OPEN] is BLOCKING only if it lives in one of those sections, or touches a floor item.
Otherwise: pick the CONSERVATIVE default, park it as
[PARKED: <default> | deferred: <section> is not downstream-consumed]
and keep going. Do not ask the user. Never park a floor item, a schema/contract/FR-set/[core] change,
or any default that reduces coverage or loosens a boundary — those are BLOCKING.
Return: artifact path + a 3-line summary + [OPEN] items (BLOCKING, need a user decision now)
+ [PARKED] items (deferred, listed separately with the default you chose).
If any phase returns BLOCKING [OPEN] items, batch them and surface via AskUserQuestion before advancing past the level. Record every resolved decision to the decision log (closes gap G35/decision-log loop). cfn-decide owns the register; the orchestrator forwards mid-level decisions to it.
Wireframe gate (L5→L6 barrier, when frontend=yes). cfn-ux (L5) emits a low-fi wireframe and returns its reference (wireframe: <url|path>) as a BLOCKING approval item (see cfn-ux Phase 6). At the L5 join, BEFORE spawning L6 (design ∥ ops), surface the wireframe with one AskUserQuestion: Approve / Revise. This is the visual twin of the spec §1b intent walk — §1b confirms interaction intent in words before the schema locks at L4; the wireframe confirms screen structure + flow in a picture before design/test-plan/ops/write-plan build on it. Catching a wrong structure here costs a cfn-ux patch; catching it at Step 7 would cost re-running L6–L9.
- Approve → proceed to L6. Record the approval to the decision log.
- Revise → route to
cfn-ux in patch mode with the user's note as the finding; it adjusts the structure (a control, a screen, a flow) and re-renders. Re-surface. This rides the same 3-BLOCKING-cycle-per-level bound as any L5 blocking item; after round 3, surface residual via AskUserQuestion (accept as-is / keep iterating / descope).
- A revision that would change an FR, an AC, or the schema is NOT a wireframe tweak — route it to
cfn-spec/cfn-data and re-run the affected levels, not a cfn-ux patch.
- A
_skipped: no renderable screens_ reference raises no gate.
Because the wireframe is approved at L5, it never reaches Bar A/Bar B or the Step 7 batch: the structure the plan is built on was signed off before the plan existed.
[PARKED] items do not gate the level. Collect them into a running list (artifact, item, chosen default, reason) and carry it to Step 7, where they surface as one batched AskUserQuestion after the bars pass.
Triage audit (cheap, do it — the rule is only worth having if it is enforced in one direction). For each BLOCKING item a phase returns, confirm the named section actually appears in that artifact's Downstream-consumed row, or that it names a floor item. A phase escalating a terminal-artifact item is re-prompted once with the rule restated, not forwarded to the user. Do not audit in the other direction: a phase that parks something it should have blocked on gets caught by Bar A/Bar B, which read the parked default as a stated assumption.
Bound: max 3 BLOCKING-item cycles per level (resolve, re-run phase, re-check). If a phase still returns BLOCKING items after round 3, stop and surface the residual items via AskUserQuestion (accept as-is / keep iterating / descope) instead of looping again.
Loop-back protocol: patch mode (used by Bar A, Bar B, and Step 7 overrides)
"Loop the owning phase" does not mean re-run it. A full re-run of e.g. cfn-test-plan rewrites a whole artifact to fix two findings, costs a full phase execution, and churns rows the bars already passed. Loop-backs are the pipeline's serial tail — up to 3 Bar A rounds plus 3 Bar B rounds land end-to-end after every branch has joined, so this is where saved minutes are real minutes.
Default: PATCH. Spawn the owning phase's agent with:
Follow .claude/skills/<phase-skill>/SKILL.md exactly. Read the skill file first.
PATCH MODE: planning/<PHASE>_<slug>.md already exists and already passed its own contract.
Read it. Fix ONLY these findings:
<verbatim finding list — file:line | kind | detail>
Rewrite only the rows/sections those findings name. Do not restructure, re-derive, or renumber
anything else; downstream artifacts already cite these ids. Preserve every id (FR-n, AC-n, D-n).
Re-run your skill's own self-checks (e.g. the coverage self-check) before returning.
Return: the artifact path + one line per finding stating how it was fixed.
Escalate to full re-run when either: the same finding survives 2 patch rounds, or a finding requires renumbering / adding an FR / changing the artifact's structure (a patch cannot honestly do that). A patch round and a full re-run each count as one round against the phase's 3-round bound — the bound counts attempts, not effort.
Patch mode does not apply to a phase that never ran (a dropped phase newly forced on by a resolved decision). That is a fresh spawn at its normal directive.
Step 5: L8: write_plan + Bar A
write_plan and plan_review are slash commands run by the orchestrator in main chat via the Skill tool, never spawned as subagents.
Run /write-plan "<task>" --mode=<tier>; it consumes every planning/<PHASE>_<slug>.md artifact. Then run Bar A (bars/verifiable-done.md): convert success criteria to executable AC rows, emit planning/VERIFY_<slug>.md. If Bar A fails (any non-executable AC, any unmapped FR/EC), loop back to the owning phase (usually test_plan or spec) in patch mode (see Loop-back protocol), not the whole pipeline and not a full phase re-run.
Mechanical static pass (Bar A step 1.5, REQUIRED). After VERIFY_<slug>.md is emitted, run the static checker:
.claude/skills/cfn-megaplan/bars/check-verifiable-static.sh "planning/VERIFY_${SLUG}.md"
Exit 1 (error findings — missing AC field, taxonomy mismatch, non-decidable/weasel pass, coverage-counter gap) routes back to the owning phase and counts against the same 3-round Bar A bound. Do not hand-write this scan. Only when it is clean (exit 0) do you proceed.
Bless the integrity hash (W2). After Bar A passes (static pass clean), bless the manifest. Use bless-verify.sh — never write the sidecar by hand. It re-runs the static checker and refuses to pin anything on an error finding, then writes the sidecar plus an append-only bless ledger naming which ACs moved:
.claude/skills/cfn-megaplan/bars/bless-verify.sh "planning/VERIFY_${SLUG}.md" --note "Bar A pass"
Exit 1 = refused (Bar A findings remain, nothing pinned). On a re-bless it prints structure_changed / predicate_changed; a predicate_changed: true means a pass condition moved and needs a stated reason before you advance.
This is the plan-stage bless (the default), which is why every AC's evidence field reads PENDING: <reason> here — the code it checks does not exist yet. cfn-loop-task 5E.3a backfills the real output from the exit-gate run and re-blesses with --stage exit, which rejects any surviving PENDING.
PLAN persistence gate (REQUIRED — downstream /cfn-loop-task hard-depends on it). /write-plan writes planning/PLAN_<slug>.md; this is the lane-derivation source cfn-loop-task reads. After /write-plan returns, assert the file exists:
[ -f "planning/PLAN_${SLUG}.md" ] || { echo "FATAL: write-plan did not persist planning/PLAN_${SLUG}.md"; }
If it is missing, re-run /write-plan before advancing to L9. MEGAPLAN_<slug>.md (Step 7) is an INDEX/summary, NOT the plan — it cannot substitute for PLAN_<slug>.md. A megaplan that produces VERIFY_ but no PLAN_ will break cfn-loop-task at lane derivation (the plan file is the only source of lanes + exclusive file ownership).
Bound: max 3 Bar A loop-back iterations. If Bar A still fails after round 3, stop and surface the residual failures via AskUserQuestion (accept as-is / keep iterating / descope).
Step 6: L9: plan_review + Bar B
Run /cfn-plan-review (assumptions, dependency trace, blast radius, alpha-readiness scaled to tier) in main chat via the Skill tool. Then run Bar B (bars/haiku-executable.md): static + structural + coverage scans, then the live haiku probe. Any finding routes to the owning phase (ui_control → cfn-ux, value source → cfn-data/cfn-arch, branch → cfn-pseudo), which fixes it in patch mode (see Loop-back protocol). Re-run Bar B after each fix round.
Bound: max 3 Bar B rounds. If findings remain after round 3, stop and surface residual findings via AskUserQuestion (accept as-is / keep iterating / descope).
Step 7: Deferred-decision batch, synthesis + hand-off
Deferred-decision batch (run FIRST, before the handoff gate). Take the [PARKED] list accumulated across Step 4 levels. These are the questions the triage rule kept off the critical path; this is where they get answered, once, together. (The wireframe is NOT here — it is a BLOCKING gate at the L5→L6 barrier in Step 4, resolved before design/test-plan/ops run.)
- Drop any parked item whose default the plan already made moot (a later phase decided it).
- Surface the rest via
AskUserQuestion, batched 4 per call, each stating the chosen default and what changes if overridden. Every item is pre-answered by its default, so the user can accept the whole batch in one pass.
AskUserQuestion caps at 4 options per question — an item with more candidate values gets its top 3 plus "Other".
- Accepted default → rewrite the marker to
[PARKED: <default> | accepted]. Record it to the decision log via cfn-decide like any other resolved fork.
- Override → route to the owning phase in patch mode with the override as the finding.
Re-gating after an override (do not skip — the bars passed against the OLD bytes).
| Override changed | Re-run |
|---|
| nothing (default accepted) | nothing |
| artifact prose only, no AC row and no plan step | the static passes (check-verifiable-static.sh, check-haiku-static.sh) |
an AC row (added/removed/rewritten), or a [core] FR, or a plan step's semantics | full Bar A + full Bar B, including the live haiku probe |
Any edit to VERIFY_<slug>.md must re-bless via bars/bless-verify.sh "planning/VERIFY_<slug>.md" --note "<why>", or cfn-loop-task Step 0 will correctly reject the manifest as tampered. The re-bless appends a ledger entry naming the moved ACs and fields; read its predicate_changed line before accepting an override that touched a pass condition.
Override rounds are bounded at 2. Residual disagreement is a scope question, not a planning loop — surface it and stop.
Handoff-file gate (run BEFORE writing the synthesis). cfn-loop-task needs BOTH planning/PLAN_<slug>.md (lane source) and planning/VERIFY_<slug>.md (completion gate). Assert both exist; if either is missing the megaplan is NOT done — re-run the owning step (/write-plan for PLAN_, Bar A for VERIFY_) before synthesis:
for F in "PLAN_${SLUG}" "VERIFY_${SLUG}"; do
[ -f "planning/${F}.md" ] || echo "FATAL: missing planning/${F}.md — megaplan not build-ready"
done
[ -f "planning/.VERIFY_${SLUG}.sha256" ] || echo "FATAL: missing planning/.VERIFY_${SLUG}.sha256 — Bar A hash not blessed (re-run Step 5 static pass + hash)"
Write planning/MEGAPLAN_<slug>.md. All eight ## sections below are REQUIRED — emit every one even if empty (write _none_); dropping a section is a template violation. Do NOT rename headings.
# MegaPlan: <task>
Tier: <tier> Build flags: <frontend? db? pii? unknowns?> Generated: <date>
## Artifacts (active phases only)
<list of planning/*_<slug>.md actually produced — MUST include PLAN_<slug>.md and VERIFY_<slug>.md>
## Gates
- Bar A verifiable-done: PASS (N ACs, FR <m/m>, EC <k/k> mapped) -> planning/VERIFY_<slug>.md
- Bar B haiku-executable: PASS (0 findings after <r> rounds)
# or, in a multi-plan program only: CONDITIONAL-PASS (see Cross-plan seams; blocked solely on
# named sibling-plan items, all tracked below). CONDITIONAL-PASS is NOT a valid handoff state
# for a standalone megaplan — a standalone plan loops its owning phase until PASS.
## Open decisions resolved
<from cfn-decide register>
## Deferred decisions
<one row per [PARKED] item from the Step 7 batch:
`phase | item | default chosen | accepted|overridden | re-gate run (none|static|full)`.
`_none_` if triage parked nothing. Every row here is a question that was kept off the critical
path on purpose — an item in this table that turns out to have been schema/contract/FR-set
affecting means the triage rule mis-classified it; fix the rule, not just the row.>
## Cross-plan seams (multi-plan program ONLY; omit the section body with "_none — standalone plan_" otherwise)
<seam ledger rows: `owner-plan | item | target artifact/migration | dependency-critical? | applied|PENDING`.
Every PENDING row that this plan hard-depends on keeps Bar B at CONDITIONAL-PASS, not PASS.>
## Open tech debt in scope
<rows from .cfn-cache/tech-debt-ledger.json whose file/area is touched by this plan; no_trigger rows first. Empty (`_none_`) if the ledger is absent or clean. These are backlog candidates for the user, not auto-scheduled work.>
## Build order (multi-plan program ONLY; else "_standalone_")
<this plan's position in the program DAG, e.g. MP1 -> MP2 -> [this] -> MP4>
## Next
/cfn-loop-task "<task>" --mode=<mode> (reads PLAN_<slug>.md for lanes + VERIFY_<slug>.md as completion gate)
Hand-off mode mapping: mode = standard if tier is beta, else the tier verbatim. Planning tier vocabulary is mvp|beta|enterprise; execution mode vocabulary is mvp|standard|enterprise; beta maps to standard.
Multi-plan programs (a task decomposed into N interdependent megaplans)
When one build is too large for a single megaplan and is split into sibling plans (MP1…MPn) that share a schema / contracts package / decision log, the single-plan assumptions above bend. Extra rules:
-
Program index doc. Write planning/MEGAPLAN_program_<program-slug>.md (or _mp0_) that owns what no single plan can: the build-order DAG across plans, the shared contracts/decision-register paths, and a consolidated cross-plan seam ledger (every row from every plan's ## Cross-plan seams). Without it the reconciliation smears across each plan's prose and drifts. Each plan links back to it.
-
Shared decision register. All plans append to ONE register (e.g. planning/DECISIONS_<program>.md) so a fork resolved in MP2 is visible to MP4. cfn-decide still owns the format; the register is program-scoped, not plan-scoped.
-
Cross-plan seam ledger is first-class. A seam = an item plan A needs that lives in plan B's artifacts (a column, RPC, enum member, edge). Each seam row: owner | item | target migration/artifact | dependency-critical? | applied|PENDING. A PENDING dependency-critical seam is a real blocker: it keeps the dependent plan's Bar B at CONDITIONAL-PASS.
-
Bar B CONDITIONAL-PASS verdict (multi-plan only). A plan whose OWN decomposition is haiku-executable but which is blocked solely on named, tracked sibling-plan seam items is CONDITIONAL-PASS, not PASS and not a Bar B failure to loop. It becomes a true PASS the instant every blocking seam flips to applied. Standalone plans never use this state — they loop the owning phase to PASS. Do NOT hand a CONDITIONAL-PASS plan to cfn-loop-task until its blocking seams are applied (re-check at the program level, per build order).
-
Back-propagation rule (CRITICAL). Planning a LATER plan (MP4) may discover a touchpoint that must live in an EARLIER, already-"done" plan's artifacts (MP4 forcing columns into MP1's 0001 migration). When this happens: (a) apply the item to the earlier plan's DATA/ARCH/etc. artifacts, (b) re-run that earlier plan's Bar A + Bar B, (c) update its seam ledger row to applied. An earlier plan that still lists forced items as "NOT yet applied" is NOT build-eligible — its own artifacts are internally inconsistent with its synthesis. A dependency-critical back-propagated item MUST ship in the earlier plan's migration (the sibling that writes it builds before the plan that consumes it), never deferred to the later plan's migration.
-
Program build order gates execution. cfn-loop-task runs per plan in DAG order (MP1 → MP2 → …). Before starting plan N, confirm every seam plan N depends on is applied in the plans already built. The program index doc's build-order DAG is the source of truth for this sequencing.
Worked example: profile resolution + DAG walk
Task: "coach dashboard with payout table" --tier=beta
Build Flags (SPEC section 8): frontend=yes db=yes pii=yes unknowns=no
Profile resolution (profiles/beta.json):
- research: condition unknowns, flag false -> DROPPED. Skipped-dep rule fires:
spec's prompt gets "Input RESEARCH: ABSENT (phase skipped: unknowns=false)".
- spec: full. decide: full. pseudo: full.
- data: condition db, flag true -> kept, directive full. pii floor: pii=yes forces
PII handling (classification, retention, access boundaries) into data
even at beta; the floor, not the tier, decides this.
- arch: full. ux: condition frontend, flag true -> kept, full.
- design: condition frontend, flag true -> kept (drops i18n).
- test_plan: full. ops: full.
Model resolution (Step 3a, from beta.json):
- opus: spec, decide, data, arch, ux, ops (structure-deciding, or carrying floor items)
- sonnet: research, pseudo, design, test_plan (gathering, or terminal artifacts)
DAG walk (each bracket = one message, phases in a bracket spawn in parallel):
L2 spec (hard barrier)
L3 [decide, pseudo] (one message)
L4 [data]
L5 [arch, ux] (one message)
WIREFRAME GATE (frontend=yes: cfn-ux emitted a wireframe; Approve/Revise
BLOCKS before L6. Approve → L6; Revise → cfn-ux patch, re-render)
L6 [design, ops] (one message)
L7 test_plan (consumes OPS §2 + DATA §6; own level below ops at beta+)
L8 write_plan (slash command, main chat) + Bar A
static pass (check-verifiable-static.sh) + bless .VERIFY hash
ASSERT planning/PLAN_<slug>.md persisted (loop-task lane source)
L9 plan_review (slash command, main chat) + Bar B
Open-item triage during the walk:
- L4 data raises "retention window for payout rows?" -> DATA schema IS downstream-consumed
(arch, ux, ops, test_plan read it) AND pii=yes makes it a floor item -> BLOCKING.
AskUserQuestion before L5. Correct stall: answering it later would be a migration.
- L6 design raises "which breakpoint does the payout table collapse at?" -> DESIGN is terminal
-> parked [PARKED: collapse at md (768px) | deferred: DESIGN is not downstream-consumed].
- L7 test_plan raises "should e2e cover the payout-export path?" -> TEST is terminal, no floor
-> parked [PARKED: cover payout-export | deferred: TEST is not downstream-consumed]
(conservative side = cover it).
Both parked items surface together in ONE AskUserQuestion at Step 7, after the bars pass.
Net: 1 human stall on the critical path instead of 3.
Hand-off: /cfn-loop-task "coach dashboard with payout table" --mode=standard
(tier beta maps to execution mode standard)
precondition: PLAN_<slug>.md + VERIFY_<slug>.md both on disk
Failure modes
| Failure | Recovery |
|---|
| phase agent missing | resurrect from .claude/backups/ or fall back to general-purpose with the SKILL.md path in the prompt |
spec has [OPEN] | surface via AskUserQuestion before L3 (SPEC is consumed by 7 phases — its opens are almost always BLOCKING) |
| phase escalated a terminal-artifact item as BLOCKING | re-prompt that phase once with the triage rule restated; do not forward to the user |
| phase parked something schema/contract/FR-set affecting | Bar A/Bar B catch it (they read the default as a stated assumption); fix via patch mode and correct the phase's triage row |
| Bar A fails | patch the owning phase only — not the pipeline, not a full phase re-run |
| Bar B probe returns questions | route each to its owning phase, patch it, re-probe |
| same finding survives 2 patch rounds | escalate to a full phase re-run; if that phase was downgraded in Step 3a, re-run it at opus |
Step 7 override changed an AC row or a [core] FR | re-run full Bar A + Bar B (the bars passed against the old bytes), then re-bless .VERIFY_<slug>.sha256 |
| tier ambiguous | AskUserQuestion, recommend from spec |
| user downgrades tier | floor items stay on; warn if a downgrade drops a phase the build flags say is needed |
write-plan left no PLAN_<slug>.md | re-run /write-plan; never hand off with only MEGAPLAN_/VERIFY_ — loop-task lane derivation hard-fails without PLAN_ |
| sibling plan forced an item into an already-done plan | apply it, re-run that plan's Bar A + Bar B, flip its seam row to applied (back-propagation rule); do not build the earlier plan while it lists forced items unapplied |
| dependent plan blocked on sibling seam | Bar B = CONDITIONAL-PASS (multi-plan only); hold cfn-loop-task until blocking seams are applied, per program build order |
Anti-patterns
- Running phases strictly sequentially (ignore the level batching), wastes wall-clock.
- Spawning a node before its deps return.
- Letting a tier knob disable a
floor item (RLS/auth/secrets/PII).
- Treating Bar A/Bar B as advisory. They are hard gates.
- Stopping the pipeline to ask a question no downstream phase reads. Triage it, park it with a conservative default, batch it at Step 7.
- Parking a floor item, a schema/contract change, or any default that reduces coverage or loosens a boundary, because the section looked terminal. Triage classifies by section; the floor and the never-deferrable list override the section every time.
- Judging an
[OPEN] by how important it feels. Triage is mechanical: is the section downstream-consumed, yes or no.
- Full-re-running a phase to fix two Bar findings. Patch mode exists; a re-run also churns ids downstream artifacts already cite.
- Downgrading
spec / data / arch / decide-at-full to a cheaper model. They decide the structure every later phase consumes; an error there is the most expensive kind.
- Accepting a Step 7 override that rewrites an AC row without re-running Bar A/Bar B and re-blessing the
.VERIFY hash. The gates passed against different bytes; cfn-loop-task will reject the manifest.
- Editing planning artifacts during implementation without re-running Bar B.
- Handing off to
cfn-loop-task with only MEGAPLAN_/VERIFY_ on disk. MEGAPLAN_ is an index, not the lane source; loop-task needs PLAN_<slug>.md.
- Shipping an earlier plan that still lists back-propagated sibling items as "NOT yet applied" — its artifacts contradict its own synthesis.
- Using CONDITIONAL-PASS to hand off a standalone (non-program) megaplan. That state exists only for sibling-seam blocking in a multi-plan program.
Related
- Phases:
cfn-research, cfn-spec, cfn-decide, cfn-pseudo, cfn-data, cfn-arch, cfn-ux, cfn-design, cfn-test-plan, cfn-ops
- Gates:
bars/verifiable-done.md, bars/haiku-executable.md
- Profiles:
profiles/{mvp,beta,enterprise}.json
- Inputs:
cfn-tech-debt (Step 0 reads its .cfn-cache/tech-debt-ledger.json so open cfn: shortcuts in scope surface as backlog candidates)
- Downstream:
/write-plan, /cfn-plan-review, /cfn-loop-task
- Backlog + design rationale:
docs/PLANNING_PIPELINE_GAPS.md