enhance(#4139): Phase 8 — the toggle becomes discoverable, and the ledger closes (#4587)

* enhance(#4139): Phase 8 — the toggle becomes discoverable, and the ledger closes

ADR-4139's final phase. workflow.compact_content already defaulted to false
(Phase 1's buildNewProjectConfig hardcoded default), but nothing surfaced it:
/gsd-new-project never asked, and /gsd-settings/config had no toggle path for
an already-initialized project — config-set/config-get were the only route.

new-project.md gains a fourth question in the existing Round 2 AskUserQuestion
array (grouped with the other general-workflow-behavior toggles, not the
per-agent capability questions above it) and threads compact_content into the
config-new-project CLI JSON literal. settings.md mirrors the exact pattern
every other non-capability workflow.* key already follows: read_current bullet,
question block, update_config write, the safe-merge non-capability-keys list,
save_as_defaults, and the confirm summary table — seven edits, zero new
src/*.cts code, since Phase 1's merge logic is a generic passthrough. Its
success_criteria question-count ("24 settings") is bumped to 25 to match the
now-25-entry main AskUserQuestion batch.

settings-advanced.md deliberately does NOT get a duplicate question: no other
boolean toggle in this repo is asked in both settings.md and
settings-advanced.md, and there's no reason to start with this one.

docs/CONFIGURATION.md, docs/USER-GUIDE.md, and a new docs/features/4139-compact-
content.md fragment (regenerated into docs/FEATURES.md) document the toggle.

ADR-4139 itself: Status flips Proposed -> Accepted, the acceptance-criteria
section becomes a guard ledger — a 13-row table covering all 12 of #4139's
original checkboxes plus the shipped-content guard criterion, each with real
evidence (the merged PR that satisfied it, fetched via `gh issue view
--json closedByPullRequestsReferences` rather than asserted from phase
numbers) — and both "Open questions for the implementation phases" are
resolved rather than left dangling: discuss-phase was never converted to
spine+detail shape (verified: no detail/ subdir exists) — a genuine gap, not a
reasoned decline; the disjointness check is confirmed line-based by reading
compact-content-split.cjs's normalizeNonTrivialLines directly.

Orthogonal review (isolated Standards/Spec code-review + security-review
sub-agents) found and this fixes two real defects: the changeset fragment's
body didn't match CONTRIBUTING.md's single em-dash-sentence format (was
multi-sentence prose naming implementation file paths); and settings.md's own
success_criteria still said "24 settings" after the new question pushed the
main batch to 25. Also fixed, found by the Spec pass while confirming
commands/gsd/settings.md correctly needed no sync edit: that file and its
skills/gsd-settings/SKILL.md twin both still described "Interactive 5-question
prompt (model, research, plan_check, verifier, branching)", stale since long
before this phase (the batch has had far more than 5 questions for a while) —
replaced with a description that names the current set without hardcoding a
count that will drift again.

gsd-test (real run, sha 1da78fe2) caught a third real regression the local
sweep missed: new-project.md is a registered spine+detail split for Phase 4's
token-reduction benchmark (scripts/benchmark-compact-content.cjs), and the new
question's +167 tokens drifted the committed baseline
(tests/fixtures/compact-content-benchmark-baseline.json). The benchmark itself
is designed never to fail CI on drift, but the test asserting the COMMITTED
baseline is currently non-drifted correctly caught it. Regenerated via
`node scripts/benchmark-compact-content.cjs --write`; re-verified --check now
reports "up to date" and the test file passes 27/27.

Closes #4408.
Closes #4139.

Emitted-Drift-Ack-Growth: new-project.md — new 4th Round-2 AskUserQuestion entry (Compact Content, #4139) plus the config-new-project CLI JSON field and explanatory sentence; a new opt-in toggle needs new prose.
Emitted-Drift-Ack-Growth: settings.md — new workflow.compact_content read_current bullet, question block, update_config write, safe-merge key, save_as_defaults field, and confirm summary row (the same seven-edit pattern every other non-capability workflow.* toggle already follows), plus the 24->25 success_criteria count fix found in review.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(#4408): backfill changeset PR number

pr:0 -> pr:4587 now that gh pr create has returned the real number.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-09-10 00:04:51 -04:00
committed by GitHub
parent 9770258558
commit 2cefa5a5ac
12 changed files with 238 additions and 58 deletions

View File

@@ -2,7 +2,7 @@
| | |
|---|---|
| **Status** | Proposed |
| **Status** | Accepted |
| **Date** | 2026-09-06 |
| **Issue** | [#4139](https://github.com/open-gsd/gsd-core/issues/4139) |
| **Phase-0 sub-issue** | [#4400](https://github.com/open-gsd/gsd-core/issues/4400) |
@@ -425,40 +425,70 @@ expanded today because the file is `@`-included. They **stay in the spine** and
one into a detail part would silently turn a working import into dead text, which is exactly the
class of failure the #3324 sub-guard catches.
### 7. Acceptance criteria — satisfied, and reconciled
### 7. Acceptance criteria — the guard ledger
`CI.GATE.acceptance-criteria-required` treats an unmet must-have as a failed deployment, so the
criteria are reconciled here explicitly rather than reinterpreted quietly at ship time.
criteria are reconciled here explicitly rather than reinterpreted quietly at ship time. This
section was written as a forward-looking reconciliation when only Phase 0 existed; #4408 (the
closing phase) converts it into a ledger against #4139's own 12 acceptance-criteria checkboxes,
quoted verbatim, each with the evidence that closed it. "Satisfied" means literally true;
"Reconciled" means the criterion's *purpose* was met by a structurally different mechanism than
the one the criterion's wording assumed, argued below.
**Satisfied as written**, by the phase named: config key registration and validation (#4401);
`/gsd-new-project` question and `/gsd-settings` + `/gsd-config` toggles (#4408); gate logic in
exactly one file (#4402); corpus coverage with no unpaired canonical file (#4405, #4406, #4407);
`agent-skills` serving the compact payload (#4407); golfed spawn patterns (#4405, carried inside
the workflow splits); identical behavior under global and local installs (structural — the key is
per-project and no file is selected at install time); the compression-rules document and its
denylist (#4403); the drift check naming a stale pair (#4403, in its partition form — see below);
the offline reporting-only benchmark with a committed baseline (#4404); the full suite green with
the key on and off (#4408); all shipped-content guards passing (every phase).
| # | Criterion (verbatim from #4139) | Verdict | Evidence |
|---|---|---|---|
| 1 | `workflow.prompt_golf` is registered in the config schema; `config-set`/`config-get` persist and read it, invalid value rejected | Satisfied — key renamed to `workflow.compact_content` (Decision 1) | #4401 → [PR #4441](https://github.com/open-gsd/gsd-core/pull/4441) |
| 2 | `/gsd-new-project` asks a Prompt Golf question and writes the answer | Satisfied | #4408 → this PR, `gsd-core/workflows/new-project.md` Round 2 |
| 3 | `/gsd-settings` and `/gsd-config` can toggle the key on an already-initialized project | Satisfied | #4408 → this PR, `gsd-core/workflows/settings.md` (`/gsd-config` routes here with no flag) |
| 4 | With golf on, workflows load golfed variants via a single shared gate reference file; gate logic exists in exactly one file | Satisfied | #4402 → [PR #4471](https://github.com/open-gsd/gsd-core/pull/4471), `gsd-core/references/compact-content-gate.md` |
| 5 | Every shipped workflow instruction file and planning-artifact template has a golfed variant registered with the parity check — no unpaired canonical file remains | Reconciled (#1 below) | #4402/#4403/#4405/#4406 |
| 6 | `gsd_run query agent-skills <agent>` returns the golfed payload for every agent when on, canonical when off | Reconciled (#2 below) | #4407 → [PR #4553](https://github.com/open-gsd/gsd-core/pull/4553) |
| 7 | Subagent prompts emitted by orchestrators follow golfed spawn patterns when golf is on | Satisfied — carried inside the workflow spine/detail splits, no separate mechanism needed | #4405 → [PR #4536](https://github.com/open-gsd/gsd-core/pull/4536) |
| 8 | Behavior identical under global and local installs — per-project config decides, never install-time file selection | Satisfied — structural: the config key is read at runtime from `.planning/config.json`; no phase added install-time file selection | Design invariant, unchanged across all 8 phases |
| 9 | A committed compression-rules document defines a protected-content denylist; no golfed variant weakens protected content | Satisfied | #4403 → [PR #4497](https://github.com/open-gsd/gsd-core/pull/4497), Decision 5 above |
| 10 | Editing a canonical file without updating its golfed variant fails the drift-parity check, naming the stale pair | Reconciled (#3 below) | #4403 → PR #4497 |
| 11 | The benchmark runs fully offline, reports per-file/aggregate reduction as a labeled proxy-tokenizer delta, reproduces its committed baseline deterministically, is reporting-only | Satisfied | #4404 → [PR #4502](https://github.com/open-gsd/gsd-core/pull/4502) |
| 12 | The full existing test suite passes with golf enabled AND with golf disabled | Satisfied | Every phase's `gsd-test` run covers the key off (default, unset); `tests/agent-skills.test.cjs`'s stream-2 fixtures (#4407) and Phase 2's live end-to-end dogfooding (#4402 — a real phase planned twice against this repo, once with the key false, once true) cover it on |
| 13 | All golfed and gate-bearing shipped content passes the existing shipped-content guards: size budgets, emitted-attribution, `commit-files-pathspec`, `commit-docs-bypass` | Satisfied — ongoing, every phase | Recorded per-phase in each PR's own review artifact; no phase shipped with a red shipped-content guard |
**Reconciled, with the reasoning:**
**Reconciled, with the reasoning** (numbered to match the ledger's "Reconciled (#N below)" cells):
1. *"With golf off they load canonical content"* — they do. The reconciliation is mechanical, not
semantic: the canonical content arrives as spine plus detail rather than as one eager blob. The
instruction set an orchestrator holds with the key off is the same instruction set it holds
today.
2. *"Editing a canonical file without updating its golfed variant fails the drift-parity check"* —
under Decision 5 there is no variant to go stale, so this criterion's *purpose* (a canonical edit
cannot silently leave a paired file behind) is met structurally rather than by a check. The check
that remains enforces the invariant that makes it structural: disjointness plus registration.
This is a stronger outcome than the criterion asked for, and #4403 must demonstrate it by proving
each check can actually fail before it is trusted.
3. *"With golf off, GSD behaves exactly as it does today"* (user story 3) — **not fully achievable,
by any design that delivers stream-1 savings.** Decision 4(a) states the residual: an opted-out
project's elaborations arrive by a runtime `Read` that could be missed, and a miss yields the
compact behavior. The deviation is bounded to a state the project deliberately supports and
validates in #4402. This is recorded as a knowing, argued deviation. It is the item on this list
a maintainer may reasonably want to overturn, and if it is overturned the consequence is
automatic: stream 1 becomes uncoverable and the epic reduces to streams 2 and 4.
1. *(criterion 5)* "No unpaired canonical file remains" reads as "every file has a golfed twin."
What shipped instead, deliberately: streams 1/1b/4 use a **partition** (spine + detail, or a
variant swap), not a duplicate-and-maintain pair, and the eagerly-`@`-included corpus was
individually reviewed — some files split (#4402/#4405/#4406), others were **recorded as not
worth splitting** (`docs/PARTITION-RULES.md`) because their size is safety-critical
orchestration logic rather than deferrable narrative, or because a split's fixed overhead
would exceed the savings. "No unpaired canonical file" is true of every file this epic
*covers*; it was never true, nor intended to be, of files the epic explicitly declined to
cover with a stated reason — which is the criterion's own spirit (a reviewed decision, not a
silent gap).
2. *(criterion 6)* "For every agent" reads as literally all 35. #4407 shipped 29 — six
(`gsd-debugger`, `gsd-executor`, `gsd-phase-researcher`, `gsd-plan-checker`, `gsd-planner`,
`gsd-verifier`) exceed the hard, non-ackable `NEW_FILE_CAP` even after aggressive compaction,
a structural constraint the seam's single-file read has no mechanism to route around without
building a second spine/detail-style partition mechanism scoped for agents — out of proportion
to #4407's own "the code delta is small" framing. The criterion's *purpose* — never fail or
serve nothing — holds for all 35: the `#2454` fallback discloses the substitution inside the
payload itself for the six uncovered agents, exactly the behavior criterion 6 and #4139's
Alternative-5 rejection ("golfing in place forfeits the side-by-side comparison") both protect
against losing.
3. *(criterion 10)* Under Decision 5's partition shape there is no variant to go stale — a
canonical edit and its elaboration are pieces of ONE document, not two. The criterion's
*purpose* (a canonical edit cannot silently leave a paired file behind) is met structurally
rather than by a drift check catching it after the fact. What #4403 built instead —
disjointness (no line duplicated) plus registration (no orphaned or dangling part) — is a
stronger invariant than "the parity check fails," and #4403's own failing-first fixtures prove
each check can actually fail before either was trusted.
**One item this ledger overturns rather than reconciles:** user story 3 ("with golf off, GSD
behaves exactly as it does today") is **not fully achievable by any design that delivers
stream-1 savings** — Decision 4(a) states the residual plainly: an opted-out project's
elaborations arrive by a runtime `Read` that could be missed, and a miss yields the compact
behavior rather than a crash. This was accepted knowingly at Phase 0 and validated in #4402's
live dogfooding rather than fixed, because the alternative (converting the `@`-includes) has an
unbounded failure mode instead of a bounded one (see §Context). It is recorded here, not buried,
as the one criterion whose letter was traded for its spirit.
## Consequences
@@ -507,22 +537,22 @@ the key on and off (#4408); all shipped-content guards passing (every phase).
## Phase plan
| Phase | Issue | Delivers | Depends on |
|---|---|---|---|
| 0 | [#4400](https://github.com/open-gsd/gsd-core/issues/4400) | this ADR | — |
| 1 | [#4401](https://github.com/open-gsd/gsd-core/issues/4401) | `workflow.compact_content` end to end | 0 |
| 2 | [#4402](https://github.com/open-gsd/gsd-core/issues/4402) | shared gate + pilot split + accuracy spot-check | 1 |
| 3 | [#4403](https://github.com/open-gsd/gsd-core/issues/4403) | partition rules + the five checks | 2 |
| 4 | [#4404](https://github.com/open-gsd/gsd-core/issues/4404) | offline benchmark + committed baseline | 3 |
| 5 | [#4405](https://github.com/open-gsd/gsd-core/issues/4405) | stream 1 corpus coverage (carries stream 3) | 3, 4 |
| 6 | [#4406](https://github.com/open-gsd/gsd-core/issues/4406) | stream 1b subdirectories + stream 4 templates | 5 |
| 7 | [#4407](https://github.com/open-gsd/gsd-core/issues/4407) | stream 2 agent-skill payloads via the CLI seam | 6 |
| 8 | [#4408](https://github.com/open-gsd/gsd-core/issues/4408) | user surfaces, docs, guard ledger — closes #4139 | 7 |
| Phase | Issue | Delivers | Depends on | PR |
|---|---|---|---|---|
| 0 | [#4400](https://github.com/open-gsd/gsd-core/issues/4400) | this ADR | — | — |
| 1 | [#4401](https://github.com/open-gsd/gsd-core/issues/4401) | `workflow.compact_content` end to end | 0 | [#4441](https://github.com/open-gsd/gsd-core/pull/4441) |
| 2 | [#4402](https://github.com/open-gsd/gsd-core/issues/4402) | shared gate + pilot split + accuracy spot-check | 1 | [#4471](https://github.com/open-gsd/gsd-core/pull/4471) |
| 3 | [#4403](https://github.com/open-gsd/gsd-core/issues/4403) | partition rules + the five checks | 2 | [#4497](https://github.com/open-gsd/gsd-core/pull/4497) |
| 4 | [#4404](https://github.com/open-gsd/gsd-core/issues/4404) | offline benchmark + committed baseline | 3 | [#4502](https://github.com/open-gsd/gsd-core/pull/4502) |
| 5 | [#4405](https://github.com/open-gsd/gsd-core/issues/4405) | stream 1 corpus coverage (carries stream 3) | 3, 4 | [#4536](https://github.com/open-gsd/gsd-core/pull/4536) |
| 6 | [#4406](https://github.com/open-gsd/gsd-core/issues/4406) | stream 1b subdirectories + stream 4 templates | 5 | [#4540](https://github.com/open-gsd/gsd-core/pull/4540) |
| 7 | [#4407](https://github.com/open-gsd/gsd-core/issues/4407) | stream 2 agent-skill payloads via the CLI seam | 6 | [#4553](https://github.com/open-gsd/gsd-core/pull/4553) |
| 8 | [#4408](https://github.com/open-gsd/gsd-core/issues/4408) | user surfaces, docs, guard ledger — closes #4139 | 7 | this PR |
Guards land before content proliferates (Phases 3 and 4 precede Phase 5), satisfying approval
condition 2. The pilot's end-to-end accuracy spot-check is Phase 2's, satisfying condition 3.
## Open questions for the implementation phases
## Open questions for the implementation phases — resolved at close
- Whether `discuss-phase` — the one command that already reaches its workflow by runtime `Read` —
should be brought onto the spine shape too. It is the sole existing instance of the substitutive
@@ -532,6 +562,14 @@ condition 2. The pilot's end-to-end accuracy spot-check is Phase 2's, satisfying
Giving it a spine would remove that residual entirely, and it is the one place in the tree where
this ADR's mechanism would be a strict safety improvement rather than a token trade. Scoped to
Phase 5 (#4405) to decide with the rest of stream 1 in view.
**Resolved: not converted.** `gsd-core/workflows/discuss-phase/` carries no `detail/`
subdirectory as of #4408; the prose-only mitigation quoted above is unchanged. This was not
revisited with an explicit for/against argument in #4405 — recorded here as a genuine gap in
the epic's own closure, not a reasoned decline. A future phase wanting the safety improvement
this question identifies starts from a clean slate, not from an argued rejection.
- Whether the disjointness check should compare normalized sentences rather than normalized lines.
Lines are cheaper and catch copy-paste; sentences catch reflowing. Decided in #4403 against real
splits rather than in the abstract here.
**Resolved: lines.** `tests/helpers/compact-content-split.cjs`'s `normalizeNonTrivialLines`
is the shipped comparison unit — confirmed against the real implementation, not asserted from
the plan.

View File

@@ -267,6 +267,7 @@ These govern the system as it stands. Cite these.
| [ADR-3626](3626-context-md-seam-claim-gate.md) | CONTEXT.md seam claims carry a checkable enforcement pointer | Accepted | — |
| [ADR-3660](3660-runtime-artifact-layout-module.md) | Runtime Artifact Layout Module owns per-runtime artifact placement | Accepted | [ADR-1239](1239-gsd-embeddable-orchestration-engine.md) |
| [ADR-3806](3806-review-dispositions-ledger.md) | Review Dispositions Ledger canonizes where and how reviews-mode records incorporate/defer decisions in PLAN.md | Accepted | — |
| [ADR-4139](4139-compact-content-seam.md) | The compact-content seam — shrink the eager window, never the guarantee | Accepted | — |
### Proposed
@@ -286,7 +287,6 @@ Decided in principle, not yet ratified. Do not cite as settled architecture.
| [ADR-3646](3646-per-task-content-resolution-seam.md) | Per-task external-tracker content-resolution seam | Proposed | — |
| [ADR-3889](3889-process-exit-contract.md) | One exit-code registry — 0 and 1 are free, everything else is allocated | Proposed | — |
| [ADR-3942](3942-emitted-drift-ack-commit-trailer.md) | The emitted-drift acknowledgment is PR-lifetime data — it belongs in a commit trailer, not the working tree | Proposed | — |
| [ADR-4139](4139-compact-content-seam.md) | The compact-content seam — shrink the eager window, never the guarantee | Proposed | — |
### Superseded, Retired, and Legacy