* enhance(#4402): split plan-phase into a spine + detail, add the shared compact-content gate
ADR-4139 Decisions 3-5, Phase 2 of the #4139 Compact Content epic. Pilot
split for plan-phase.md, the largest of the 58 eagerly-@-included workflow
files (98,290 bytes): the spine keeps every happy-path step, every
protected-content block (planner/checker prompt templates, quality gates,
the failing-direction few-shot example, the two ScheduleWakeup guardrail
paragraphs — each marked with a <!-- gsd:protected --> sentinel), and
condensed one-paragraph summaries of five rare/opt-in fallback paths
(planner and checker filesystem-hang recovery, phase-split recommendation,
source-audit gaps, the thinking-partner conditional, and plan bounce). The
full text of those five moves verbatim to gsd-core/workflows/plan-phase/detail.md
(9.9KB, well under the 32,768-byte NEW_FILE_CAP), read by the spine only
when workflow.compact_content is false (the default) — the exact same
resolution rule now stated once in the new shared
gsd-core/references/compact-content-gate.md, which every future split
references instead of restating.
Verified mechanically (tests/plan-phase-compact-split.test.cjs, scoped to
this one split — Phase 3/#4403 owns the generalized guard): the union of
spine + detail contains every non-trivial line the parent commit carried
(0 missing), no non-trivial line is duplicated between them (0 duplicated),
and every declared protected block is well-formed and non-empty. The spine
shrinks from 98,290 to 93,206 bytes (-5.2% of the eager-window cost this
epic exists to reduce); detail.md's 9,853 bytes are only ever paid by a
project that has NOT opted in.
Verified live, end to end, twice, against this actual repo (not a
synthetic fixture) — real gsd-planner and gsd-plan-checker subagent
spawns, real PLAN.md output:
- workflow.compact_content=false: planned a real disposable phase
(a docs/how-to page for enabling the key itself); planner returned
PLANNING COMPLETE, checker returned VERIFICATION PASSED, all fact-checks
against real repo state confirmed.
- workflow.compact_content=true (detail.md never read): planned a second
real disposable phase; planner returned PLANNING COMPLETE with
frontmatter.validate and verify.plan-structure both clean, again fully
grounded against real repo state. The five condensed fallback sections
were independently re-read spine-only and confirmed sufficient to act on
correctly without detail.md's elaboration.
Also drafts gsd-core/references/compact-content-protected-content.md — the
protected-content category list and <!-- gsd:protected --> sentinel syntax
ADR-4139 Decision 5 calls for, written to move to Phase 3 (#4403) unchanged
once it lands there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4402): move detail.md into the ADR-4139-mandated detail/ subdirectory
Two independent review sub-agents (Standards and Spec axes of /code-review)
caught the same structural defect: ADR-4139 Decision 6 mandates
gsd-core/workflows/<name>/detail/*.md ("one or more parts... individually
skippable"), and this PR had shipped a flat plan-phase/detail.md instead,
copying issue #4402's own (inconsistent) restatement rather than the
locked ADR text. Fixed by git-mv to plan-phase/detail/elaboration.md and
updating every cross-reference (the spine's step 0.5 gate pointer, the
shared compact-content-gate.md's own resolution-rule wording, and the
completeness test's path constants).
Also, from the same review pass:
- docs/CONFIGURATION.md and gsd-core/references/planning-config.md's
workflow.compact_content rows said "nothing branches on it yet" — no
longer true now that plan-phase.md's spine does. Updated both to name
plan-phase as the pilot and note the rest of the corpus is still pending.
- Regenerated all 19 tests/fixtures/install-tree/*.json golden fixtures
(npm run gen:install-tree) — the three new shipped files were missing
from the installer emitted-tree goldens.
- Found via a cache-busted `eslint . --max-warnings 0` (this repo's
eslint --cache has produced false-greens before): the split test's
`git show` call had a bare `timeout: 10000` literal, tripping
local/no-adhoc-timeout-literal. Extracted to the existing GIT_TIMEOUT_MS
constant from tests/helpers/timeouts.cjs instead of a second guessed
copy of the same class of timeout.
Verified NOT needed, by tracing the actual mechanism rather than asserting
(tests/helpers/emitted-provenance.cjs's gsd-core-verbatim rule attributes
every gsd-core/{workflows,references}/** path to itself as an identity
source): an Emitted-Drift-Ack-Hash/-Growth trailer. Every changed/added
path in this diff is hand-authored and present in the diff itself, so
diffEmitted's attribution loop resolves `via` to the path's own source
before ever reaching the ack-lookup branch — there is no unattributed
delta to acknowledge. The spine also shrank (98,290 to 93,206 bytes), so
the growth ratchet has nothing to ack either.
Re-verified after these changes: the completeness/disjointness self-check
(0 missing, 0 duplicated) still holds against the relocated detail file,
and a full `npm run lint:ci` passes clean with the eslint cache cleared.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4402): restore literal content the pre-existing drift guards pin on
The first gsd-test run against this split (19 failures) surfaced real
regressions: several pre-existing structural guards pin the EXACT text
of the sections this split condensed, and paraphrasing broke them.
- tests/plan-phase-drift-guard.test.cjs expects the literal
`DISK_PLANS=$(gsd_run query find-phase ...)` bash assignment inside
plan-phase.md itself, not a prose description of the same check.
Restored the exact line into both §9a and §11a's spine summaries.
- tests/thinking-partner.test.cjs expects plan-phase.md to literally
offer "No, I'll decide" as the skip option. Restored that exact
phrase into the condensed thinking-partner paragraph.
- Both restores would have duplicated the same text into
plan-phase/detail/elaboration.md (which still carries the full
elaboration). Removed the now-redundant restatements from the
detail file instead of leaving them duplicated — the spine already
computes DISK_PLANS before the detail elaboration is ever read, so
the detail file references it rather than recomputing it.
- Re-running scripts/sync-runtime-launcher.cjs after that edit found
the canonical gsd_run preamble had also become an unintentional
spine/detail duplicate (both files call gsd_run and each is
required, by runtime-launcher-parity's own contract, to carry its
own copy). That's sanctioned duplication under a DIFFERENT
contract, not lost/copy-pasted content, so
tests/plan-phase-compact-split.test.cjs now excludes it from the
disjointness check the same way it already excludes trivial
fences/headings.
- Applied the adversarial-review finding on tests/plan-phase-compact-split.test.cjs's
own isTrivial(): a blanket `line.length <= 15` cutoff silently
swallowed real content (e.g. the 14-char `<quality_gate>`
sentinel). Replaced it with a specific bare-label-line pattern
(`Options:`, `Display banner:` etc.) — verified 0 missing / 0
duplicated against the actual split, an improvement over both the
original cutoff and a naive full removal (which produces
false-positive "duplicates" on generic recurring labels).
- gsd-core/references/planning-config.md's own workflow.compact_content
row used `/gsd-plan-phase` (hyphen). That file is Claude-facing
source text (gsd-core/references/), which tests/slash-command-namespace.test.cjs
requires in colon form; docs/CONFIGURATION.md's use of the hyphen
form is correct as-is since docs/ is human-facing and outside that
test's scanned directories. Fixed to `/gsd:plan-phase`.
- tests/plan-phase-compact-split.test.cjs's own `git show` of the
parent commit failed inside the gsd-test sandbox ("detected dubious
ownership") because the checkout is mounted under a UID the
invoking user doesn't own. Scoped `-c safe.directory=<repo-root>`
to that one git invocation rather than touching global git config.
- docs/INVENTORY.md still had one outstanding "detail.md part" wording
fix from the earlier adversarial-review pass, staged now.
Re-verified locally against the exact assertions in all four affected
test files (all pass) before dispatching a fresh gsd-test run — no
change here should have broken any of the other 18 gates; `npm run
lint` is clean with the eslint cache cleared.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(#4402): restore the full marker enumeration to §9a's spine trigger line
The isolated Spec-axis review flagged that §9a's "Triggered when" line was
condensed to "Agent() returns but the return contains no recognized
marker" — dropping the literal `## PLANNING COMPLETE` / `## PHASE SPLIT
RECOMMENDED` / `## ⚠ Source Audit` / `## CHECKPOINT REACHED` /
`## PLANNING INCONCLUSIVE` enumeration, which is exactly the "machine-
parsed structural headings" category compact-content-protected-content.md
lists as protected. The load-bearing use of that same list (the
gsd_stall_watch call and the Handle Planner Return bullets a few lines
above) was never touched — only this one descriptive restatement was
genericized — but leaving any instance of a protected category
unsentineled is the silent erosion ADR-4139 Decision 4(c) warns
sufficiency isn't machine-checkable enough to catch on its own. Restored
the full enumeration into the spine.
That reintroduced an exact duplicate into plan-phase/detail/elaboration.md,
which still stated the same trigger sentence verbatim. Reworded the
detail file's version to reference the spine's trigger condition instead
of restating it, since the spine is now the single place that sentence
lives in full — mirroring the DISK_PLANS/"already computed above" pattern
from the previous commit.
Re-verified locally: completeness/disjointness (0 missing, 0 duplicated)
and all previously-fixed literal-content assertions still hold.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* docs(#4402): backfill changeset pr number to 4471
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>