chore(#2932): emit a per-invocation section manifest from the init bundle (#2987)

* chore(#2932): emit a per-invocation section manifest from init

Extends the init bundle with a typed per-invocation section manifest so an
invocation loads only the branch guidance it will actually take.

The three flag/state-gated branches in execute-phase.md move into their own
step files; the parent keeps its gsd:section markers wrapping a one-line
on-demand reference, so each section's prose lives in exactly one file and
the parent shrinks 93369 -> 89507 bytes. A new drift-guarded generator
derives the shipped section manifest from those markers, and a new pure
evaluator maps invocation facts to applicable section ids.

The evaluator is a lookup over the frozen WHEN_VOCABULARY, never a parser
(Greenspun's Tenth Rule, ADR-1671:69); a parity test asserts the vocabulary
and the predicate map stay exhaustively in sync.

Closes #2932

* fix(#2932): fail closed on prototype-chain when values

An isolated adversarial review found WHEN_PREDICATES[section.when] was a
bracket lookup on a plain-prototype object, so inherited Object.prototype
members resolved as predicates: "constructor"/"toString"/"valueOf"/
"hasOwnProperty" returned truthy and SILENTLY INCLUDED the section, and
"__proto__" threw an untyped TypeError carrying no .reason. Both violate
the module's documented fail-closed contract, and the manifest is read from
disk at run time so it cannot be assumed trustworthy.

Builds the predicate map on a null prototype and guards the lookup with an
explicit Object.hasOwn check. Adds table-driven coverage for nine
Object.prototype-shaped keys asserting the TYPED reason (asserting only
that it throws would still pass while broken) plus a fast-check property
injecting a hostile value at an arbitrary document position.

* test(#2932): retarget execute-phase step assertions at extracted step files

* fix(#2932): emit typed reasons for generator lib-load and write failures

* fix(#2932): restore launcher preamble in extracted steps and refresh derived fixtures

* chore(#2932): backfill changeset pr number to 2987

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-02 12:34:41 -04:00
committed by GitHub
parent 33985c11a9
commit a987cf2731
46 changed files with 2680 additions and 179 deletions

View File

@@ -381,6 +381,29 @@ grammar, the frozen `when=` vocabulary, and fail-closed authoring rules, and
[ADR-1671](adr/1671-dynamic-context-management-platform.md) (open questions 1 and 2) for why
in-file markers were chosen over separate fragment files or a sidecar manifest.
### Section Manifest (`src/section-manifest.cts`, ADR-1671 Phase 5)
Two seams turn a workflow's `gsd:section` markers into per-invocation applicability data.
`scripts/gen-section-manifest.cjs --write` (wired into `build` after `build:lib`, and into
`lint:generated-sync`) scans `gsd-core/workflows/*.md` and writes the committed
`gsd-core/workflows/section-manifest.json`: a `{id, when, read}` triple per marked section,
where `read` is the path of the step file the section's body was extracted to. It reuses
`parseWorkflowSections` unchanged rather than re-implementing marker parsing, and fails closed
(`--check`) on a marker naming a step file that does not exist or a step file no marker
references.
A separate pure evaluator, `src/section-manifest.cts` (compiled to
`gsd-core/bin/lib/section-manifest.cjs` per ADR-457), maps one invocation's facts —
`{waveFlag, phaseNumber, hasPriorPhases}` — to an included/excluded partition of section ids
via `selectSections`. Per Greenspun's Tenth Rule, this is a total lookup over the frozen
`when=` vocabulary, never a parser: it never tokenizes or interprets `when=` structure, and an
unrecognized value fails closed rather than being silently excluded.
`execute-phase.md`'s `partial-wave` and `gap-closure-artifacts` sections — previously inlined
directly per #2930's pilot — now delegate to dedicated step files under
`gsd-core/workflows/execute-phase/steps/`, the same pattern the pre-existing `regression-gate`
section already used.
### CLI Tools (`gsd-core/bin/`)
Node.js CLI utility (`gsd-tools.cjs`) with domain modules split across `gsd-core/bin/lib/` (see [`docs/INVENTORY.md`](INVENTORY.md#cli-modules) for the authoritative roster):

View File

@@ -440,6 +440,7 @@
"runtime-slash.cjs",
"schema-detect.cjs",
"secrets.cjs",
"section-manifest.cjs",
"security.cjs",
"semver-compare.cjs",
"shell-command-projection.cjs",

View File

@@ -528,6 +528,7 @@ Full listing: `gsd-core/bin/lib/*.cjs`.
| `runtime-slash.cjs` | Runtime-aware slash-command formatter — single source of truth for emitting `/gsd-<cmd>` (skills-based runtimes) and `$gsd-<cmd>` (codex) in user-facing output and persisted artifacts (#3584) |
| `schema-detect.cjs` | Schema-drift detection for ORM patterns (Prisma, Drizzle, Supabase, TypeORM, Payload); exports `detectSchemaFiles`, `detectSchemaOrm`, `checkSchemaDrift`, `SCHEMA_PATTERNS`, `ORM_INFO` |
| `secrets.cjs` | Secret-config masking convention (`****<last-4>`) for integration keys; exports `SECRET_CONFIG_KEYS`, `isSecretKey`, `maskSecret`, `maskIfSecret` |
| `section-manifest.cjs` | Pure `when=` evaluator over `InvocationFacts` (ADR-1671, epic #1671 Phase 5, #2932) — `selectSections` partitions a document-order list of parsed `gsd:section` sections into `included`/`excluded` id arrays for one concrete invocation, via `WHEN_PREDICATES`, a total lookup (never a parser) over the frozen `WHEN_VOCABULARY` imported unchanged from `workflow-fragments.cjs`; an unrecognized `when=` value fails closed (`REASON.UNKNOWN_WHEN`), and a coordinated-change guard at module load throws if a vocabulary entry has no predicate. Compiled from `src/section-manifest.cts` |
| `semver-compare.cjs` | Shared semver comparison policy helpers (`compareSemverCore`, stable-triplet validation, normalized tuple parsing) consumed by update-check hooks, statusline dev-install detection, and changeset extract range logic (#10) |
| `security.cjs` | Path traversal prevention, prompt injection detection, safe JSON/shell helpers |
| `shell-command-projection.cjs` | Runtime-aware shell command projection for managed hook serialization: decides PowerShell call-operator usage by runtime/platform and normalizes Windows script path tokens |