* fix(#3724): stop advisory Dimension 3b findings from forcing the revision loop Dimension 3b (undeclared/temporal coupling, #1954) is spec'd "never a blocker" but tagged severity: warning — the tier plan-phase's revision loop counts as must-fix — and the planner is never taught the rule, so every multi-wave phase touching shared mutable state replans at least once, and intentionally coupled plans re-flag identically every iteration to the stall prompt. Three coordinated changes: - gsd-plan-checker: retag 3b to severity: info, the tier references/revision-loop.md already exempts by design; recognize a coupling_justified frontmatter declaration in the Do-NOT-flag list so deliberate pairs converge. Additions are offset by trimming 3b motivation prose — the checker sits 45 bytes under its LARGE hard cap. - plan-phase step 12: INFO-only accept — an issues block with zero BLOCKER/WARNING entries accepts the plan and surfaces the advisories instead of re-entering the revision loop. Real blockers and warnings still gate unconditionally. - gsd-planner: slim pointer in assign_waves to the new progressive-disclosure reference gsd-core/references/planner-coupling.md (the planner sits 19 chars under its own cap), which carries the shared-mutable-state rule and the coupling_justified escape hatch so first-pass plans avoid the finding when the coupling is unintentional. Documented the coupling_justified field in docs/reference/plan-md.md. Growth acks per #2914; inventory manifest and install-tree fixtures regenerated for the new reference file. Closes #3724 Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * test(#3724): pin Dimension 3b at severity: info The severity retag makes the old assertion (severity: warning) stale; lock the advisory tier from both directions — info must be present, warning must not — so a future edit cannot silently re-arm the revision-loop trigger. Refs #3724 Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * chore(#3724): changeset fragment for PR #3758 Refs #3724 Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * docs(#3724): roster planner-coupling.md in docs/INVENTORY.md The new reference was enumerated in the manifest and all 19 install-tree fixtures but missing its row in the Modular Planner Decomposition table — the roster half the manifest-sync test cannot check. (Review Blocker.) Refs #3724 Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * test(#3724): cover all four acceptance criteria (review round 1) - plan-checker-coupling: the 3b severity assertion is now a PARITY check deriving the exempt tier from revision-loop.md's flow instead of hardcoding info — editing either side alone reds the suite. New describe pins the other three criteria: plan-phase's INFO-only accept clause (proven failing-first), the BLOCKER + WARNING count staying intact, the coupling_justified Do-NOT-flag exemption + fix_hint, and the planner pointer + planner-coupling.md content. - ack fragment: $comment's plan-phase figure corrected to +79B; the 2775 pin note carried forward into the gsd-planner.md entry, updated for upstream's #3761/#3764 Rule-paragraph anchor (which this diff leaves verbatim). The parallel-dependent-plans re-anchor this commit originally carried was superseded by upstream #3764 during review; this branch no longer touches that file. Refs #3724 Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): review round 2 — align the stance enumeration, complete the template contract MAJOR: <adversarial_stance>'s severity enumeration gains the INFO bullet so it agrees with Dimension 3b's 'ALWAYS INFO' mandate instead of contradicting it. Funded by extracting the inline <examples> block to the new progressive- disclosure reference gsd-core/references/plan-checker-examples.md (@-inlined from the same spot; #1949 precedent), which also restores the 3b motivation clause round 1 traded away (Nit 4) and nets the agent file SMALLER than base (49107 -> 48486) — the extraction the byte pressure was owed. MINOR: gsd-core/templates/phase-prompt.md now carries coupling_justified, and the field's shape becomes one 'plan-id: reason' string per coupled peer so a plan justified against two peers can express it; docs/reference/plan-md.md's Type column names the shape. NIT: the 3409 ack's plan-phase entry no longer calls the #1168 workflow ratchet an 'XL tier'. Acks and derived artifacts updated accordingly (checker entry removed — a shrink needs no ack; INVENTORY roster row + regen:derived for the new file). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * test(#3724): derive the 3b negative severity assertion (review round 2) Every severity token in the 3b span must BE the tier revision-loop.md exempts, replacing the hardcoded severity:warning negative — if the loop's exemption ever moves, the failure names the real conflict instead of blaming the agent file with a mutually-unsatisfiable pair. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): refit the planner coupling pointer under the char cap Upstream #3299 (PR #3390) grew agents/gsd-planner.md to 49146 chars at the base, leaving 5 chars of headroom where the +16-char pointer was measured against 13 more. The pointer prose shortens to 'Non-file coupling:' — 49150 chars, back under the strict 49152-char cap — and the ack figures follow. The @-path the tests pin is unchanged. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): re-home the plan-phase ack after the #3823 spent-fragment sweep Upstream #3078/#3823 deleted all fully-spent ack fragments, including 3409-unreachable-guard-arms.json, which carried this PR's plan-phase.md +79B append. Per the collision remedy that sweep added: take the deletion and home the still-live entry in this PR's own fragment. Figures re-measured at this merge base (90871 -> 90950 LF bytes). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): absorb the spent #3172 plan-phase fragment into this PR's ack Upstream #3825 shipped 3172-stated-failing-direction.json naming only plan-phase.md, now spent at the base — colliding with this PR's live plan-phase entry. Per the #3003 pattern the fully-spent single-path fragment is deleted and this fragment stays the path's one source; figures re-measured at this base (93073 -> 93152 LF bytes). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): review round 3 — true up the ack figures, restore the wave comment The fragment's absolute sizes are re-measured and anchored to basee40e9670(planner 47259 -> 47330 chars, checker 45537 -> 44916 B, plan-phase 91186 -> 91265 LF bytes), with a note that absolutes rot as next moves — the deltas are the durable claims. The round-1 removal of the '# Implicit dependency: files_modified overlap forces a later wave.' pseudocode comment offset headroom base drift had already returned, so it is restored (findings 2-3). Changeset gains the (#3724) backlink (finding 4). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): review round 4 — close the verify-work surface, harden the boundaries BLOCKER: verify-work.md's verify_gap_plans is the second multi-plan consumer of the checker's sentinels, and its ISSUES FOUND handler entered revision_loop with zero severity parsing — the guaranteed replan #3724 fixed in plan-phase, alive on the gap-closure surface. The handler now counts BLOCKER + WARNING and accepts INFO-only returns with advisories displayed. The checker's INFO stance bullet is reworded to the claim that is true everywhere ('revision gates count only BLOCKER + WARNING'). Minor 1: plan-phase's iteration_count >= 3 arm recounts severities, so an INFO-only third check accepts instead of halting on a '0 issues remain' user gate. Minor 2: the coupling_justified exemption now requires the entry to NAME the other plan, closing the blanket-suppression reading. Nit 1: INVENTORY row states the extraction buys cap headroom, not context. Nit 2: the advisory display gains a concrete format on both surfaces. Ack fragment re-anchored at baseddde001a: verify-work.md +264B (new entry), plan-phase.md +395B, checker still net negative (-512B). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * test(#3724): pin the verify-work accept and the iteration-cap boundary (review round 4) Two wiring assertions: verify_gap_plans' ISSUES FOUND handler gates on BLOCKER + WARNING and accepts INFO-only blocks, and plan-phase's iteration_count >= 3 arm recounts severities instead of gating advisories — the limit+1 boundary of the gate this PR fixes. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): review round 5 — fail closed at the gates, surface the advisory Blocker 1: the checker's step-10 status rule routes an INFO-only result to ## ISSUES FOUND (with a new ### Advisories (info) template section and a severity-aware recommendation) so the orchestrator receives the block and displays the advisory instead of silently accepting a bare PASSED. Blockers 2+3: all three gate surfaces (plan-phase step 12 both arms, verify-work verify_gap_plans) carry one canonical clause verbatim — an entry whose severity is missing or unrecognized counts as a BLOCKER (fail closed) — making the accept condition an explicit-INFO whitelist while keeping issue_count coherent for stall math. Major 1: the INFO stance bullet scopes its claim to the plan-phase and verify-work gates (quick mode's loop still revises on any ISSUES FOUND). Major 2: INVENTORY row and ack $comment state the extraction's real trade (readability, +0.6 KB eager runtime context), not a cap remedy. Minor 1: plan-md.md marks coupling_justified as prompt convention, unvalidated. Nit 1: ack absolutes re-anchored at base 1e67ec97; checker now +120B and acked. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * test(#3724): pin the round-5 contract — fail-closed parity, INFO-only return shape New: three-surface verbatim parity test for the fail-closed clause (Blockers 2+3); checker return-contract test for the INFO-only ## ISSUES FOUND route and advisories section (Blocker 1). All seven newly pinned tokens are absent at f3a5682d, so each new assertion fails pre-fix. Updated: accept-clause regexes track the explicit-INFO whitelist wording; the severity sweep scopes to the span's fenced yaml examples via yamlSeverityTiers (round-5 Minor 3, applied to the blocker negative too); the iteration-cap comment states it is a prose pin, not an executed boundary check (Minor 4); splitLines call sites document the line-pin coupling (Nit 2). Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): adopt next's line wrap in the 3b motivation clause — drops a wrap-only hunk from the diff Byte-identical content; the wrap difference was an artifact of the round-1 base adaptation predating upstream's #3003 landing. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM * fix(#3724): review round 7 — gate every checker consumer, not just the two audited ones Blocker: quick/steps/plan-checker-loop.md (issue-named in #3724) gets the same canonical fail-closed clause and explicit-INFO whitelist accept as plan-phase/verify-work — an INFO-only result proceeds instead of entering quick mode's revision loop. Major: import.md plan_validate handles the checker return by severity (INFO-only never blocks an import) and is added to agent-contracts.md's consumer enumeration, which had omitted it. The checker's INFO stance bullet drops the quick-mode carve-out — the claim is universally true again now that every consuming gate is severity-aware. Minor: an applied coupling_justified exemption is surfaced as its own info advisory so a stale one-sided declaration stays observable. Nit: plan-phase's revision-iteration Display line is explicitly conditioned on not having already proceeded to step 13. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM Emitted-Drift-Ack-Growth: import.md — #3724 round 7: the plan_validate step's checker-return handler becomes severity-aware — counts BLOCKER + WARNING failing closed and accepts an explicitly-INFO-only return with advisories displayed instead of blocking the import * test(#3724): pin the round-7 surfaces — five-gate parity, quick/import accepts, exemption visibility The verbatim fail-closed parity test extends to quick/steps/plan-checker-loop.md and import.md plan_validate; new assertions pin quick mode's INFO-only proceed, import's never-blocks accept, import.md's presence in agent-contracts.md's consumer row, and the surfaced coupling_justified exemption advisory. All four newly pinned token families are absent at the pre-fix head, so each new assertion fails first. Claude-Session: https://claude.ai/code/session_01GshUzpGjoxiw6uNRiFMHvM --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
349 lines
26 KiB
Markdown
349 lines
26 KiB
Markdown
# PLAN.md schema reference
|
||
|
||
A per-plan `PLAN.md` is GSD Core's executable unit of work — a structured document that tells an executor agent exactly what to build and how to verify it was built correctly. This page documents its structure. See [docs index](../README.md).
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
Plans live inside phase directories at:
|
||
|
||
```
|
||
.planning/phases/<NN>-<slug>/<NN>-<PP>-PLAN.md
|
||
```
|
||
|
||
For example: `.planning/phases/03-post-feed/03-02-PLAN.md` (Phase 3, Plan 2).
|
||
|
||
Plans are produced by the `gsd-planner` agent (spawned by `/gsd-plan-phase`) and consumed by `execute-phase`. A phase typically contains between one and four plans; plans within a phase are assigned to execution waves so that independent work runs in parallel.
|
||
|
||
---
|
||
|
||
## YAML frontmatter
|
||
|
||
Every PLAN.md opens with a YAML frontmatter block between `---` delimiters.
|
||
|
||
### Annotated example
|
||
|
||
```yaml
|
||
---
|
||
phase: 03-post-feed
|
||
plan: 02
|
||
type: execute
|
||
wave: 2
|
||
depends_on: ["03-01"]
|
||
files_modified:
|
||
- src/components/PostFeed.tsx
|
||
- src/components/PostCard.tsx
|
||
- src/app/feed/page.tsx
|
||
files_deleted:
|
||
- src/components/LegacyFeed.tsx
|
||
autonomous: true
|
||
requirements: ["FEED-01", "FEED-03"]
|
||
user_setup: []
|
||
|
||
must_haves:
|
||
truths:
|
||
- "User can scroll through posts from followed accounts"
|
||
- "Each post shows author avatar, name, timestamp, and content"
|
||
- "Empty state appears when no posts exist"
|
||
artifacts:
|
||
- path: "src/components/PostFeed.tsx"
|
||
provides: "Scrollable post list"
|
||
min_lines: 40
|
||
- path: "src/components/PostCard.tsx"
|
||
provides: "Individual post card"
|
||
exports: ["PostCard"]
|
||
key_links:
|
||
- from: "src/components/PostFeed.tsx"
|
||
to: "src/app/api/feed/route.ts"
|
||
via: "fetch in useEffect — calls /api/feed endpoint"
|
||
pattern: "fetch.*api/feed"
|
||
---
|
||
```
|
||
|
||
### Frontmatter field reference
|
||
|
||
| Field | Required | Type | Purpose |
|
||
|---|---|---|---|
|
||
| `phase` | Yes | string | Phase identifier, e.g. `03-post-feed`. |
|
||
| `plan` | Yes | string | Plan number within the phase, e.g. `02`. |
|
||
| `type` | Yes | `execute` or `tdd` | `execute` for standard plans; `tdd` for test-driven plans where tests are written before implementation. |
|
||
| `wave` | Yes | integer | Execution wave. Plans in wave 1 run in parallel (no dependencies). Plans in wave 2+ wait for all plans in the previous wave to complete. Pre-computed at plan time by `gsd-planner`. |
|
||
| `depends_on` | Yes | array of plan IDs | Plans this plan must wait for. Empty array = wave 1. Accepts three forms, resolved in order: the full plan id (`"03-01-auth-hardening"`), the canonical phase-plan prefix (`"03-01"`), or the bare plan number (`"01"`, #3897) — which resolves to the sibling plan in the **same phase** whose canonical id ends `-01`. The bare form is in-phase only; it never resolves across phases. If two plans in the same phase share a bare form, the first one (by sorted plan-file order) wins — deterministic, but arbitrary when the collision happens, so prefer the full or canonical form when phase has any short-form collision risk. Example: `["03-01"]` means this plan runs after Plan 01 in Phase 3; from within Phase 3 itself, `["01"]` means the same thing. |
|
||
| `files_modified` | Yes | array of paths | Every file this plan creates or modifies. Used by the plan-checker to detect same-wave file conflicts and by execute-phase for merge tracking. |
|
||
| `files_deleted` | No | array of paths | Every file this plan deliberately **removes**. The post-wave cleanup gauntlet blocks the merge of any executor branch whose diff deletes a file — a net against a mass-deletion accident — and this field is the opt-in that names the exceptions. Matching is exact per path after separator normalization: a declared path merges, an undeclared one still blocks that plan's entry (and only that entry). There are no globs and no directory prefixes, so a declaration can never authorize more than it literally lists. Omit the field and the guard's original unconditional block stays in force, which is why absence is always the safe default (#3003). Counts toward same-wave conflict detection alongside `files_modified`: a plan deleting a file another plan in the same wave is editing is the sharpest conflict there is — one branch removes what the other is writing — so the two plans are pushed into different waves regardless of which side holds the deletion. |
|
||
| `coupling_justified` | No | array of `"plan-id: reason"` strings | One entry per deliberately coupled same-wave peer, e.g. `["03-02: both append independent config keys"]` — declares that the coupling with that plan through a shared mutable resource (config key, table, migration, env var) is deliberate and order-independent. The plan-checker's Dimension 3b recognizes the declaration and does not flag the pair, so intentionally coupled plans can pass verification without serializing waves. The `"plan-id: reason"` shape is a prompt-level convention read by the checker, not a schema — the plan parser (`src/plan-document.cts`) neither validates nor rejects the field, so a typo'd plan-id silently exempts nothing (#3724). |
|
||
| `autonomous` | Yes | boolean | `true` when all tasks are type `auto`. `false` when the plan contains any `checkpoint:*` task that requires human interaction. |
|
||
| `requirements` | Yes | array of IDs | Requirement IDs from ROADMAP.md that this plan addresses. Every phase requirement ID must appear in at least one plan's `requirements` field. Empty arrays are a BLOCKER. |
|
||
| `user_setup` | No | array of objects | External-service setup steps that Claude cannot automate (account creation, secret retrieval, dashboard configuration). When present, execute-phase generates a `USER-SETUP.md` checklist for the developer. |
|
||
| `status` | No | `superseded` | Marks a plan that was deliberately reassigned or abandoned mid-phase and will never be executed. A `status: superseded` plan is excluded from the phase's plan and summary counts, so it never holds the phase below 100%. See [Superseded plans](#superseded-plans). Any other value (or the field's absence) has no effect on counting. |
|
||
| `estimate` | No | object | Projected execution cost: `{tokens, raw_tokens, tasks, confidence}` (#2631, [ADR-2629](../adr/2629-phase-effort-estimation-calibration.md)). `tokens` is an `estimateTokens`-scale projection with the project's calibration factor **already applied** (which is why the plan-checker passes `--calibrated` to `estimate-check` — re-applying it would square the correction); `confidence` (`low`/`med`/`high`) is **derived from the calibration sample count, never self-rated**. Additive and optional — a plan without it behaves exactly as before. A plan estimated above `workflow.smart_zone_tokens` is flagged with a split recommendation at plan time; the flag is advisory and never blocks. |
|
||
| `must_haves` | Yes | object | Goal-backward verification criteria. See below. |
|
||
| `agent_hint` | No | string | Per-plan specialist executor routing (#1689). Name of a subagent that shares the `gsd-executor` execution contract (reads `execute-plan.md`, atomic-commit protocol). When the named agent resolves on the active runtime (an agent file exists in the runtime's agent dir), `execute-phase` dispatches it instead of `gsd-executor`. Unset/unresolved → `gsd-executor`, byte-identical to today. Default-on via `workflow.agent_hint_routing`; set `false` to disable. See [Per-plan executor routing](#per-plan-executor-routing). |
|
||
| `gap_closure` | Only in gap-closure mode | string, exact match | Must be exactly the literal lowercase `true` — validated as a string comparison, not a YAML boolean, so `True`, `TRUE`, `yes`, and `1` are all rejected. Required on every plan generated by `/gsd-plan-phase --gaps`, checked by the `plan-gap-closure` schema (`src/frontmatter.cts`) rather than `plan`. `/gsd-execute-phase --gaps-only` filters strictly on this field, so an omitted or wrong-valued `gap_closure` on a gap-closure plan means it is silently skipped — zero executors spawned, no error (#2847). Standard and reviews-mode plans validate against the unmodified `plan` schema, which neither requires nor checks this field (nothing rejects it as an extra field either, if present). |
|
||
|
||
### Per-plan executor routing
|
||
|
||
A plan can opt into a **specialist executor** by setting `agent_hint:` to the name of a subagent that shares the `gsd-executor` execution contract — it reads `execute-plan.md`, follows the atomic-commit protocol, and carries Read/Edit/Write/Bash. A Flutter specialist, for example:
|
||
|
||
```yaml
|
||
---
|
||
agent_hint: well-me-flutter-engineer
|
||
---
|
||
```
|
||
|
||
At dispatch, `execute-phase` resolves the hint against the **active runtime's agent directory** (both project-local and user-global, across the runtime's filename variants — `.md`, `.agent.md`, `.toml`, …) and dispatches the named subagent via `subagent_type`. If the field is absent, blank, or the named agent does not resolve, the plan dispatches to `gsd-executor` — byte-identical to behavior without the field. Routing is gated by `workflow.agent_hint_routing` (default-on; see [CONFIGURATION](../CONFIGURATION.md#workflow-toggles)).
|
||
|
||
The specialist agent is an ordinary agent file (e.g. `agents/well-me-flutter-engineer.md` on Claude Code); there is no separate registration manifest.
|
||
|
||
### Superseded plans
|
||
|
||
A phase reads complete when every `*-PLAN.md` has a matching `*-SUMMARY.md`. When a plan is reassigned or dropped mid-phase — its work folded into a later plan — it will never gain a summary, and without a marker it would pin the phase below 100% forever (the plan-level analogue of a retired phase). Add `status: superseded` to that plan's frontmatter to exclude it from **both** the plan count (denominator) and the summary count (numerator):
|
||
|
||
```yaml
|
||
---
|
||
phase: 05-api
|
||
plan: "12"
|
||
type: execute
|
||
status: superseded
|
||
---
|
||
```
|
||
|
||
A phase with 13 plans, two of them `superseded`, then reads `11/11 → complete` — no fabricated summary required. The match is case-insensitive. Plans without the marker are counted exactly as before.
|
||
|
||
---
|
||
|
||
## `must_haves` field
|
||
|
||
`must_haves` captures what must be observably true for the phase goal to be achieved. It is derived during planning and verified after execution by the `gsd-verifier` agent.
|
||
|
||
### Sub-fields
|
||
|
||
| Sub-field | Type | Purpose |
|
||
|---|---|---|
|
||
| `truths` | array of strings | Observable behaviours from the user's perspective. Each must be verifiable. Example: `"User can send a message"`, not `"WebSocket library installed"`. |
|
||
| `artifacts` | array of objects | Files that must exist with substantive implementation (not stubs). |
|
||
| `artifacts[].path` | string | File path relative to project root. |
|
||
| `artifacts[].provides` | string | What capability this file delivers. |
|
||
| `artifacts[].min_lines` | integer (optional) | Minimum line count to be considered non-stub. |
|
||
| `artifacts[].exports` | array of strings (optional) | Expected named exports to verify. |
|
||
| `artifacts[].contains` | string (optional) | Regex or literal pattern that must appear in the file. |
|
||
| `key_links` | array of objects | Critical connections between artifacts — the wiring that makes the system work end-to-end. |
|
||
| `key_links[].from` | string | Source file (relative path from project root). Must be a literal file path — describe components or symbols in `via:`. |
|
||
| `key_links[].to` | string | Target file (relative path from project root). Must be a literal file path — describe endpoints, modules, or APIs in `via:`. |
|
||
| `key_links[].via` | string | Description of how they connect, including any endpoint, component, or symbol name (e.g. `fetch in useEffect — calls /api/feed`, `Prisma query via prisma.message`, `import`). |
|
||
| `key_links[].pattern` | string (optional) | Regex to verify the connection exists in source. |
|
||
|
||
---
|
||
|
||
## Body structure
|
||
|
||
After frontmatter, the plan body uses named XML-style blocks read by the executor agent.
|
||
|
||
### `<objective>`
|
||
|
||
States what the plan delivers and why it matters for the project:
|
||
|
||
```xml
|
||
<objective>
|
||
Implement the post feed as a scrollable card list.
|
||
|
||
Purpose: Core display feature for the social feed phase.
|
||
Output: PostFeed and PostCard components wired to /api/feed.
|
||
</objective>
|
||
```
|
||
|
||
### `<execution_context>`
|
||
|
||
Lists the workflow files associated with executing the plan. Always includes the execute-plan workflow; adds the checkpoints reference when the plan contains checkpoint tasks:
|
||
|
||
```xml
|
||
<execution_context>
|
||
@~/.claude/gsd-core/workflows/execute-plan.md
|
||
@~/.claude/gsd-core/templates/summary.md
|
||
</execution_context>
|
||
```
|
||
|
||
These `@` paths point at the local GSD install, not at repository files. The prefix shown here (`~/.claude/gsd-core/…`) is the Claude global-install location; other runtimes and local installs resolve to their own install directory — for example `.cursor/gsd-core/…`, or an absolute project path for a `--local` install. Because the prefix is install-relative, this block is not clone-portable: a committed plan carries whichever prefix the authoring install had. Execution does not depend on it — `/gsd-execute-phase` loads the execute-plan workflow from its own installed copy — so the block records the execution context rather than resolvable repository references. Contrast `<context>` (below), whose repository-relative `@` paths resolve after a `git clone`.
|
||
|
||
### `<context>`
|
||
|
||
References source files the executor needs to read. Includes project-level planning docs and any source files whose patterns or types the plan must replicate. Prior plan `SUMMARY.md` files are included only when there is a genuine dependency (imported types, shared decision) — not reflexively:
|
||
|
||
```xml
|
||
<context>
|
||
@.planning/PROJECT.md
|
||
@.planning/ROADMAP.md
|
||
@.planning/STATE.md
|
||
@src/components/UserCard.tsx
|
||
</context>
|
||
```
|
||
|
||
### `<tasks>`
|
||
|
||
Contains one or more `<task>` elements. Every task element must carry `<name>`, `<files>`, `<read_first>`, `<action>`, `<verify>`, `<acceptance_criteria>`, and `<done>` for `type="auto"` and `type="tracer"` tasks. Optional `<precondition>` (see [Preconditions](#preconditions)) and `<reversibility>` (see [Reversibility](#reversibility)) elements may sit between `<name>` and `<files>`.
|
||
|
||
---
|
||
|
||
## Preconditions
|
||
|
||
`<precondition>` is an **optional** element on `<task>` (issue #1949, *The Pragmatic Programmer* Topic 23 — Design by Contract). It states, in a single line of runnable/checkable prose, what must already be true for the task to begin safely. It closes the front-of-task side of the contract triad — preconditions (before) ↔ postconditions (`<verify>`/`<done>`/`<acceptance_criteria>`, after) ↔ invariants (`must_haves.truths`, across the whole plan).
|
||
|
||
```xml
|
||
<task type="auto">
|
||
<name>Add /reveal endpoint handler</name>
|
||
<precondition>server bootstraps and responds to GET /health (from the tracer slice)</precondition>
|
||
<files>server/reveal.ts</files>
|
||
<action>…</action>
|
||
<verify>curl /reveal?path=… opens the OS file manager</verify>
|
||
<done>Endpoint committed and manually verified</done>
|
||
</task>
|
||
```
|
||
|
||
**Optional and back-compat:** a plan that omits `<precondition>` on every task behaves exactly as today — the executor skips the check with no visible change. Adding `<precondition>` to a task tells the executor to assert it before any other task work (read-only checks only: file existence, env var presence, idempotent health pings; no side-effecting checks — halt and surface a checkpoint if one seems required) and halt (returning a `checkpoint:human-verify`, no partial commit) on an unmet precondition. Plans that include `<precondition>` pass `verify plan-structure` unchanged — the structural validator checks for the presence of required tags and does not reject unknown optional tags.
|
||
|
||
**Emission cases** (planner-side): emit `<precondition>` only when a task relies on state the plan's own `depends_on` ordering does not already guarantee. Three cases cover every legitimate use:
|
||
|
||
1. **External service setup** (`user_setup` frontmatter) — the consuming task ties a specific setup step to itself so the executor halts if the setup was skipped.
|
||
2. **Prior-phase artifact dependency** — a generated schema, a migration's dist output, a contract file from an earlier phase. Cross-phase `depends_on` does not cross phase boundaries, so `<precondition>` is the explicit pointer.
|
||
3. **Environment variable / runtime configuration** — a tool, API, or script the task invokes requires an env var or runtime config that exists *now*, not at plan time.
|
||
|
||
Full emission rules, anti-patterns ("the system is ready" is not checkable; do not use `<precondition>` for intra-plan sequencing — that is what `depends_on` is for), and the contract triad mapping: see `gsd-core/references/planner-preconditions.md`.
|
||
|
||
---
|
||
|
||
## Reversibility
|
||
|
||
`<reversibility>` is an **optional** element on `<task>` (issue #1951, *The Pragmatic Programmer* Topic 15 — "Reversibility"). It records how costly the decision the task implements would be to undo, so a one-way-door choice gets a human beat before the agent walks through it. The `rating` attribute carries the classification; the body carries a one-line rationale.
|
||
|
||
```xml
|
||
<task type="auto">
|
||
<name>Define the on-disk event log format</name>
|
||
<reversibility rating="one-way">Phases 4-6 read this file; changing the
|
||
format after they land requires a migration for every existing project.</reversibility>
|
||
<files>src/event-log.cts</files>
|
||
<action>…</action>
|
||
<verify><automated>npm run test:unit -- event-log</automated></verify>
|
||
<done>Format documented and written by the writer under test</done>
|
||
</task>
|
||
```
|
||
|
||
| Rating | Meaning | Effect on the plan |
|
||
|---|---|---|
|
||
| `reversible` | Undo is local and cheap. | None. This is the default when no rating is given. |
|
||
| `costly` | Undo touches many call sites or needs a coordinated change. | Flagged in the plan so the reader sees the weight. Never blocks. |
|
||
| `one-way` | Undo requires a migration, breaks a published contract, or is impossible. | The planner inserts a `checkpoint:decision` immediately **before** the dependent task. |
|
||
|
||
**Optional and back-compat:** a plan that omits `<reversibility>` on every task behaves exactly as today — no flag, no checkpoint. Plans that include it pass `verify plan-structure` unchanged; the structural validator checks for the presence of required tags and does not reject unknown optional tags.
|
||
|
||
**Autonomy:** inserting a `checkpoint:decision` means the plan contains a checkpoint, so its frontmatter must set `autonomous: false`.
|
||
|
||
**Override:** `/gsd-plan-phase --no-reversibility-gates` (`REVERSIBILITY_GATES=false`) suppresses checkpoint insertion for intentionally-unattended runs. Ratings are still recorded and `costly` items are still flagged — the override changes what stops the run, not what the plan remembers.
|
||
|
||
Full taxonomy, emission rules, and anti-patterns (chiefly: rating everything `one-way` produces checkpoint fatigue; prefer *removing* irreversibility over gating it): see `gsd-core/references/planner-reversibility.md`.
|
||
|
||
---
|
||
|
||
## Task types
|
||
|
||
| Type | Use | Autonomy |
|
||
|---|---|---|
|
||
| `auto` | Everything the executor can do independently. | Fully autonomous. |
|
||
| `tracer` | The leading thin end-to-end slice a plan starts with by default (tracer-first) — production-quality, wired through every layer, with a real end-to-end `<verify>`. | Fully autonomous; after committing, the executor runs the tracer's `<verify>` as an early integration gate. A tracer carrying `gate="blocking-human"` STOPs for a human in every mode, auto included. Otherwise autonomous runs halt on failure before expansion, and interactive runs honor `workflow.human_verify_mode` (#3299): under the `end-of-phase` default a `<verify>` carrying only `<automated>` is re-run and, on success, expansion continues with **no** checkpoint (failure still halts); under `mid-flight`, or when the tracer carries `<human-check>`, a `checkpoint:human-verify` is presented. Full precedence chain: `gsd-core/references/checkpoints.md` → "Tracer feedback gate". |
|
||
| `checkpoint:human-verify` | Visual or functional verification that requires a human to look at a running UI or service. | Pauses execution; presents to the developer; resumes on approval. |
|
||
| `checkpoint:decision` | Implementation choices that arose during execution and require the developer's input. | Pauses execution; presents options; resumes on selection. |
|
||
| `checkpoint:human-action` | Truly unavoidable manual steps (account creation, hardware interaction). Used sparingly. | Pauses execution; resumes on confirmation. |
|
||
|
||
Plans that contain any checkpoint task must set `autonomous: false` in frontmatter.
|
||
|
||
---
|
||
|
||
## `auto` task structure
|
||
|
||
```xml
|
||
<task type="auto">
|
||
<name>Task 1: Create PostCard component</name>
|
||
<files>src/components/PostCard.tsx</files>
|
||
<read_first>src/components/UserCard.tsx, src/types/post.ts</read_first>
|
||
<action>Create PostCard component accepting a Post prop (id, authorId, content, createdAt,
|
||
reactionCount). Render author avatar using UserAvatar from UserCard pattern. Show timestamp
|
||
using date-fns formatDistanceToNow. Export as named export PostCard.</action>
|
||
<verify>npx tsc --noEmit</verify>
|
||
<acceptance_criteria>
|
||
- src/components/PostCard.tsx exports named export PostCard
|
||
- PostCard.tsx contains "reactionCount" prop usage
|
||
- npx tsc --noEmit exits 0
|
||
</acceptance_criteria>
|
||
<done>PostCard renders post content with author and timestamp</done>
|
||
</task>
|
||
```
|
||
|
||
### Required fields for `auto` tasks
|
||
|
||
| Field | Rule |
|
||
|---|---|
|
||
| `<files>` | Every file the task creates or modifies. The executor writes only these files. |
|
||
| `<read_first>` | Files the executor must read before touching anything — the file being modified, any source-of-truth pattern file, any file whose types or conventions must be replicated. |
|
||
| `<action>` | Concrete instructions with exact identifiers, file paths, function signatures, and expected values. Never says "align X with Y" without specifying the target state. Never contains fenced code blocks or full implementations. |
|
||
| `<verify>` | A runnable command or check that proves the task succeeded. Must distinguish pass from fail — `echo "done"` is not valid. Accepts either the wrapped form (`<verify><automated>cmd</automated></verify>`) or the legacy bare-text form (`<verify>cmd</verify>`); both are valid. **On a `type="tracer"` task, prefer the wrapped form:** the tracer feedback gate's auto-continue (#3299) requires a `<verify>` carrying only `<automated>`, so a bare-text tracer verify falls through to the STOP fallback and still presents a `checkpoint:human-verify` in interactive runs even under `end-of-phase`. |
|
||
| `<acceptance_criteria>` | Verifiable conditions: grep-verifiable strings, command exit codes, observable behaviours. No subjective language ("looks correct", "properly configured"). Negative greps (`! grep -Eq 'PAT' file`) are file-scoped — region-scope them (`sed -n`/`awk` range, then grep) when a sibling task needs the construct elsewhere in the same file (#968). |
|
||
| `<done>` | A short measurable statement of the completed outcome. |
|
||
|
||
---
|
||
|
||
## Plan quality dimensions
|
||
|
||
The `gsd-plan-checker` agent reviews every PLAN.md across 12 dimensions before execution begins. A plan that fails any BLOCKER-severity check is returned to `gsd-planner` for revision (up to 3 iterations):
|
||
|
||
| Dimension | What it checks |
|
||
|---|---|
|
||
| **1 — Requirement Coverage** | Every phase requirement ID from ROADMAP.md appears in at least one plan's `requirements` frontmatter field and has covering task(s). |
|
||
| **2 — Task Completeness** | Every `auto` task carries all required fields (`<files>`, `<action>`, `<verify>`, `<acceptance_criteria>`, `<done>`). No vague or empty fields. |
|
||
| **3 — Dependency Correctness** | `depends_on` references are valid, acyclic, and consistent with wave numbers. Wave N plan depends only on plans in waves < N. |
|
||
| **4 — Key Links Planned** | Artifacts in `must_haves.key_links` have corresponding tasks that implement the wiring — not just the artifact creation. |
|
||
| **5 — Scope Sanity** | Plans stay within context budget: 2–3 tasks per plan (4 = warning, 5+ = BLOCKER), ≤ 8–10 files per plan (15+ = BLOCKER). |
|
||
| **6 — Verification Derivation** | `must_haves.truths` are user-observable behaviours, not implementation details. Artifacts map to truths. Key links cover critical wiring. |
|
||
| **7 — Context Compliance** | Every `D-NN` decision from CONTEXT.md is addressed by at least one task. No task implements anything from `<deferred>`. |
|
||
| **7b — Scope Reduction Detection** | Task actions do not silently reduce a locked decision to a "v1", "stub", or "future enhancement" without delivering the full decision scope. Always a BLOCKER when found. |
|
||
| **7c — Architectural Tier Compliance** | Tasks assign capabilities to the correct tier per the RESEARCH.md Architectural Responsibility Map (when present). Security-sensitive capabilities in the wrong tier are BLOCKERs. |
|
||
| **8 — Nyquist Compliance** | When `workflow.nyquist_validation` is enabled and RESEARCH.md exists, every task has an `<automated>` verify command, no consecutive window of 3 tasks lacks coverage, and VALIDATION.md is present. |
|
||
| **9 — Cross-Plan Data Contracts** | When plans share data pipelines, their transformations are compatible — no plan strips data that another plan needs in original form. |
|
||
| **10 — CLAUDE.md Compliance** | Plans respect project-specific conventions, forbidden patterns, required tools, and security requirements from `./CLAUDE.md`. |
|
||
| **11 — Research Resolution** | When RESEARCH.md exists, its `## Open Questions` section is marked `(RESOLVED)` before planning proceeds. |
|
||
| **12 — Pattern Compliance** | When PATTERNS.md exists, tasks reference the correct analog patterns for each new or modified file. |
|
||
|
||
---
|
||
|
||
## Wave execution model
|
||
|
||
Wave numbers are pre-computed during planning. Execute-phase groups plans by wave number and runs each wave's plans in parallel:
|
||
|
||
```
|
||
Wave 1: Plan 01, Plan 02, Plan 03 (all run simultaneously — no dependencies)
|
||
Wave 2: Plan 04 (waits for Wave 1 to complete)
|
||
Wave 3: Plan 05 (waits for Wave 2 to complete)
|
||
```
|
||
|
||
Plans within a wave that modify overlapping files must not be in the same wave — the plan-checker's Dimension 3 flags this as a BLOCKER.
|
||
|
||
---
|
||
|
||
## Plan output
|
||
|
||
After a plan executes successfully, the executor writes a SUMMARY.md at:
|
||
|
||
```
|
||
.planning/phases/<NN>-<slug>/<NN>-<PP>-SUMMARY.md
|
||
```
|
||
|
||
The SUMMARY.md is the canonical record of what was built. Subsequent plans in the same phase may reference it when they have a genuine dependency on its types or decisions.
|
||
|
||
---
|
||
|
||
## Related
|
||
|
||
- [CONTEXT.md schema](context-md.md)
|
||
- [Planning artifacts](planning-artifacts.md)
|
||
- [Features](../FEATURES.md)
|
||
- [docs index](../README.md)
|