Sub-issue of #3180 — Phase 8 of 8. Added by the 2026-08-08 coverage audit; specified in ADR-3180 § 7.5 and Decision 4(d)/(e), which are the source of truth for the behavior below.
Pre-submission checklist
What existing feature or behavior does this improve?
Plan and summary counting in the prompt layer — the workflow markdown under gsd-core/workflows/ that ships to every runtime and counts .planning/ artifacts in raw shell.
Current behavior
Seven sites across four workflows re-derive live-plan counting with ls -1 … *-PLAN.md | wc -l instead of asking the CLI:
gsd-core/workflows/progress.md — the plan and summary counts
gsd-core/workflows/execute-plan.md — the same pair
gsd-core/workflows/plan-phase.md — DISK_PLANS, twice
gsd-core/workflows/plan-review-convergence.md — PLAN_COUNT
A shell glob is not scanPhasePlans, so every fix that landed on the owner missed all seven: they count status: superseded plans as live (#2349 never reached them), report zero for phases on the post-#3139 nested plans/ layout, and miss loosely-named plan files. #1762's 30 plans, 24 summaries figure came from here — not from either of the two .cts copies the epic originally named. A second path in progress.md has an independent schema mismatch, consuming .plans/.summaries arrays that the roadmap-analysis output never emits.
Until ADR-3180 Amendment 3 this surface was invisible to every guard, because all of them scanned src/ and called that "whole-repo". It is now covered by scripts/lint-planning-prompt-drift.cjs with a shrink-only baseline of exactly those 7 sites: new sites fail, and a baseline entry that stops firing also fails. The guard makes the debt visible and non-growing; it does not remove it.
Proposed behavior
A CLI surface answering "how many plans and summaries are live in this phase", backed by scanPhasePlans — so the prompt layer asks rather than re-deriving. All seven sites call it. The baseline goes to zero, and the ratchet's stale-entry check makes the removal self-enforcing: the guard fails if an entry is left behind after its site is migrated.
Concretely, for a user:
/gsd-progress and /gsd-execute-plan stop counting superseded plans as outstanding work.
- A phase using the nested
plans/ layout stops reporting zero plans in the workflow output.
- The plan count a workflow shows and the plan count
phase find reports are the same number for the same phase — today they can differ.
The .plans/.summaries schema mismatch in progress.md is fixed in the same change; it is the same defect class (a consumer inventing a contract the producer never had).
Reason and benefit
Why the current behavior is a problem: the prompt layer is what the agent actually reads. A wrong plan count there does not merely display wrong — it drives wave scheduling, convergence checks and "is this phase done" decisions inside the workflows themselves. And because the count is produced by shell inside a markdown file, no amount of consolidation in src/ reaches it: this is the one surface where the epic's central mechanism (give the derivation an owner and route callers to it) has no import to route through.
Concrete benefit: the last surface that can disagree with scanPhasePlans stops being able to. Every future fix to plan-liveness semantics — including the closed terminal-state vocabulary § 7.5 calls for — reaches the workflows automatically instead of needing seven separate shell edits nobody will remember to make.
Scope of changes
Files modified:
- a CLI verb (or an addition to an existing one) returning live plan/summary counts for a phase, backed by
scanPhasePlans — exact surface to be settled in design, per ADR-3180 § 7.5's planFiles vs allPlanFiles distinction
gsd-core/workflows/progress.md — 2 sites, plus the .plans/.summaries schema mismatch
gsd-core/workflows/execute-plan.md — 2 sites
gsd-core/workflows/plan-phase.md — 2 sites
gsd-core/workflows/plan-review-convergence.md — 1 site
scripts/baselines/planning-prompt-drift-baseline.json — emptied as sites migrate
tests/planning-prompt-drift.test.cjs — extend; plus behavioral coverage that the CLI surface and scanPhasePlans agree, including a superseded plan and a nested-layout phase
.changeset/, docs/
Workflow edits ripple to the workflow size gates and section-manifest.json — see the repo's workflow-edit checklist.
Breaking changes
Yes — the numbers change, in the direction of being right.
- A phase whose plans are all
status: superseded reports zero live plans in workflow output, not a positive count.
- A phase on the nested
plans/ layout stops reporting zero.
- Loosely-named plan files start being counted.
Anything downstream that had calibrated against the shell-glob numbers will see different values. This cannot be made backward compatible without preserving the defect — the two counts disagreeing is the bug.
Alternatives considered
- Fix the shell snippets in place — teach each one to skip superseded plans. Rejected — seven copies of the supersession rule, in a language with no type checking, in files that ship to 19 runtimes. This is the epic's failure mode with extra steps.
- Leave the ratchet in place and call the surface acknowledged. Rejected — a ratchet is a way to watch debt, not a way to keep it. ADR-3180 Decision 4(e) requires the guard to land with an owning phase precisely so an acknowledgment cannot become permanent.
- Have workflows parse
roadmap analyze JSON. Partially viable and should be evaluated in design — but the .plans/.summaries mismatch already in progress.md is what happens when a prompt assumes a shape that command does not emit, so any JSON-consuming path needs the contract asserted by a test, not by reading.
- Extend the drift guard to auto-fix. Rejected — a guard that rewrites shipped prompt text is a code generator wearing a lint's clothes.
Area affected
Planning system (phases, roadmap, state)
Additional context
Depends on whichever phase first exposes the CLI surface this calls; the semantics it needs already exist in scanPhasePlans (Phase 1, #3183). Independent of Phases 4–7 otherwise.
Related: #1762 (the reproduction that found this surface). The plan-lifecycle representation gap in ADR-3180 § 7.5 — prose- and fence-retired plans carrying no status key — is not in scope here and needs its own decision; this phase makes the prompt layer agree with the owner, whatever the owner decides "live" means.
Parent epic: #3180 · Spec: ADR-3180 § 7.5, Decision 4(d)/(e) · Guard: scripts/lint-planning-prompt-drift.cjs
Sub-issue of #3180 — Phase 8 of 8. Added by the 2026-08-08 coverage audit; specified in ADR-3180 § 7.5 and Decision 4(d)/(e), which are the source of truth for the behavior below.
Pre-submission checklist
approved-enhancementbefore writing any codeWhat existing feature or behavior does this improve?
Plan and summary counting in the prompt layer — the workflow markdown under
gsd-core/workflows/that ships to every runtime and counts.planning/artifacts in raw shell.Current behavior
Seven sites across four workflows re-derive live-plan counting with
ls -1 … *-PLAN.md | wc -linstead of asking the CLI:gsd-core/workflows/progress.md— the plan and summary countsgsd-core/workflows/execute-plan.md— the same pairgsd-core/workflows/plan-phase.md—DISK_PLANS, twicegsd-core/workflows/plan-review-convergence.md—PLAN_COUNTA shell glob is not
scanPhasePlans, so every fix that landed on the owner missed all seven: they countstatus: supersededplans as live (#2349 never reached them), report zero for phases on the post-#3139 nestedplans/layout, and miss loosely-named plan files. #1762's30 plans, 24 summariesfigure came from here — not from either of the two.ctscopies the epic originally named. A second path inprogress.mdhas an independent schema mismatch, consuming.plans/.summariesarrays that the roadmap-analysis output never emits.Until ADR-3180 Amendment 3 this surface was invisible to every guard, because all of them scanned
src/and called that "whole-repo". It is now covered byscripts/lint-planning-prompt-drift.cjswith a shrink-only baseline of exactly those 7 sites: new sites fail, and a baseline entry that stops firing also fails. The guard makes the debt visible and non-growing; it does not remove it.Proposed behavior
A CLI surface answering "how many plans and summaries are live in this phase", backed by
scanPhasePlans— so the prompt layer asks rather than re-deriving. All seven sites call it. The baseline goes to zero, and the ratchet's stale-entry check makes the removal self-enforcing: the guard fails if an entry is left behind after its site is migrated.Concretely, for a user:
/gsd-progressand/gsd-execute-planstop counting superseded plans as outstanding work.plans/layout stops reporting zero plans in the workflow output.phase findreports are the same number for the same phase — today they can differ.The
.plans/.summariesschema mismatch inprogress.mdis fixed in the same change; it is the same defect class (a consumer inventing a contract the producer never had).Reason and benefit
Why the current behavior is a problem: the prompt layer is what the agent actually reads. A wrong plan count there does not merely display wrong — it drives wave scheduling, convergence checks and "is this phase done" decisions inside the workflows themselves. And because the count is produced by shell inside a markdown file, no amount of consolidation in
src/reaches it: this is the one surface where the epic's central mechanism (give the derivation an owner and route callers to it) has no import to route through.Concrete benefit: the last surface that can disagree with
scanPhasePlansstops being able to. Every future fix to plan-liveness semantics — including the closed terminal-state vocabulary § 7.5 calls for — reaches the workflows automatically instead of needing seven separate shell edits nobody will remember to make.Scope of changes
Files modified:
scanPhasePlans— exact surface to be settled in design, per ADR-3180 § 7.5'splanFilesvsallPlanFilesdistinctiongsd-core/workflows/progress.md— 2 sites, plus the.plans/.summariesschema mismatchgsd-core/workflows/execute-plan.md— 2 sitesgsd-core/workflows/plan-phase.md— 2 sitesgsd-core/workflows/plan-review-convergence.md— 1 sitescripts/baselines/planning-prompt-drift-baseline.json— emptied as sites migratetests/planning-prompt-drift.test.cjs— extend; plus behavioral coverage that the CLI surface andscanPhasePlansagree, including a superseded plan and a nested-layout phase.changeset/,docs/Workflow edits ripple to the workflow size gates and
section-manifest.json— see the repo's workflow-edit checklist.Breaking changes
Yes — the numbers change, in the direction of being right.
status: supersededreports zero live plans in workflow output, not a positive count.plans/layout stops reporting zero.Anything downstream that had calibrated against the shell-glob numbers will see different values. This cannot be made backward compatible without preserving the defect — the two counts disagreeing is the bug.
Alternatives considered
roadmap analyzeJSON. Partially viable and should be evaluated in design — but the.plans/.summariesmismatch already inprogress.mdis what happens when a prompt assumes a shape that command does not emit, so any JSON-consuming path needs the contract asserted by a test, not by reading.Area affected
Planning system (phases, roadmap, state)
Additional context
Depends on whichever phase first exposes the CLI surface this calls; the semantics it needs already exist in
scanPhasePlans(Phase 1, #3183). Independent of Phases 4–7 otherwise.Related: #1762 (the reproduction that found this surface). The plan-lifecycle representation gap in ADR-3180 § 7.5 — prose- and fence-retired plans carrying no
statuskey — is not in scope here and needs its own decision; this phase makes the prompt layer agree with the owner, whatever the owner decides "live" means.Parent epic: #3180 · Spec: ADR-3180 § 7.5, Decision 4(d)/(e) · Guard:
scripts/lint-planning-prompt-drift.cjs