* refactor(#720): lazy-load MVP-only reference bodies (eager @-import → gated Read) Convert eager @-imports of MVP-only reference bodies into lazy "Read" instructions gated on MVP_MODE / WALKING_SKELETON / MVP+TDD, so non-MVP planning/execution runs no longer pull MVP guidance into context. Covers both the workflow files and the planner/executor agent definitions (the dominant context-cost path): - workflows/plan-phase.md: planner-mvp-mode.md + skeleton-template.md (L146/936/937/941) - workflows/execute-phase.md: execute-mvp-tdd.md halt-report ref, now gated on gate-trip (L191) - agents/gsd-planner.md: planner-mvp-mode.md, user-story-template.md, skeleton-template.md - agents/gsd-executor.md: execute-mvp-tdd.md The dedicated always-MVP mvp-phase workflow keeps its eager imports (intentional). Behaviour is unchanged; non-MVP runs simply carry less loaded context. Adds a regression guard mirroring the discuss-phase lazy-load test, and documents the conformance in docs/ARCHITECTURE.md. Refs #720 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#720): add changeset fragment (pr #746) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/lazy-mvp-bodies-progressive-disclosure.md
Normal file
5
.changeset/lazy-mvp-bodies-progressive-disclosure.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Changed
|
||||
pr: 746
|
||||
---
|
||||
**`/gsd:plan-phase` and `/gsd:execute-phase` no longer eagerly load MVP-only guidance on non-MVP runs** — the MVP planner rules, user-story template, Walking-Skeleton template, and MVP+TDD halt-report reference are now Read lazily by the planner/executor only when MVP / Walking-Skeleton / MVP+TDD mode is active, in both the workflow files and the `gsd-planner`/`gsd-executor` agent definitions, instead of being `@`-imported into every run. Behaviour is unchanged; non-MVP planning/execution simply carries less context. (#720)
|
||||
@@ -385,7 +385,7 @@ If RED or GREEN gate commits are missing, add a warning to SUMMARY.md under a `#
|
||||
|
||||
## MVP+TDD Gate
|
||||
|
||||
**When the orchestrator passes both `MVP_MODE=true` and `TDD_MODE=true`:** Before running the implementation step of any task with `tdd="true"`, run the runtime gate from `@~/.claude/gsd-core/references/execute-mvp-tdd.md`. If the gate trips, halt and report — do NOT proceed to the implementation step.
|
||||
**When the orchestrator passes both `MVP_MODE=true` and `TDD_MODE=true`:** Before running the implementation step of any task with `tdd="true"`, run the runtime gate from `~/.claude/gsd-core/references/execute-mvp-tdd.md` (Read it). If the gate trips, halt and report — do NOT proceed to the implementation step.
|
||||
|
||||
**Halt-and-report protocol:**
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ Exceptions where `tdd="true"` is not needed: `type="checkpoint:*"` tasks, config
|
||||
|
||||
## MVP Mode Detection
|
||||
|
||||
**When `MVP_MODE` is enabled (passed by the plan-phase orchestrator):** Decompose tasks as **vertical feature slices**, not horizontal layers. Required reading: `@~/.claude/gsd-core/references/planner-mvp-mode.md` (loaded conditionally by the orchestrator).
|
||||
**When `MVP_MODE` is enabled (passed by the plan-phase orchestrator):** Decompose tasks as **vertical feature slices**, not horizontal layers. Required reading: Read `~/.claude/gsd-core/references/planner-mvp-mode.md` for the vertical-slice rules (lazy — only on MVP runs).
|
||||
|
||||
**Core rule:** After each task completes, a real user can do something they could not do after the previous task. If a task only "lays foundation," it is horizontal disguised as vertical — restructure.
|
||||
|
||||
@@ -323,7 +323,7 @@ Exceptions where `tdd="true"` is not needed: `type="checkpoint:*"` tasks, config
|
||||
**As a** [user role], **I want to** [capability], **so that** [outcome].
|
||||
```
|
||||
|
||||
Format rules from `@~/.claude/gsd-core/references/user-story-template.md`:
|
||||
Format rules (Read `~/.claude/gsd-core/references/user-story-template.md`):
|
||||
- All three slots required. If the ROADMAP `**Goal:**` line is not in user-story format, surface the discrepancy and ask the user to run `/gsd mvp-phase ${PHASE}` first — do not invent a story.
|
||||
- Bold the three keywords (`**As a**`, `**I want to**`, `**so that**`) when emitting to PLAN.md. The ROADMAP form does not use bolded keywords; the PLAN form does.
|
||||
2. First task: failing end-to-end test for the happy path.
|
||||
@@ -332,7 +332,7 @@ Exceptions where `tdd="true"` is not needed: `type="checkpoint:*"` tasks, config
|
||||
|
||||
**Mode is all-or-nothing per phase** (PRD decision Q1). Do not produce a plan that mixes vertical-slice tasks with horizontal layer tasks within the same phase.
|
||||
|
||||
**Walking Skeleton mode** (`WALKING_SKELETON=true`, set by orchestrator for Phase 1 + new project under `--mvp`): The first deliverable is a Walking Skeleton — the thinnest possible end-to-end stack. In addition to `PLAN.md`, produce `SKELETON.md` using the template at `@~/.claude/gsd-core/references/skeleton-template.md`. `SKELETON.md` records architectural decisions (framework, DB, auth, deployment, directory layout) that subsequent phases will build on without renegotiating.
|
||||
**Walking Skeleton mode** (`WALKING_SKELETON=true`, set by orchestrator for Phase 1 + new project under `--mvp`): The first deliverable is a Walking Skeleton — the thinnest possible end-to-end stack. In addition to `PLAN.md`, produce `SKELETON.md` using the template at `~/.claude/gsd-core/references/skeleton-template.md` (Read it now). `SKELETON.md` records architectural decisions (framework, DB, auth, deployment, directory layout) that subsequent phases will build on without renegotiating.
|
||||
|
||||
**Compatibility with TDD detection:** When both `MVP_MODE=true` and `workflow.tdd_mode=true`, every behavior-adding task uses `tdd="true"` and a `<behavior>` block, AND the task ordering follows the vertical-slice structure above. The first task is always a failing end-to-end test.
|
||||
|
||||
|
||||
@@ -193,6 +193,16 @@ parent dispatches, modes/ holds per-flag behavior (`power.md`, `all.md`,
|
||||
checkpoint.json schemas that are read only when the corresponding output
|
||||
file is being written.
|
||||
|
||||
`workflows/plan-phase.md`, `workflows/execute-phase.md`, and the
|
||||
`gsd-planner` / `gsd-executor` agent definitions apply the same discipline
|
||||
to their MVP-only reference bodies — `planner-mvp-mode.md`,
|
||||
`user-story-template.md`, `skeleton-template.md`, and `execute-mvp-tdd.md`
|
||||
are referenced for the planner/executor to Read only on MVP,
|
||||
Walking-Skeleton, or MVP+TDD paths, rather than eagerly `@`-imported, so
|
||||
non-MVP runs do not pay their context cost (guards against the "`@`-import
|
||||
behind a conditional still loads eagerly" leak; see #720). The dedicated
|
||||
`mvp-phase` workflow keeps its eager imports, since it is always MVP.
|
||||
|
||||
### Agents (`agents/*.md`)
|
||||
|
||||
Specialized agent definitions with frontmatter specifying:
|
||||
|
||||
@@ -188,7 +188,7 @@ if [ "$MVP_MODE" = "true" ] && [ "$TDD_MODE" = "true" ]; then
|
||||
fi
|
||||
fi
|
||||
```
|
||||
Pure doc-only / config-only / test-only tasks return `is_behavior_adding=false` and are exempt. See `execute-mvp-tdd.md` for the halt report format.
|
||||
Pure doc-only / config-only / test-only tasks return `is_behavior_adding=false` and are exempt. When the gate trips, Read `~/.claude/gsd-core/references/execute-mvp-tdd.md` for the exact halt report format.
|
||||
</step>
|
||||
|
||||
<step name="check_blocking_antipatterns" priority="first">
|
||||
|
||||
@@ -143,7 +143,7 @@ fi
|
||||
```
|
||||
|
||||
When `WALKING_SKELETON=true`:
|
||||
- Planner is instructed to produce `SKELETON.md` in the phase directory alongside `PLAN.md`. The template lives at `@~/.claude/gsd-core/references/skeleton-template.md`.
|
||||
- Planner is instructed to produce `SKELETON.md` in the phase directory alongside `PLAN.md`. The template lives at `~/.claude/gsd-core/references/skeleton-template.md` — the planner reads it when producing SKELETON.md (lazy; not loaded on non-skeleton runs).
|
||||
- The plan must scaffold project + routing + one real DB read/write + one real UI interaction + dev deployment — the thinnest possible end-to-end working slice.
|
||||
|
||||
**Interaction with `--prd <filepath>`.** `--mvp` and `--prd` compose. The PRD express path (Step 3.5) creates `CONTEXT.md` from the PRD file and continues to research; the Walking Skeleton gate fires independently from the conditions above. When both are active on Phase 1 of a new project, the planner receives `WALKING_SKELETON=true` and PRD-derived context simultaneously — the PRD informs *what the skeleton should prove*. No precedence is needed; the two signals are orthogonal. See [`references/mvp-concepts.md`](../references/mvp-concepts.md) for the broader interaction map.
|
||||
@@ -933,12 +933,12 @@ Each TDD plan gets one feature with RED/GREEN/REFACTOR gate sequence.
|
||||
</tdd_mode_active>
|
||||
` : ''}
|
||||
|
||||
**MVP_MODE:** ${MVP_MODE} (when true, follow vertical-slice rules from `@~/.claude/gsd-core/references/planner-mvp-mode.md`; when false, ignore MVP guidance entirely.)
|
||||
**WALKING_SKELETON:** ${WALKING_SKELETON} (when true, the first deliverable must be a Walking Skeleton — produce SKELETON.md alongside PLAN.md.)
|
||||
**MVP_MODE:** ${MVP_MODE} (when true, follow vertical-slice rules from `~/.claude/gsd-core/references/planner-mvp-mode.md`; when false, ignore MVP guidance entirely.)
|
||||
**WALKING_SKELETON:** ${WALKING_SKELETON} (when true, the first deliverable must be a Walking Skeleton — Read the template at `~/.claude/gsd-core/references/skeleton-template.md` and produce SKELETON.md alongside PLAN.md.)
|
||||
|
||||
${MVP_MODE === 'true' ? `
|
||||
<mvp_mode_active>
|
||||
**MVP Mode is ENABLED.** Follow vertical-slice planning rules from @~/.claude/gsd-core/references/planner-mvp-mode.md. Each plan must deliver a complete vertical slice — thin end-to-end functionality rather than horizontal layers.
|
||||
**MVP Mode is ENABLED.** Read `~/.claude/gsd-core/references/planner-mvp-mode.md` now and follow its vertical-slice planning rules. Each plan must deliver a complete vertical slice — thin end-to-end functionality rather than horizontal layers.
|
||||
</mvp_mode_active>
|
||||
` : ''}
|
||||
</planning_context>
|
||||
|
||||
@@ -402,3 +402,150 @@ describe('SIZE: discuss-phase progressive disclosure (issue #2551)', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
||||
|
||||
describe('workflow progressive disclosure — MVP bodies lazy-loaded (#720)', () => {
|
||||
// MVP-only reference bodies (planner-mvp-mode.md, skeleton-template.md,
|
||||
// execute-mvp-tdd.md) must NOT be eagerly @-imported at the top level of the
|
||||
// always-loaded workflow files or agent definitions. An @-prefixed path is
|
||||
// expanded into context the moment the file loads — regardless of whether
|
||||
// MVP_MODE is true — inflating every session's token cost. Use a plain
|
||||
// backtick path or a conditional "Read ..." instruction instead. See issue #720.
|
||||
|
||||
test('plan-phase.md does not eagerly @-import planner-mvp-mode.md', () => {
|
||||
const planPhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'plan-phase.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*planner-mvp-mode\.md/.test(planPhaseContent),
|
||||
'plan-phase.md contains an eager @-import of planner-mvp-mode.md — ' +
|
||||
'this loads the MVP body into context for every session, even when MVP_MODE is false. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('plan-phase.md does not eagerly @-import skeleton-template.md', () => {
|
||||
const planPhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'plan-phase.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*skeleton-template\.md/.test(planPhaseContent),
|
||||
'plan-phase.md contains an eager @-import of skeleton-template.md — ' +
|
||||
'this loads the template into context on every plan-phase invocation. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('plan-phase.md still references both MVP bodies (lazy reference preserved)', () => {
|
||||
const planPhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'plan-phase.md'), 'utf-8');
|
||||
assert.ok(
|
||||
/planner-mvp-mode\.md/.test(planPhaseContent) && /skeleton-template\.md/.test(planPhaseContent),
|
||||
'plan-phase.md must still reference planner-mvp-mode.md and skeleton-template.md ' +
|
||||
'(as lazy backtick paths or Read instructions) so agents know where to find them. ' +
|
||||
'Do not delete the references — only remove the leading @ sigil. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('plan-phase.md does not list MVP bodies in <required_reading>', () => {
|
||||
const planPhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'plan-phase.md'), 'utf-8');
|
||||
const requiredReadingMatch = planPhaseContent.match(/<required_reading>([\s\S]*?)<\/required_reading>/);
|
||||
if (requiredReadingMatch) {
|
||||
const block = requiredReadingMatch[1];
|
||||
assert.ok(
|
||||
!/planner-mvp-mode\.md/.test(block),
|
||||
'planner-mvp-mode.md must NOT appear in plan-phase.md <required_reading> — ' +
|
||||
'that block is always loaded regardless of MVP_MODE. See #720.'
|
||||
);
|
||||
assert.ok(
|
||||
!/skeleton-template\.md/.test(block),
|
||||
'skeleton-template.md must NOT appear in plan-phase.md <required_reading> — ' +
|
||||
'that block is always loaded regardless of MVP_MODE. See #720.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('execute-phase.md does not eagerly @-import execute-mvp-tdd.md', () => {
|
||||
const executePhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'execute-phase.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*execute-mvp-tdd\.md/.test(executePhaseContent),
|
||||
'execute-phase.md contains an eager @-import of execute-mvp-tdd.md — ' +
|
||||
'this loads the MVP TDD body into context for every session. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('execute-phase.md still references execute-mvp-tdd.md (lazy reference preserved)', () => {
|
||||
const executePhaseContent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'execute-phase.md'), 'utf-8');
|
||||
assert.ok(
|
||||
/execute-mvp-tdd\.md/.test(executePhaseContent),
|
||||
'execute-phase.md must still reference execute-mvp-tdd.md (as a lazy backtick path ' +
|
||||
'or Read instruction) so agents know where to find it. ' +
|
||||
'Do not delete the reference — only ensure there is no leading @ sigil. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-planner.md does not eagerly @-import planner-mvp-mode.md', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-planner.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*planner-mvp-mode\.md/.test(content),
|
||||
'gsd-planner.md contains an eager @-import of planner-mvp-mode.md — ' +
|
||||
'this loads the MVP body into context for every session, even when MVP_MODE is false. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-planner.md does not eagerly @-import skeleton-template.md', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-planner.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*skeleton-template\.md/.test(content),
|
||||
'gsd-planner.md contains an eager @-import of skeleton-template.md — ' +
|
||||
'this loads the template into context on every planner invocation. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-planner.md does not eagerly @-import user-story-template.md', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-planner.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*user-story-template\.md/.test(content),
|
||||
'gsd-planner.md contains an eager @-import of user-story-template.md — ' +
|
||||
'this loads the template into context on every planner invocation. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-planner.md still references the three MVP bodies', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-planner.md'), 'utf-8');
|
||||
assert.ok(
|
||||
/planner-mvp-mode\.md/.test(content),
|
||||
'gsd-planner.md must still reference planner-mvp-mode.md (as a lazy path or Read instruction). ' +
|
||||
'Do not delete the reference — only remove the leading @ sigil. See #720.'
|
||||
);
|
||||
assert.ok(
|
||||
/skeleton-template\.md/.test(content),
|
||||
'gsd-planner.md must still reference skeleton-template.md (as a lazy path or Read instruction). ' +
|
||||
'Do not delete the reference — only remove the leading @ sigil. See #720.'
|
||||
);
|
||||
assert.ok(
|
||||
/user-story-template\.md/.test(content),
|
||||
'gsd-planner.md must still reference user-story-template.md (as a lazy path or Read instruction). ' +
|
||||
'Do not delete the reference — only remove the leading @ sigil. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-executor.md does not eagerly @-import execute-mvp-tdd.md', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-executor.md'), 'utf-8');
|
||||
assert.ok(
|
||||
!/@[~./\w-]*execute-mvp-tdd\.md/.test(content),
|
||||
'gsd-executor.md contains an eager @-import of execute-mvp-tdd.md — ' +
|
||||
'this loads the MVP TDD body into context for every session. ' +
|
||||
'Replace with a conditional Read instruction or a plain backtick path. See #720.'
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-executor.md still references execute-mvp-tdd.md', () => {
|
||||
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-executor.md'), 'utf-8');
|
||||
assert.ok(
|
||||
/execute-mvp-tdd\.md/.test(content),
|
||||
'gsd-executor.md must still reference execute-mvp-tdd.md (as a lazy path or Read instruction). ' +
|
||||
'Do not delete the reference — only remove the leading @ sigil. See #720.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user