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).
Medium features (3-7 files, single shared-state surface): use /cfn-megaplan-lite, the balanced-cut alternative.
Invocation
/cfn-megaplan "<task>" [--tier=mvp|beta|enterprise] [--bar-b=full|sonnet] [--unattended] # 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.
--unattended (also CFN_MEGAPLAN_UNATTENDED=1): no human is watching. Every gate that would stall on AskUserQuestion takes its recorded default instead of stopping (tier inference takes the inferred tier; BLOCKING [OPEN] items take the conservative side and are re-marked [AUTO: <default> | unattended]; the wireframe gate auto-approves, see the gate rule). Every auto-taken decision is recorded with decided_by: auto-unattended so Step 7 can list them as one batch for the human to overturn. Unattended never widens scope, never loosens a boundary, and never skips a bar; it only removes the wait.
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/<slug>/AUDIT_<PHASE>_<slug>.md (findings table, file:line | issue | severity | fix). Synthesis (Step 7) merges them into planning/<slug>/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
flooritem (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
AskUserQuestionbefore 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.mdexists, 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-debtfeedback loop). If.cfn-cache/tech-debt-ledger.jsonexists, READ it (never re-harvest) and list any opencfn: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_triggerrows (rot risk) rank first.
# Read open tech debt in scope. Surfaces all open debt unfiltered (no relevance scoring).
# cfn: surfaces every ledger row, add path/keyword relevance filter when a ledger exceeds ~50 markers
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)
PDIR=$(.claude/skills/cfn-megaplan/lib/plan-paths.sh ensure "$SLUG") # planning/<slug>, created
Every artifact of this plan lands in ONE per-plan directory: planning/<slug>/. Filenames keep the slug suffix ($PDIR/SPEC_<slug>.md, $PDIR/UX_<slug>.md, $PDIR/OPS_<slug>.md), because every sidecar and ledger name in the bars is derived from the basename and the suffix keeps a file self-identifying when it is copied out of its directory. Nothing this pipeline writes goes loose in planning/.
Path rules for every step below and every phase prompt:
- Writes always use
$PDIR/<NAME>(nested). Never write toplanning/<NAME>. - Reads resolve through the shared resolver, which checks
$PDIR/<NAME>first and falls back to the legacy flatplanning/<NAME>so plans written before this layout keep working:
SPEC=$(.claude/skills/cfn-megaplan/lib/plan-paths.sh resolve "$SLUG" "SPEC_${SLUG}.md") \
|| echo "FATAL: no SPEC artifact at $SPEC"
plan-paths.sh (.claude/skills/cfn-megaplan/lib/plan-paths.sh) is the single source of truth for this layout — dir|ensure|resolve|write|newest|slug-of, sourceable as plan_dir/plan_resolve/etc. Downstream (/write-plan, /cfn-loop-task, cfn-workbench, cfn-share) resolves through it too. Do not hand-roll the nested-then-flat lookup.
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 $PDIR/SPEC_<slug>.md (resolve it via plan-paths.sh resolve, Step 1). 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.
Bar B executor tier. profile.bars.haiku_executable is sonnet at mvp/beta and full at enterprise; --bar-b=full|sonnet overrides it. sonnet models the real executor (opus coordinator + sonnet lanes): steps name file + symbol + done predicate, typed signatures optional, no live haiku probe. full is the haiku-literal bar with the probe. Rules and rationale: bars/haiku-executable.md "Executor tier". Pass the resolved tier into /write-plan (it changes the step-row validity rule) and record it in the Bar B gate report as bar_b_tier and in the synthesis Gates line as tier=<sonnet|full> (cfn-loop-task 5E.6 reads that token into the run ledger, which is how a too-loose sonnet bar becomes visible: cli/run-ledger.sh stats). bars.verifiable_done is always full; there is no lower tier for Bar A.
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/extrasinto 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
floorarray is forced into the relevant phase regardless of tier or directive. Alightdataphase still authors RLS, auth boundaries, secrets handling. Askip-level concern that is infloor(e.g.pii_if_presentwhenpiiflag 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:
modelpresent → spawn that phase at that model.modelabsent → inherit the session model. Everywrite_plan/plan_reviewstep 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, anddecideatfullstayopusin every profile. Downgrading them moves the error upstream of everything, where it is most expensive. - A
directive: lightphase is the safe downgrade candidate —lightalready dropped the parts that needed judgment. - Never downgrade a phase carrying a
flooritem. - If a downgraded phase fails Bar A/Bar B twice on the same finding, re-run it at
opusand 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.
Size gate at every join (L2 through L9). After each level returns and before the next is spawned, run the byte-cap check against the plan dir with the tier profile:
.claude/skills/cfn-megaplan/bars/check-size.sh --all "$PDIR" --profile .claude/skills/cfn-megaplan/profiles/<tier>.json # the profile loaded at Step 2
Caps live in the profile's .caps (mvp = 2x, beta = 3x, enterprise = 4x the cfn-megaplan-fast caps; see _caps_note). An OVER line means that artifact will be re-read in full by every downstream phase, which is the measured #1 cost driver (SPECs at 110-136KB and VERIFYs at 545KB in the curve2026 run). Rule: one sonnet compress spawn per OVER artifact (general-purpose, prompt: "remove prose, keep every id/table/contract/check/AC row; target <= bytes; do not drop or renumber ids"), re-run the check once; still OVER → record [PARKED] size-over: <artifact> <bytes>/<cap> and advance. The gate never blocks a level and never spawns a second compress. Cap the phase prompt too: paste Byte cap for this artifact: <cap> from the same profile so the phase aims under it instead of being compressed after.
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 (the template, nothing more — see Brief budget below):
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>
Plan dir: planning/<slug>/ (ALL your reads and writes live here; never write loose in planning/)
Read inputs: <dep artifact paths, each already resolved to a real path by the orchestrator>
Write artifact: planning/<slug>/<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).
Brief budget. A phase brief is the template above filled in — assignment, paths, cap — and stays ≤ 15% of the artifact's byte cap (3.5KB at mvp SPEC). Brief size drives artifact size: the writer mirrors the brief's register, so a multi-KB brief produces an over-cap artifact and pays the compress spawn at the join (reported 2026-09-03: thousands-of-words briefs → 36KB SPEC vs 24KB cap → 234K subagent tokens compressing two files). Context never rides in the brief: research findings, prior decisions, and phase detail go to input files the agent reads (dep artifacts, decision-log rows, the phase skill itself). /.claude/brief-size-warn.log`).cfn-spawn-depth-guard.sh warns on main-chat briefs over CFN_BRIEF_MAX_BYTES (default 4096; log `
If any phase returns BLOCKING [OPEN] items, batch them and surface via AskUserQuestion before advancing past the level. Record every resolved decision via .claude/skills/cfn-decisions/record.sh (closes gap G35/decision-log loop AND populates the per-run JSON ledger). The writer owns the per-run JSON register and delegates the SQLite sync to decision-log/record.sh (behavior-preserving for SQLite via delegation, additive for JSON as a new per-run artifact); the orchestrator forwards mid-level decisions to the writer, which records them.
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-uxin 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 viaAskUserQuestion(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-dataand re-run the affected levels, not acfn-uxpatch. - A
_skipped: no renderable screens_reference raises no gate. - Unattended runs (
--unattended/CFN_MEGAPLAN_UNATTENDED=1). The gate still fires but does not stop. Auto-approval means exactly: record the approval to the decision log aswireframe: <ref> | approved_by: auto-unattended | reason: no reviewer present, write the same line into the[PARKED]running list so it reaches the Step 7 batch as a "re-open?" item, and proceed to L6 with the wireframe AS EMITTED (no auto-revise, no structure guess). An orchestrator that invents its own approval wording, or proceeds without recording, is off-protocol: a later human must be able to see that nobody looked at this picture. Attended runs never auto-approve.
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/<slug>/<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 $PDIR/<PHASE>_<slug>.md artifact (it resolves the plan dir itself via plan-paths.sh). Then run Bar A (bars/verifiable-done.md): convert success criteria to executable AC rows, emit $PDIR/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 "${PDIR}/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.
Run the size gate (Step 4) here too, over PLAN_<slug>.md and VERIFY_<slug>.md: a VERIFY over cap is usually duplicated evidence prose, not extra ACs; compress once per the Step 4 rule before blessing so the hash pins the compact form.
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 "${PDIR}/VERIFY_${SLUG}.md" --note "Bar A pass"
The bars are path-agnostic (every sidecar is derived from the file's own directory + basename), so the sidecar, snapshot, and ledger land in $PDIR alongside the manifest — nothing extra to configure.
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 $PDIR/PLAN_<slug>.md; this is the lane-derivation source cfn-loop-task reads. After /write-plan returns, assert the file exists in the plan dir (a PLAN_ that landed loose in planning/ means write-plan did not pick up the plan dir — move it into $PDIR and fix the invocation):
[ -f "${PDIR}/PLAN_${SLUG}.md" ] || { echo "FATAL: write-plan did not persist ${PDIR}/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) at the resolved executor tier: static + structural + coverage scans (the static set includes bars/check-phase-width.sh — <=15 steps, <=8 distinct files per step-number major, so loop-task's one-lane-per-phase rule yields parallelizable lanes; over = split the phase by file cluster in the step table), then the live haiku probe only at full (sonnet skips it; the structural scan accepts file + symbol name in place of a typed signature). 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. AskUserQuestioncaps 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 viacfn-decidelike 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) |
| one or more AC rows (added/removed/rewritten) | re-bless; then exactly what the bless's regate line says (per-AC scope, see bars/verifiable-done.md "Per-AC re-gate scope"). Typical: LLM Bar A on the named rows + coverage block, Bar B static+structural on the steps bound to them, no probe unless a row was added |
| a plan step's semantics, no AC change | Bar B static + structural on the changed steps only; live probe only if the change added a step or a new file/component |
a [core] FR (SPEC), or --force-full |
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 "$PDIR/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 plus a regate scope; read its predicate_changed line before accepting an override that touched a pass condition, and do the regate work before handing off. Never re-run the whole gate for a one-row change; never skip a row regate names.
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 $PDIR/PLAN_<slug>.md (lane source) and $PDIR/VERIFY_<slug>.md (completion gate), in the plan dir. 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 "${PDIR}/${F}.md" ] || echo "FATAL: missing ${PDIR}/${F}.md — megaplan not build-ready"
done
[ -f "${PDIR}/.VERIFY_${SLUG}.sha256" ] || echo "FATAL: missing ${PDIR}/.VERIFY_${SLUG}.sha256 — Bar A hash not blessed (re-run Step 5 static pass + hash)"
# nothing from this plan may sit loose in the planning root
ls planning/*_"${SLUG}".md 2>/dev/null && echo "WARN: plan artifacts loose in planning/ — move them into ${PDIR}/"
Write $PDIR/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)
Plan dir: planning/<slug>/
<list of planning/<slug>/*_<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/<slug>/VERIFY_<slug>.md
- Bar B haiku-executable: PASS (0 findings after <r> rounds, tier=<sonnet|full>)
# tier= is read back by cfn-loop-task 5E.6 (cli/run-ledger.sh); keep the token exact.
# 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 planning/<slug>/PLAN_<slug>.md for lanes
+ planning/<slug>/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 directory layout. Each sibling plan keeps its own planning/<mpN-slug>/. Program-scoped files (index, shared register) get one more dir at the same level: planning/<program-slug>/. Nothing program-scoped goes loose in planning/ either.
Program index doc. Write
planning/<program-slug>/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 (`planning//DECISIONS_.md
…(truncated)