* feat(#2995): extend fragment emission to agents/ across every read point Epic #1671 Phase 6.4. `composeWorkflow` stripped `<!-- gsd:section -->` markers only for `gsd-core/workflows/`, so a marked agent shipped its markers verbatim into every runtime — and agent text is loaded into a subagent's context on every dispatch. The issue proposed widening the `copyWithPathReplacement` guard. That is a no-op for agents: agents never traverse that function. Agent content is read for emission at five independent points, and the obvious chokepoint `stageAgentsForProfile` short-circuits on the DEFAULT `full` profile (`skills === '*'` returns the real unstaged directory), so a hook placed there is dead code on most installs. Composition now happens at two call sites instead of five parallel surfaces: `stageAgentsForRuntimeWithConverter` (with `agentsKind` and `kimiAgentsKind` routed through it via an identity converter) and the inline agent loop in bin/install.js. Both compose BEFORE any path rewrite, so a `.claude/` -> `.windsurf/` regex can never reach inside a marker attribute — the ordering #2930 established for workflows. `installCodexConfig` was the fifth read point: Codex embeds each agent's prompt into a per-agent `.toml` via its own readFileSync. Call-graph analysis missed it; the exhaustive per-runtime emission sweep found it. That is why the new guard is behavioral rather than structural — a sixth read point fails the sweep without anyone remembering to extend a list. tests/agent-fragments-emission.install.test.cjs spawns a real installer for every runtime at every agent-bearing scope, derived from RUNTIME_META and the capability registry at run time so a new runtime cannot be silently under-covered. It asserts markers are absent AND the `when="always"` body is retained, so marker-absence cannot be satisfied by dropping content. An identity-composer negative control proves the assertion can fail. Verified: 0 install failures, 0 marker leaks, body retained on 27 runtime/scope paths; red before the wiring on claude(global+local), zcode(global+local), kimi, codex and opencode. Refs #2995 * chore(#2995): give the tightest agents headroom and correct the design lock Epic #1671 Phase 6.4, second half. `agents/gsd-verifier.md` had 12 bytes of headroom under its 49,152-byte LARGE cap and `agents/gsd-debugger.md` had 147 under its 57,344-byte XL cap. Both now extract reference material to `gsd-core/references/` behind an @-reference — the documented DEFECT.AGENT-FILE-SIZE-CAP-BREACH remedy: gsd-verifier 49,140 -> 46,371 B headroom 12 -> 2,781 gsd-debugger 57,197 -> 48,851 B headroom 147 -> 8,493 Byte accounting proves no content was lost: the combined agent+reference delta is exactly the new files' headers plus the agents' slim replacement blocks. Each agent keeps its routing table and a one-line summary per entry, so it degrades gracefully on a runtime that does not inline @-references. `agents/gsd-planner.md` is untouched and still passes both char guards (49,130 < 49,152); it needed no change, so it took none. The other nine LARGE/XL agents carry NO gsd:section markers, and that is deliberate, not deferred. `when=` selection is read from gsd-core/workflows/section-manifest.json, which gen-section-manifest.cjs derives from gsd-core/workflows/*.md only — shape `{workflows: ...}`, no per-agent key, no per-agent init entry point. An agent atom therefore fails admission gate (2) ("a fact the init seam demonstrably computes at a real entry point") and would evaluate false forever while looking like working gating. Marking agents would manufacture exactly the silent-inertness rot the frozen vocabulary exists to prevent. ADR-1671 gains three amendments, two of which close gaps /adr-phase-coverage found against what actually merged: - The 19 -> 29 vocabulary widening shipped in #2994 with no coordinated ADR amendment, which that bullet's own rule forbids. Recorded now. - `flag:--verify-only` was one of six atoms #2992 withheld and deferred to "the LARGE/XL rollout phase". Five shipped; this one is permanently rejected, and that disposition lived only in a merged PR body. - Phase 6.4's own finding: emission extends to agents/, gating does not. CONTEXT.md's glossary was stale on both seams — Workflow Fragments Module still listed the original 4-atom vocabulary and described when= as "not yet acted on", and Section Manifest Module still described InvocationFacts as {waveFlag, phaseNumber, hasPriorPhases}. Both now match the shipped contract. Inventory manifest regenerated AFTER build:lib per the documented ordering landmine; 19 install-tree fixtures pick up the two new references. Refs #2995 * chore(#2995): correct the compose-site count and mark the raw stager Self-review found two comment defects in the prior commit. The agentsKind comment claimed composition lands at TWO call sites; it is three, since installCodexConfig's per-agent .toml writer was added after that comment was written. And stageAgentsForProfile is now production-dead — both callers route through the composing stager — while staying exported and unit-tested, which makes it a trap: it does a raw copyFileSync and short-circuits to the unstaged source directory under the default profile, so a future caller would silently reintroduce the marker-shipping path. Its JSDoc now says so. * test(#2995): guard the marker-documenting-doc class for agents Widening the composer's scope to agents/ makes reachable the exact class #2930 narrowed scope to avoid: a file that DOCUMENTS the marker syntax with an unfenced example is indistinguishable from a real marker, so the composer drops that line from the emitted artifact. Three rows. A fenced example must compose byte-identically. No shipped agent may carry a marker outside a fence — asserted by parsing every real agent and requiring zero explicit sections, which is what makes the fence protection load-bearing rather than decorative. And a non-vacuity row asserts an UNFENCED marker IS parsed as a real marker, so if that ever stops being true the second row is guarding nothing. Also applies two review findings: stageAgentsForProfile's new JSDoc claimed it had no production caller, which is false — bin/install.js's _stageAgents still calls it, and its consumers compose before writing. Corrected to state the invariant instead. And a let/const nit in the emission sweep. * fix(#2995): keep verifier status vocabulary in the agent, fix a wrong fixture The first remote run came back red with three failures. Both root causes were mine. 1. tests/agent-frontmatter.test.cjs requires agents/gsd-verifier.md to literally contain HOLLOW and DISCONNECTED. The Step 4b extraction moved that status vocabulary into gsd-core/references/verifier-wiring-patterns.md, so the agent no longer had it. Byte accounting said no content was lost, and byte-wise that was true — but a contract required those tokens to live IN THE AGENT. That is ADR-1671:66's flexReserve floor stated concretely: a load-bearing fragment must not be trimmed out of its host, and "the bytes still exist somewhere" is not the test. The two status tables are restored to the agent and deliberately mirrored in the reference with a note saying so, so the procedure there still reads standalone. gsd-verifier lands at 47,069 B — headroom 12 -> 2,083, rather than the 2,781 the first attempt claimed. 2. Row 12b of the new marker-documentation guard asserted that an unfenced marker example parses as a real marker, and threw instead: "unmatched /gsd:section close marker". The grammar is WHOLE-LINE only. The fixture had put the OPEN marker inline mid-sentence, so it was correctly not recognised as an open while the close, on its own line, was. That is a real refinement of the hazard this guard exists for: only a marker on its OWN line is mis-parsed — which is exactly how a documentation example is normally written. Row 12b now uses a whole-line marker, and a new row 12c pins the inline case as explicitly NOT a marker. No test was weakened to accommodate the change; the change was corrected to satisfy the tests. Refs #2995 * chore(#2995): backfill changeset pr number to 3058 --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -223,6 +223,7 @@
|
||||
"debugger-repro-hardening.md",
|
||||
"debugger-sbfl.md",
|
||||
"debugger-semantic-recall.md",
|
||||
"debugger-techniques.md",
|
||||
"decimal-phase-calculation.md",
|
||||
"doc-conflict-engine.md",
|
||||
"domain-probes.md",
|
||||
@@ -297,6 +298,7 @@
|
||||
"user-story-template.md",
|
||||
"verification-overrides.md",
|
||||
"verification-patterns.md",
|
||||
"verifier-wiring-patterns.md",
|
||||
"verify-mvp-mode.md",
|
||||
"workstream-flag.md",
|
||||
"worktree-branch-check.md",
|
||||
|
||||
@@ -301,6 +301,8 @@ Full roster at `gsd-core/references/*.md`. References are shared knowledge docum
|
||||
| `debugger-repro-hardening.md` | Regression-test hardening (PBT shrinking + oracle classification + boundary neighbors) loaded by `gsd-debugger`. |
|
||||
| `debugger-prevention.md` | Prevention / blameless-postmortem output (5-Whys + why-not-caught + recurrence guard) loaded by `gsd-debugger`. |
|
||||
| `debugger-semantic-recall.md` | Semantic knowledge-base recall via MemPalace (keyword-fallback) loaded by `gsd-debugger`. |
|
||||
| `debugger-techniques.md` | Full step-by-step bodies for the 10 debugging techniques (binary search, delta debugging, git bisect, …) routed by `gsd-debugger`'s technique-selection table. |
|
||||
| `verifier-wiring-patterns.md` | Data-flow trace procedure and the four wiring patterns (Component→API, API→Database, Form→Handler, State→Render) loaded by `gsd-verifier`. |
|
||||
| `mandatory-initial-read.md` | Shared required-reading boilerplate injected into agent prompts. |
|
||||
| `agent-skills-bootstrap.md` | Shared agent_skills self-load contract (query + Read + dedup guard) injected into all 22 consumer agents. |
|
||||
| `project-skills-discovery.md` | Shared project-skills-discovery boilerplate injected into agent prompts. |
|
||||
|
||||
@@ -142,6 +142,50 @@ Pure Agent Skills (A alone) and pure MCP (D alone) were rejected as the foundati
|
||||
the "Rejected" cases (`--auto`/`--chain`/persisted-config interleaving;
|
||||
negated `--skip-bounce` OR `--gaps` OR NOT(...)) recorded in
|
||||
`.gsd/phase/chore-2993-fragmentize-plan-phase/40-design.md`.
|
||||
|
||||
**Amended by #2994 (Phase 6.3) — the vocabulary widens 19 → 29, third coordinated
|
||||
amendment.** Rolling the fragment model onto the remaining 13 LARGE/XL workflows
|
||||
surfaced 10 more atoms, admitted under the same two gates #2992 established. This
|
||||
amendment is recorded retroactively by #2995 (Phase 6.4): #2994 shipped the atoms
|
||||
without it, which this bullet's own rule forbids ("Widening the vocabulary requires a
|
||||
coordinated ADR amendment, not an organic edit"). The gap was found by re-running
|
||||
`/adr-phase-coverage` against what actually merged. The atoms each satisfy both
|
||||
admission gates and are not in question; the missing record is.
|
||||
|
||||
**Shipped (10).** `flag:--fix`, `state:auto-advance-active`, `state:fallow-enabled`,
|
||||
`state:flat-mode`, `state:git-create-tag`, `state:is-monorepo`, `state:next-channel`,
|
||||
`state:plan-strategy-converge`, `state:reviewer-instances-configured`,
|
||||
`state:ui-phase-active`, `state:workstream-active`. Compound real-world triggers
|
||||
(`--converge OR --cross-ai`, `--next OR --rc`, `--auto OR` config, `--discuss OR
|
||||
--full`) are each resolved to a single boolean in `src/init.cts` before evaluation, so
|
||||
`when=` still sees one operator-free atom — the `state:chunked-mode` precedent above.
|
||||
`state:flat-mode` is the positively-phrased inverse of `state:workstream-active`,
|
||||
because negation is not in the grammar.
|
||||
|
||||
**`flag:--verify-only` is permanently REJECTED, not pending.** #2992 listed it among
|
||||
six withheld atoms and deferred all six to "the LARGE/XL rollout phase". Five shipped
|
||||
in #2994. `flag:--verify-only` did not, and will not: `docs-update`'s control flow is
|
||||
interleaved across three non-contiguous touch-points, so gating one would leave the
|
||||
other two as raw `$ARGUMENTS` checks. An atom with no genuine consuming section is dead
|
||||
vocabulary — the rot the frozen list exists to prevent. That disposition was recorded
|
||||
only in merged PR #3030's body, leaving this ADR still asserting a hand-off that will
|
||||
never complete; it is recorded here so the withheld list reaches a terminal state.
|
||||
|
||||
**Amended by #2995 (Phase 6.4) — the grammar does NOT extend to `agents/`.**
|
||||
Migration step 7 names agents alongside workflows. Emission does extend: agent bodies
|
||||
now pass through `composeWorkflow` on every emission path, so a marker in an agent is
|
||||
stripped rather than shipped verbatim. **Gating does not.** `when=` selection is
|
||||
consumed from the committed `gsd-core/workflows/section-manifest.json`, which
|
||||
`scripts/gen-section-manifest.cjs` derives from `gsd-core/workflows/*.md` only; its
|
||||
shape is `{workflows: {...}}` and there is no per-agent entry, no per-agent init entry
|
||||
point, and no consumer that could evaluate an agent's `when=`. An agent atom therefore
|
||||
fails admission gate (2) — "a fact the init seam demonstrably computes at a real entry
|
||||
point" — and would be the exact silent-inertness failure that gate exists to prevent: a
|
||||
marker that looks like working gating while evaluating `false` forever. Agents are
|
||||
consequently size-managed by extraction to `gsd-core/references/` (the documented
|
||||
`DEFECT.AGENT-FILE-SIZE-CAP-BREACH` fix-forward), not by `when=` markers. Extending
|
||||
gating to agents would require a per-agent manifest family and a dispatch-time seam to
|
||||
read it; that is a separate decision, not an organic edit, and is not taken here.
|
||||
- **Budget unit:** bytes for emission caps (matches `lfByteCount`, deterministic, offline-safe); a token estimate for run-time selection.
|
||||
|
||||
**Corrected by #2931 (Phase 4) — the Windsurf cap was never load-bearing.** The Context
|
||||
|
||||
@@ -253,6 +253,35 @@ Composition runs **before** the per-runtime converters (the `.claude/` →
|
||||
`.windsurf/`-style path and reference rewrites), so a marker's `id`/`when`
|
||||
attribute text is never exposed to a rewrite regex.
|
||||
|
||||
### Emission covers `agents/` too — gating does not
|
||||
|
||||
Since epic #1671 Phase 6.4 (#2995), agent definitions under `agents/` pass
|
||||
through the same composition step as workflows. A marker in an agent file is
|
||||
stripped at emit rather than shipped verbatim into the runtime, on every path
|
||||
that emits agent content:
|
||||
|
||||
| Emission path | Runtimes |
|
||||
|---|---|
|
||||
| `stageAgentsForRuntimeWithConverter` (with the raw `agents` kind and the Kimi agent kind routed through it) | the descriptor-driven runtimes, plus `claude` local and `zcode` |
|
||||
| `bin/install.js`'s inline agent loop | every non-descriptor runtime |
|
||||
| `installCodexConfig`'s per-agent `.toml` writer | `codex` |
|
||||
|
||||
All three compose **before** any path rewrite, for the same reason workflows do.
|
||||
|
||||
**What does not extend is `when=` gating.** Selection is read from
|
||||
`gsd-core/workflows/section-manifest.json`, which `gen-section-manifest.cjs`
|
||||
derives from `gsd-core/workflows/*.md` only — its shape is `{workflows: …}` and
|
||||
it has no per-agent key. There is no per-agent init entry point either, so an
|
||||
agent atom has no fact to evaluate against and would fail the vocabulary's
|
||||
second admission gate ("a fact the init seam demonstrably computes at a real
|
||||
entry point"). A `when=` on an agent section would therefore evaluate `false`
|
||||
forever while *looking* like working gating — the precise failure the frozen
|
||||
vocabulary exists to prevent.
|
||||
|
||||
Agents that need to shed bytes do so by extracting reference material to
|
||||
`gsd-core/references/` behind an `@`-reference, the documented
|
||||
`DEFECT.AGENT-FILE-SIZE-CAP-BREACH` remedy — not by adding markers.
|
||||
|
||||
## Fenced and commented lookalikes are literal
|
||||
|
||||
A `<!-- gsd:section ... -->`-shaped line inside a fenced code block (three or
|
||||
|
||||
Reference in New Issue
Block a user