chore(#1671): fragmentize plan-phase.md and repair flag forwarding to the init bundle — Phase 6.2 (#3019)
* chore(#2993): fragmentize plan-phase.md onto the fragment model Epic #1671 Phase 6.2. plan-phase.md is the largest workflow in the repo and carried zero markers; it was deferred out of the Phase 3 pilot for two reasons, both now dead. The 36-byte PRE_PHASE6 headroom was never the blocker it looked like — fragmentizing is net-negative on host source, so the trim is what creates the room. The --mvp interleaving was resolved by measurement in #2992 and no sub-line mechanism is built. - widen WHEN_VOCABULARY 14 -> 19 via a second coordinated ADR-1671 amendment: flag:--ingest, flag:--prd, flag:--research-phase, flag:--reviews, state:chunked-mode - state:chunked-mode is `--chunked` OR config workflow.plan_chunked, and that disjunction is resolved in the FACT, never in the grammar, so a compound condition never becomes an operator - parse the new flags on the plan-phase route; extract six gated bodies to gsd-core/workflows/plan-phase/steps/ behind manifest-gated stubs - prd-express-path.md was already extracted but read unconditionally; its wrapper is now gated, so the existing extraction finally pays off plan-phase.md 94,483 -> 87,575 bytes (cap 94,519): headroom goes from 36 bytes to 6,944. Also closes a surfaced docs gap: five real plan-phase flags (--chunked, --skip-ui, --bounce, --skip-bounce, --granularity) were documented in neither the argument-hint nor help. Making --chunked load-bearing without fixing its siblings would leave the defect class half-open. Refs #2993 * fix(#2993): forward flags to the init bundle so section gating actually fires Blocker found by the correctness review, confirmed directly, and missed by both the isolated reviewer and every test in this branch. Neither workflow forwarded its flags to the init CLI: plan-phase.md:71 INIT=$(gsd_run query init.plan-phase "$PHASE" $GRAN_PARAM) execute-phase.md:84 INIT=$(gsd_run query init.execute-phase "${PHASE_ARG}") So every flag: atom was permanently false in production and its section permanently excluded. For plan-phase that made the PRD express path UNREACHABLE — a regression, since it was an unconditional read before. For execute-phase this is PRE-EXISTING: #2932 shipped `flag:--wave` gating that has never once been true, so `--wave` silently dropped its own wave-filtering guidance. Fixed here under the no-defer rule. Why every test missed it: they drive the init CLI directly with flags, which works. Production goes through the workflow's bash line, which did not pass them — the exact "assert against the shape production uses" trap this branch's own test matrix warns about. - parse and forward --prd/--ingest/--research-phase/--reviews/--chunked (plan-phase) and --wave (execute-phase), using the anchored regex idiom the neighbouring GRAN_PARAM line already uses - add a regression guard DERIVED FROM THE MANIFEST: for every flag:--X section, the owning workflow's init line must forward --X. It fails against the pre-fix files and covers any future atom, rather than spot-checking today's six. Verified through the workflow shape, not the CLI shape: `3 --prd spec.md` now yields ["prd-express-gate"] (was []), `2 --wave 2` yields ["partial-wave"] (was []). Refs #2993 * test(#2993): acknowledge the execute-phase ripple and regenerate install-tree fixtures Remote matrix was red with 46 unique failures, identical on both lanes. Both causes are mechanical consequences of changing shipped workflow content, and neither is visible to any local gate. - emitted-attribution: execute-phase.md grew 163 bytes from the WAVE_PARAM forwarding fix and was unacknowledged, while the ack fragment named plan-phase.md, which SHRANK and therefore needed no ack at all — a stale entry is itself a failure. The reason now names the real ripple. The entry had to merge into the existing 2930 fragment: the ack linter does unconditional cross-fragment duplicate-key detection with no spent/live exception, so a second fragment declaring execute-phase.md collides even when the first is already merged and inert. Resolved per the linter's own guidance and that file's precedent of appending successive ripple reasons to one entry. - golden-install-tree: tests/fixtures/install-tree/*.json are committed and deliberately excluded from the ADR-2719 attribution cutover, so they must be regenerated when shipped tree content changes. Regenerated after build:lib per the ordering landmine. 19 runtimes each gained exactly the six new plan-phase step files; zero paths removed, which is the absolute failure shape those fixtures exist to catch. Refs #2993 * fix(#2993): restore the launcher preamble in an extracted step and follow moved content in its drift guards Second red run: 26 unique failures, identical on both lanes, in two classes. RUNTIME BUG (runtime-launcher-parity, 7 failures) — chunked-planning-mode.md calls gsd_run but carried no canonical launcher preamble, which is what DEFINES gsd_run(). On any non-Claude runtime that step would fail outright. The preamble is now copied verbatim from the canonical source of truth, gsd-core/workflows/_runtime-launcher.snippet.sh, and the fence dedented to column 0 to match the prd-express-path.md sibling (a list-continuation indent breaks the byte-equal preamble match). prd-express-path.md already had a correct one. This is the same defect #2932 hit when it extracted steps; the parity test caught a real bug, not a stale assertion. DRIFT GUARDS (plan-phase-drift-guard, issue-2762-plan-reviews-chunked, skill-frontmatter-contract) — these assert plan-phase.md contains content this branch moved into step files. Retargeted at where the content now lives, with the asserted property unchanged; the ALL-RUNTIMES label COUNT test now reads host + every step file so the count is preserved across the split rather than reduced. Each retargeted guard was verified to still fail when its step file is stripped, so none was weakened into vacuity. No emitted-drift ack was needed: currentSizes() enumerates gsd-core/workflows/*.md non-recursively, so files under plan-phase/steps/ are never in the size ratchet's scope. Refs #2993 * chore(#2993): backfill changeset pr number to 3019 --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -46,8 +46,9 @@ gap fragment and composes back byte-identical to its source.
|
||||
|
||||
## The frozen `when=` vocabulary
|
||||
|
||||
`when=` takes exactly one of 14 atoms (widened from 4 via the ADR-1671
|
||||
amendment for #2992, epic #1671 Phase 6.1):
|
||||
`when=` takes exactly one of 19 atoms (widened from 4 to 14 via the ADR-1671
|
||||
amendment for #2992, epic #1671 Phase 6.1, then from 14 to 19 via the
|
||||
ADR-1671 amendment for #2993, epic #1671 Phase 6.2):
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
@@ -59,9 +60,14 @@ amendment for #2992, epic #1671 Phase 6.1):
|
||||
| `flag:--discuss` | Applicable when the workflow runs with `--discuss`. |
|
||||
| `flag:--forensic` | Applicable when the workflow runs with `--forensic`. |
|
||||
| `flag:--full` | Applicable when the workflow runs with `--full`. |
|
||||
| `flag:--ingest` | Applicable when the workflow runs with `--ingest <path-or-glob>`. |
|
||||
| `flag:--prd` | Applicable when the workflow runs with `--prd <file>`. |
|
||||
| `flag:--research` | Applicable when the workflow runs with `--research`. |
|
||||
| `flag:--research-phase` | Applicable when the workflow runs with `--research-phase <N>`. A distinct atom from `flag:--research` above — neither aliases the other. |
|
||||
| `flag:--reset-phase-numbers` | Applicable when the workflow runs with `--reset-phase-numbers`. |
|
||||
| `flag:--reviews` | Applicable when the workflow runs with `--reviews`. |
|
||||
| `flag:--validate` | Applicable when the workflow runs with `--validate`. |
|
||||
| `state:chunked-mode` | Applicable when chunked planning mode is active — see [Compound conditions are resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar) below. |
|
||||
| `state:needs-codebase-map` | Applicable when a codebase map is needed (init-computed). |
|
||||
| `state:phase-mvp-mode` | Applicable when the current phase's `ROADMAP.md` entry declares `**Mode:** mvp`. |
|
||||
| `state:worktrees-enabled` | Applicable when `.planning/config.json`'s `workflow.use_worktrees` is enabled. |
|
||||
@@ -99,6 +105,31 @@ points invoked by 20+ other workflows, so a dedicated `cmdInit*` seam does
|
||||
not yet exist to compute their facts. They are withheld pending that seam,
|
||||
not rejected.
|
||||
|
||||
### Compound conditions are resolved in the fact, never the grammar
|
||||
|
||||
`state:chunked-mode` looks, at the section-body level, like it should be a
|
||||
compound condition: plan-phase's chunked planning mode activates on
|
||||
`--chunked` **OR** `.planning/config.json`'s `workflow.plan_chunked` being
|
||||
`true`. The vocabulary stays operator-free anyway, because the disjunction is
|
||||
resolved **before** it ever reaches `when=` — the init seam
|
||||
(`buildSectionManifestField` in `src/init.cts`) computes ONE boolean,
|
||||
`InvocationFacts.chunkedMode = flags.has('--chunked') ||
|
||||
readConfigJsonBoolean(cwd, ['workflow', 'plan_chunked'])`, and
|
||||
`WHEN_PREDICATES['state:chunked-mode']` reads only that single field. The
|
||||
marker grammar never sees `--chunked`, never sees the config key, and never
|
||||
sees an `OR` — it sees exactly one atom with no operator, same as every other
|
||||
entry in the frozen list.
|
||||
|
||||
This is the general rule for any future atom whose real-world trigger is
|
||||
itself a compound expression: **compounding belongs in fact computation
|
||||
(`src/init.cts`), never in the `when=` grammar (`src/workflow-fragments.cts` /
|
||||
`src/section-manifest.cts`).** A condition that cannot be reduced to one
|
||||
boolean fact computed ahead of evaluation is not eligible to become an atom —
|
||||
widening the grammar itself to express `OR`/`AND`/negation is exactly the
|
||||
Greenspun's Tenth Rule drift [The frozen `when=`
|
||||
vocabulary](#the-frozen-when-vocabulary) above exists to prevent, regardless
|
||||
of how reasonable a single compound condition looks in isolation.
|
||||
|
||||
## Fails closed
|
||||
|
||||
An authoring mistake throws at parse time, naming the source file and 1-based
|
||||
@@ -192,7 +223,7 @@ At init time, a separate pure evaluator, `src/section-manifest.cts`
|
||||
(`selectSections`), partitions a workflow's manifest sections into
|
||||
`included`/`excluded` id lists against one invocation's
|
||||
`InvocationFacts` — `{flags, phaseNumber, hasPriorPhases, needsCodebaseMap?,
|
||||
phaseMvpMode?, worktreesEnabled?}`. Only a workflow with a **dedicated
|
||||
phaseMvpMode?, worktreesEnabled?, chunkedMode?}`. Only a workflow with a **dedicated
|
||||
`cmdInit*` entry point** in `src/init.cts` can have this evaluation run for
|
||||
it, because only that entry point can assemble `InvocationFacts` from its own
|
||||
parsed CLI options and `.planning/` state reads — this is admission gate 2
|
||||
@@ -214,42 +245,51 @@ own `false` into `undefined` (`namedArgs['wave'] || undefined`) before
|
||||
handing options to the facts builder, so `flags` only ever contains tokens
|
||||
that were actually seen.
|
||||
|
||||
## Piloted on one workflow so far
|
||||
## Piloted on execute-phase.md, then rolled out to plan-phase.md
|
||||
|
||||
Only `gsd-core/workflows/execute-phase.md` carries markers today. The marker
|
||||
grammar and composer seam are general-purpose across any workflow file, but
|
||||
rollout to other LARGE/XL workflows is intentionally sequenced as later work,
|
||||
not part of this phase.
|
||||
Two workflows carry markers today: `gsd-core/workflows/execute-phase.md` (the
|
||||
#2930/Phase-3 pilot) and `gsd-core/workflows/plan-phase.md` (#2993, epic
|
||||
#1671 Phase 6.2). The marker grammar and composer seam are general-purpose
|
||||
across any workflow file; rollout to further LARGE/XL workflows remains
|
||||
sequenced as later work.
|
||||
|
||||
The pilot marks three `<step>` blocks: `partial-wave` (`flag:--wave`),
|
||||
`gap-closure-artifacts` (`state:gap-closure-phase`), and `regression-gate`
|
||||
(`state:has-prior-phases`).
|
||||
`execute-phase.md` marks three `<step>` blocks: `partial-wave`
|
||||
(`flag:--wave`), `gap-closure-artifacts` (`state:gap-closure-phase`), and
|
||||
`regression-gate` (`state:has-prior-phases`).
|
||||
|
||||
**The pilot was retargeted from `plan-phase.md` mid-phase.** Issue #2930's
|
||||
own motivating mutually-exclusive branches (`--prd`, `--ingest`, `--mvp`,
|
||||
`--reviews`) all live in `plan-phase.md`, not `execute-phase.md`. But
|
||||
`plan-phase.md` sits only 36 B under an independent, pre-existing size gate
|
||||
(`tests/phase6-capstone-conformance.test.cjs`'s `PRE_PHASE6`, an ADR-857
|
||||
Phase-6 completion property) and cannot absorb any marker overhead at all —
|
||||
so it could not be fragmentized under this phase's grammar regardless of
|
||||
branch shape. This is direct evidence for the epic's premise that
|
||||
fragmentization pays off, and it also means Phase 4 (moving size caps from
|
||||
source bytes to emitted bytes) may need to land before `plan-phase.md`
|
||||
itself can be fragmentized. Separately, and independent of the size-gate
|
||||
finding, `--mvp` would remain unmarkable by this grammar even if the size
|
||||
gate allowed it: its content in `plan-phase.md` is INTERLEAVED with other
|
||||
flags rather than living in its own contiguous section (`MVP_MODE`
|
||||
`plan-phase.md` marks six sections: `reviews-prerequisite` (`flag:--reviews`),
|
||||
`prd-express-gate` (`flag:--prd`), `adr-ingest-express-path` (`flag:--ingest`),
|
||||
`research-only-modifiers` and `research-only-early-exit` (both
|
||||
`flag:--research-phase` — two consumers sharing one atom, gated by the same
|
||||
`RESEARCH_ONLY` condition, so they include/exclude together), and
|
||||
`chunked-planning-mode` (`state:chunked-mode`).
|
||||
|
||||
**`plan-phase.md` was originally retargeted away from the #2930 pilot,
|
||||
then fragmentized here once the blocker cleared.** Issue #2930's own
|
||||
motivating mutually-exclusive branches (`--prd`, `--ingest`, `--mvp`,
|
||||
`--reviews`) all live in `plan-phase.md`, not `execute-phase.md`, but at the
|
||||
time `plan-phase.md` sat only 36 B under an independent, pre-existing size
|
||||
gate (`tests/phase6-capstone-conformance.test.cjs`'s `PRE_PHASE6`, an
|
||||
ADR-857 Phase-6 completion property) and could not absorb any marker
|
||||
overhead at all. #2993 resolves this **because fragmentizing is net-negative
|
||||
on host source, not net-positive**: each gated body moves from always-inline
|
||||
prose to a `gsd-core/workflows/plan-phase/steps/<id>.md` step file, leaving
|
||||
only a ~200 B conditional-read stub behind — the six extractions trim
|
||||
`plan-phase.md` from 94,483 B to 87,575 B, moving the file from 36 B of
|
||||
`PRE_PHASE6` headroom to roughly 7,000 B, well clear of the cap.
|
||||
|
||||
`--mvp` remains unmarkable by this grammar, unchanged by #2993 and by
|
||||
deliberate ADR-1671 decision: its content in `plan-phase.md` is INTERLEAVED
|
||||
with other flags rather than living in its own contiguous section (`MVP_MODE`
|
||||
resolution shares a single bash block with `--tdd`, `--no-tracer`, and
|
||||
`--no-reversibility-gates` handling at `plan-phase.md:125-158`, and
|
||||
elsewhere it is inline `${MVP_MODE === 'true' ? ... }` template
|
||||
interpolation embedded inside the planner prompt at `plan-phase.md:794-803`)
|
||||
— the marker grammar is closed, non-nesting, and whole-line (see
|
||||
[Marker syntax](#marker-syntax) above), with no way to wrap part of a line
|
||||
or split a shared conditional block without either corrupting the
|
||||
`--no-reversibility-gates` handling, and elsewhere it is inline
|
||||
`${MVP_MODE === 'true' ? ... }` template interpolation embedded inside the
|
||||
planner prompt) — the marker grammar is closed, non-nesting, and whole-line
|
||||
(see [Marker syntax](#marker-syntax) above), with no way to wrap part of a
|
||||
line or split a shared conditional block without either corrupting the
|
||||
conditional or bundling unrelated flags into one section. See
|
||||
[ADR-1671](../adr/1671-dynamic-context-management-platform.md) open
|
||||
question 1's resolution for the full record, and Phase 6 (LARGE/XL rollout)
|
||||
for how both limits get addressed.
|
||||
question 1's resolution for the full record.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
Reference in New Issue
Block a user