Skip to content

enhance(#3180): the prompt layer asks the CLI instead of re-deriving — Phase 8 #3218

Description

@trek-e

Sub-issue of #3180Phase 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

  • I have confirmed this improves existing behavior — it does not add a new command, workflow, or concept
  • I have searched existing issues and this enhancement has not already been proposed
  • I have read CONTRIBUTING.md and understand I must wait for approved-enhancement before writing any code
  • I can clearly describe the concrete benefit — not just "it would be nicer"

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.mdDISK_PLANS, twice
  • gsd-core/workflows/plan-review-convergence.mdPLAN_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

  1. 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.
  2. 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.
  3. 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.
  4. 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

Metadata

Metadata

Assignees

Labels

approved-enhancementEnhancement approved — contributor may begin codingenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions