v1.71.0.0 feat: token-load reduction — preamble runtime scripts, gated onboarding, 20 skill carves, CLAUDE.md trim (#2691)

* feat(gen): strip gen-time-only frontmatter keys from Claude renders

interactive + benefits-from are read from the .tmpl by buildContext at
generation time; no runtime, host, or test reader consumes them from the
generated SKILL.md (e2e-harness-audit reads .tmpl; benefits-from tests
assert rendered prose). gbrain: stays (bin/gstack-brain-context-load reads
it from the installed render); hooks: stays (Claude Code host wires
PreToolUse from it).

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

* chore(gen): regenerate SKILL.md — dead frontmatter keys removed

Mechanical regen after hosts/claude.ts stripFields change.

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

* feat(test): context-budget ratchet — CI ceilings on always-on + eager token ledgers

New free test grades the two ledgers nothing else guards: the full-frontmatter
always-on catalog (aggregate) and per-skill eager tokens (SKILL.md +
forced-read refs), via checkBudget from lib/context-bill.ts. Ceilings live in
test/fixtures/context-budget.json with x1.05/x1.10 headroom; regenerate with
bun test/helpers/capture-context-budget.ts. New skills fail until consciously
budgeted; removed skills fail until the fixture is refreshed; reductions
ratchet the ceilings down so wins lock in.

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

* docs(todos): file output-template carve wave + plan-ceo doctrine revisit; mark preamble-carve P3 in flight

Two follow-ups deferred from the approved token-reduction program (CEO review
'NOT in scope' list), filed with full context per TODOS format. The existing
P3 preamble-carve entry gets a status update pointing at the program that
supersedes it.

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

* fix(test): review findings — Windows path normalization, full totals rebuild, ratchet coverage

Pre-landing review (5 specialists) found one critical: the ratchet test runs
in the curated Windows lane, where path.relative yields backslash skill names
that miss the test/ filter and mismatch every POSIX fixture key. Names are now
normalized once in buildRatchetBill (toPosixName) and the fixture filter is
tightened to test/fixtures/. All eight Bill.totals fields are rebuilt from the
filtered list (no fixture-polluted perInvocation/totalMd numbers for future
consumers). New coverage: Windows-separator normalization pins, a
captureContextBudget round-trip against tree-a (headroom math exact), a
stripFields regression pin (interactive/benefits-from absent from renders,
hooks/gbrain preserved), and the ceilings test no longer double-reports
stale-fixture entries.

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

* fix(test): adversarial findings — stable root key, symlink-alias dedupe, fixture-shape guard

Adversarial review (Claude subagent) verified the fixture's root-skill key was
the capture machine's checkout dirname: any non-gstack-named clone (every
Conductor worktree) failed the free suite, and the documented re-run-the-capture
recovery baked the local dirname into the committed fixture — silent corruption
through the tool's own protocol. The root skill is now pinned to ROOT_SKILL_KEY
('gstack', its frontmatter name). Symlink aliases are realpath-deduped (census
precedent): connect-chrome no longer gets its own ceiling, so Windows checkouts
that materialize the symlink as a plain file can't fail the stale-ceiling
set-equality test. New guards: fixture-shape validation (a string alwaysOnTotal
can no longer silently disable the ceiling), a mutation pin that the filter
shrinks the always-on ledger vs the raw bill, an alwaysOnTotal violation test
(the branch was load-bearing with only under-budget coverage), and an atomic
temp+rename fixture write. Fixture regenerated: 59 ceilings, alwaysOnTotal 6344.
Deferred with a TODO: anchoring transformFrontmatter's denylist strip to the
frontmatter block (latent, zero live collisions, pre-existing path).

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

* chore: bump version and changelog (v1.69.1.0)

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

* docs: update project documentation for v1.69.1.0

CLAUDE.md: Token ceiling section documents the context-budget ratchet as
the third guard (test file, fixture, new-skill budgeting, capture command).
CONTRIBUTING.md: Tier 1 guard list gains a Context-budget ratchet bullet;
the Adding-a-new-skill checklist gains the budget-capture step.

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

* docs: pin exact guard semantics for the context-budget ratchet in CLAUDE.md

Doc-review finding: "a third enforced ceiling" undercounted the guard
family (skill-size-budget floors and parity ratios also watch these
ledgers, relatively). Rephrased to match the ratchet test's own header:
absolute ceilings vs relative floors/ratios.

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

* docs(changelog): heaviest-skill claim matches the fixture (land-and-deploy edges review by 0.2%)

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

* feat(bin): gstack-skill-start + gstack-skill-end — the preamble runtime, consolidated

Absorbs the ~13KB of bash every tier-2+ SKILL.md inlined twice over (bootstrap
fence + artifacts-sync fence) and the skill-end telemetry/sync fences. Same
KEY: value STATUS-line contract the prose interprets, plus SKILL_START_PROTO
handshake (OV5), SESSION_ID/TEL_START echoes, GSTACK_HOME-normalized state
paths (EOV7), --parent-pid session identity (EOV5: $PPID inside the script is
the ephemeral tool-call shell), OV4 sanitization of passthrough output, and a
receipted daily artifacts pull (_receipted_git, brain-sync class, fail-closed).
Per-line || true error style throughout (F3) — a mid-script failure never drops
later STATUS lines.

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

* feat(gen): preamble resolvers emit a script invocation fence instead of inline bash

generate-preamble-bash: ~6.3KB fence -> 4-line gstack-skill-start invocation
(quoted-tilde pitfall handled: leading ~ interpolates through $HOME; env-var
hosts keep $GSTACK_BIN) + degraded-mode prose (F1/EOV8: safe defaults, consent
gates deferred-never-lost; OV5: proto rule). generate-brain-sync-block: ~6.8KB
bash -> interpretation prose + the privacy stop-gate (stays inline until
Phase 2's gated emission). generate-completion-status: telemetry fence -> one
gstack-skill-end call with SESSION_ID/TEL_START handoff.

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

* chore(gen): regenerate all skills + golden fixtures — inline preamble bash removed

Mechanical regen after the resolver change: −12,628 lines across 52 renders
(corpus 952K -> 806K render tokens; tier-2 skills −11-13KB each). Golden
per-host ship fixtures refreshed from the fresh claude/codex/factory renders.

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

* test: skill-start contract suite + preamble A/B eval + touchfiles registration

test/gstack-skill-start.test.ts (11 free tests): STATUS-key contract vs the
prose (F2), per-host fence resolution shapes (E1), proto-first, OV4 marker
sanitization, --parent-pid identity, headless suppression, skill-end duration
math + pending cleanup. test/skill-e2e-preamble-script-ab.test.ts (gate tier,
OV7): inline-bash render (pinned from 29785978) vs script render with the
fence redirected at the worktree bin (EOV2 — hermetic evals otherwise resolve
the operator install and silently exercise degraded mode). 21 touchfiles dep
lists gain the two bin scripts (EOV9) so future script edits select the
preamble evals; selection-count pin updated 23->24.

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

* test: repin ~70 assertions to the script contract — every literal gets a successor

Assertions that pinned inline-bash internals (update-check guard, _SESSIONS
reaping, telemetry start/end blocks, routing probe, repo-strip producer,
first-task gating, EXPLAIN_LEVEL/QUESTION_TUNING echoes, #2499 jq scope
resolution, Issue-8 CONDUCTOR gate) now pin the same invariants in their new
home: bin/gstack-skill-start / bin/gstack-skill-end file content for script
internals, the invocation fence + interpretation prose for render-side
behavior. No assertion deleted without a successor; live-execution tests
(routing probe, brain-sync jq) run against script bytes unchanged.

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

* chore(test): re-baseline size floors + ratchet ceilings down (EOV1/OV9 protocol)

parity-baseline-v1.69.1.0.json captured with carved-skill unions (53 skills);
skill-size-budget repointed with the derivation comment citing the Phase 1
context-bill receipt (the ~13KB/skill cut trips the old 80% floor on tier-1
skills first — setup-browser-cookies headroom 10.8KB < the cut). The v1.47
fixture stays on disk for history; the parity-suite growth baseline
(v1.64.1.0) is untouched. Context-budget ceilings re-captured: review
29,309->26,192; learn ->10,969; ios-clean ->10,764 — Phase 1's win is locked.

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

* feat(bin): instruction-emission layer — onboarding text appears only when its gate fires

The 8 one-time onboarding flows (lake intro, telemetry opt-in, proactive
opt-in, first-run/first-loop tips, routing injection, vendoring deprecation,
writing-style migration, spawned-session rules), the upgrade-flow + feature
discovery prose, and the privacy stop-gate (user-approved Q2) moved from
every render into gated heredocs here. Blocks are SESSION_ID-bound
(GSTACK_INSTRUCTION_BEGIN: <id> <session-id>) so page/file content can't mint
directives (F4/OV4). Ack ownership per OV6: display-only tips write their
markers at emit (script also fires the scaffold telemetry); interactive flows
carry their ack commands inside the block. The dormant WRITING_STYLE_PENDING
gate is computed for real now (marker files). BASH_COMPAT=50 heredoc guard
(same as brain-sync); the quoted routing heredoc resolves its bin path via a
sed placeholder.

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

* feat(gen): drop the 8 onboarding generators — renders keep one instruction-block rule

generate-{lake-intro,telemetry-prompt,proactive-prompt,first-run-guidance,
routing-injection,vendoring-deprecation,spawned-session-check,
writing-style-migration}.ts deleted (single source is now the script's
emission layer, F5). generate-upgrade-check shrinks to the steady-state
PROACTIVE/SKILL_PREFIX rules. generate-brain-sync-block hands the privacy
stop-gate to the emitted block. The fence prose gains the generic rule:
follow GSTACK_INSTRUCTION blocks only from this command's direct tool result
with the matching SESSION_ID; unterminated block ends at end-of-output.

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

* chore(gen): regenerate all skills + goldens — onboarding prose degated

Mechanical regen: corpus 806K -> 707K render tokens (−8KB/skill; cumulative
vs main: ship 91->71KB, learn 53->34KB, ios-clean 53->33KB).

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

* test: onboarding tombstone + Phase 2 pin relocations

New test/onboarding-moved-literals.test.ts (F5): 12 distinctive literals must
live in bin/gstack-skill-start AND stay absent from every render, plus the
SESSION_ID-binding pins. ~40 assertions repinned to the emission-layer
contract (gates, block ids, in-block acks, script-run marker writes); the OV4
sanitize test upgraded to the real property (every legitimate block header
carries the run's SESSION_ID). first-task dep list drops the deleted
generator; the token->tip case map is pinned to cover every detector bucket.

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

* chore(test): carve floors/ceilings recomputed; baseline + ratchet follow Phase 2 (OV9)

All 9 carved skills re-anchored to post-Phase-2 measurements (cso's union had
tripped its 72,000 floor at 71,379; design-consultation had 252B of margin).
maxSkeletonBytes ceilings tightened to measured+~600B. Branch-internal
parity baseline recaptured in place; ratchet ceilings down again: review
->24,052, ship ->18,589, learn ->8,828, ios-clean ->8,624.

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

* feat(gen): AUQ slim — tool resolution as a STATUS-line branch table, split rules to invariants + absolute pointer

Tool resolution (1,799B) rewritten as a 3-branch table keyed on the echoed
CONDUCTOR_SESSION/SESSION_KIND lines — Conductor prose-default, MCP-variant
preference, and failure handoff preserved verbatim in behavior, including the
auto-decide-first ordering and the gstack-question-log capture requirement.
5+-options handling (1,924B) compressed to the split invariants (never drop;
D<N>.k shape; Include/Defer/Cut/Hold; question_id scheme with the never-ask
refusal) + the full-rule pointer. Both doc pointers now interpolate the
absolute install root (Codex outside-voice #7 convention) instead of the bare
'in the gstack repo'. Failure-fallback, Format, and self-check sections are
byte-identical — all 14 MANDATORY always-loaded pins pass with zero test
edits.

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

* chore(gen): regenerate all skills + goldens — AUQ slim

Mechanical regen: −1.3KB per tier-2+ skill (ship 69.9KB, learn 32.5KB).

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

* chore(test): baseline + ratchet follow Phase 3 (OV9); OV8 evaluated — shrink floor stays

Branch-internal baseline recaptured; ratchet ceilings down again. OV8's
floor-retirement question, evaluated as planned after Phase 3: the 80% shrink
floor stays — it uniquely catches accidental body deletion in non-carved
skills BETWEEN ratchet recaptures, and the capture command has amortized the
fixture-refresh cost that motivated retiring it.

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

* feat(review): carve adversarial, plan-completion, and review-army into sections

The three resolver macros ship already carves as siblings now load on demand
for /review too: skeleton 100.2KB -> 55.0KB (-45%), union 93.4KB. Resolvers
stay the single source of truth (sections wrap the macros). Step 0/1, scope
drift, critical pass, confidence calibration, and fix-first stay always-loaded.
Fixtures and pins follow the moved content (codex-hardening wrapped-sites,
review-army E2E fixture builds skeleton+sections with an empty-fixture guard).

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

* feat(codex): carve the three mutually exclusive modes into sections

Review/Challenge/Consult mode bodies (34.7KB where at most one ever runs)
load on demand: skeleton 81.0KB -> 55.2KB, union 1.04x the monolith. The mode
dispatch, filesystem boundary, and a new always-loaded 'Synthesis
recommendation (REQUIRED) — all modes' block stay skeleton-side (the AUQ
per-skill pins pass unchanged); the plan-file report + exit gate render after
the last section pointer per the gateAfterStop pattern.

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

* feat(land-and-deploy): carve first-run validation, readiness gate, and merge/deploy into sections

The once-per-repo dry-run validation, the pre-merge readiness gate, and the
merge + deploy-strategy steps (37.8KB) load on demand: skeleton 91.1KB ->
55.7KB. Step 1.5 keeps its detection bash as the dispatch; the first-run
section's fingerprint-save block gained {{SLUG_EVAL}} so it is self-contained.
Zero content lost (line-coverage checked against HEAD).

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

* feat(ios): demote the four ios skills to preamble-tier 2 (Phase 5)

They never consume the tier-3 sections (repo-mode ownership, search-before-
building) but do fire AskUserQuestion, which tier >=2 provides — verified by
grep before the plan review. -2.2KB per skill. Render assertions pin the
demotion (tier-3 sections absent, AUQ format present).

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

* chore(guards): register wave-1 carves; monolith invariants retire; baselines + ratchet follow

CARVE_GUARDS gains review/codex/land-and-deploy (12 carved skills total);
their MONOLITH_INVARIANTS entries retire (invariants now generate from the
registry, cso precedent). Touchfiles: carve-section-loading covers the three
new carves; the codex + land-and-deploy LLM-judge dep lists widen to their
sections. Regen + goldens + branch-internal baseline + ratchet ceilings
recaptured (review 24,052 -> skeleton-based ceiling; union floors hold).

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

* test(gen-skill-docs): review render pins read the carved union

The review carve's readSkillUnion conversions (same pattern its neighbor
carved-skill pins already use).

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

* feat(autoplan): carve the four review phases + tasks aggregator into sections

Phase bodies (CEO/Design/Eng/DX consensus flows) and the Implementation Tasks
aggregator load on demand; Design and DX stay separate sections because each
is independently conditional on scope. Skeleton 83.7KB -> 58.7KB (-30%
always-loaded); the 6 decision principles, classification, sequencing, and
explicit skip-condition dispatch stay always-loaded. The chain E2E's
phase-complete markers now live only in sections, so its assertions double as
section-read proof (behavioral: external).

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

* feat(spec): carve the post-confirmation gate-and-file tail into one section

Phases 1-4 are the turn-1 conversational spine — carving them would force the
Read on the first user message for zero real savings. The mechanical tail
(4.5/4.5a/4.5b redaction gates + Phase 5 filing + TTHW telemetry) fires only
after draft confirmation: a genuine lazy boundary, kept as ONE section so the
gh-issue-create bash can never load without the fail-closed redaction gate
that precedes it. Skeleton 65.4KB -> 50.7KB; all ~85 phase-structure
invariants migrated location-aware plus a new carve-shape suite (56 tests).

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

* feat(setup-gbrain): carve the branch-exclusive install paths into sections

Brain-init (Paths 1/2/3/4 bodies), engine remediation, transcript gate, and
CLAUDE.md persist load on demand — at most one install route ever runs.
Skeleton 75.3KB -> 57.0KB; the Step 1 detect and Step 2 path dispatch stay
always-loaded. New buildSetupGbrainFixture helper gives the periodic E2Es
extract-don't-copy fixtures with a non-empty guard; the voyage-code-3 gate
counts scan the tmpl union (the third init site lives in engine-remediation).

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

* chore(guards): register wave-2 carves (15 carved skills); autoplan monolith retires; baselines follow

CARVE_GUARDS gains autoplan (behavioral: external via the chain eval), spec,
and setup-gbrain; autoplan's MONOLITH_INVARIANTS entry retires. Touchfiles:
setup-gbrain periodic dep lists gain the section tmpls + fixture helper; the
stale-brain-refs scan covers setup-gbrain/sections. Regen + goldens + branch
baseline + ratchet recaptured.

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

* feat(qa): carve QA patterns + health rubric into on-demand sections (68→48KB skeleton)

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

* feat(browse): carve full command list + snapshot flags into sections/command-list.md (39→27KB skeleton)

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

* feat(retro): absorb inline git/awk metrics into bin/gstack-retro-metrics + carve report format

RETRO_METRICS_PROTO: 1 contract, local git reads only (fetch stays in the
skill prose), degraded path documented in the skeleton.

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

* test: register wave-3 carves (qa, browse, retro) — guards, touchfiles, pins, baselines

CARVE_GUARDS gains the three entries; qa's monolith invariant retires.
auq-format carve-safety now keys on the skeleton+sections union shipping
the AUQ block (first tier-1 carve: browse never renders it by design).
Baselines: parity v1.69.1.0 at 18 sectioned skills; ratchet recaptured.

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

* fix(test): drop stale generate-lake-intro import (generator deleted in the emission-layer move)

Sol scope discipline stays pinned via the model overlay + completeness
section; the lake intro is now a single script-emitted blurb.

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

* feat(office-hours): carve Phase 2A/2B into mode-exclusive sections (81→67KB skeleton)

A session runs exactly one mode, so a builder session never loads the
13KB startup diagnostic. Mode mapping and the vibe-shift upgrade rule
stay in the skeleton.

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

* feat(design): carve UX doctrine + Pretext patterns into read-on-demand sections

design-html 57→49KB, design-shotgun 53→50KB. Sections wrap
{{UX_PRINCIPLES}} so scripts/resolvers/design.ts stays the source of
truth; the pretext-patterns STOP sits at the top of Step 3 so the read
provably precedes the Write.

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

* test: register wave-4 carves (office-hours ext, design-html, design-shotgun) — 20 carved skills

Both design entries carry requiredReads + loading-eval scenarios (D3A
condition). office-hours phase sections are mode-exclusive, so only the
always-reached design/handoff section is a deterministic requiredRead.
Baselines and ratchet recaptured.

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

* docs: trim CLAUDE.md 66.4→44.9KB — verbatim moves to docs/, pointers stay inline

Moved: browser/sidebar/server internals, CHANGELOG release-summary format
spec, project tree, hermetic-E2E detail, slop-scan reference, OpenClaw
publishing. Kept inline: every hard behavioral rule (dist/ ban, redaction
scan-at-sink, egress receipts, bisect commits, eval detach, CHANGELOG
entry rules), the machine-managed GBrain block (byte-identical), and the
'## Deploying to the active skill' header with gbrain-refresh in range
(pinned by test/gbrain-refresh-install-render.test.ts). No voice rewrites.

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

* fix(test): seed onboarding markers into the hermetic child GSTACK_HOME

EOV7 made bin/gstack-skill-start honor GSTACK_HOME, so the operator-HOME
seeding in e2e-helpers.ts no longer reaches hermetic children — the
emission layer fired lake-intro/telemetry prompts that burned turns and
stalled PTY tests waiting on an answer (observed: plan-mode-no-op derailed
by the telemetry question). Onboarding-specific tests pin their own
GSTACK_HOME per-test, which merges over this seed.

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

* test: raise carve-section-loading wall clock to 480s SDK / 540s bun

The heavy full-workflow scenarios satisfy their required section reads
inside 60s but need 300-450s to finish the report on slower sandboxes;
the 300s default read as a loading failure when the carve invariant held
(traces: plan-eng-review read its section at 8s, office-hours all three
at 24s, design-html both at 50s — all timed out mid-report).

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

* fix(security): harden the skill-start trust boundary — review-army findings

Session ID gains a urandom suffix (block binding unforgeable by reflected
content); _sanitize also neutralizes spoofed SESSION_ID: lines; branch
names are charset-clamped before JSON embedding (skill-start + skill-end);
.brain-last-push reads first line only with a charset clamp; the artifacts
URL echo routes through _sanitize; the privacy consent gate fires in
interactive sessions only (spawned auto-choose could accept consent no
human gave — emission order is not a safety property); the daily pull gets
non-interactive + slow-network git guards and stamps only when the
receipted path ran; ~/.claude.json gets a grep pre-filter before the jq
parse.

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

* fix(resolvers): question-log session_id becomes a substitution placeholder + stale-comment sweep

The question-log block bound $_SESSION_ID, a shell variable the
consolidated fence never sets — hook-less hosts logged empty session_id,
breaking /plan-tune per-session grouping. It now uses the same
substitute-from-the-skill-start-echoes contract as the telemetry block.
Also: retired the pre-Phase-2 stop-gate docstring, repointed the
gbrain-local-status cross-reference at the script's inline jq, dropped an
orphaned section comment, documented retro-metrics' suffix-only census.

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

* chore: regenerate renders for the question-log placeholder; goldens + baselines follow

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

* test: hermetic update-check, onboarding gate sequencing, seeding parity

The contract test's child did a live git ls-remote + curl to github.com on
every bun run test (update_check config now gates it off); the headless
test gets a fresh GSTACK_HOME so the suppression is actually exercised; a
new OV6 test drives the script three times to pin ack-at-emit and gate
sequencing; hermetic seeding covers the config-keyed privacy gate; the
EVALS_HERMETIC=0 debug seeding reaches marker parity.

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

* test(ci): demote the preamble A/B to periodic (OV7) and add it to the periodic matrix

Post-Phase-3 demotion per the plan; the eval needs fetch-depth 0 (it git
shows a pre-Phase-1 sha), which only the periodic workflow provides — and
a static matrix entry so it can't silently never run.

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

* chore: bump version and changelog (v1.70.0.0)

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

* docs: update project documentation for v1.70.0.0

ARCHITECTURE.md: the preamble section now describes the v1.70 runtime —
the rendered {{PREAMBLE}} block invokes bin/gstack-skill-start and reads
STATUS lines, gstack-skill-end logs telemetry, and one-time onboarding
text arrives as gated GSTACK_INSTRUCTION blocks instead of riding in
every render.

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

* docs: doc-review fixes — repair moved-file links, drop unbacked session-count claim

docs/BROWSER_INTERNALS.md: the two ARCHITECTURE.md anchor links broke when
the section moved from repo-root CLAUDE.md into docs/ — now ../ARCHITECTURE.md.
ARCHITECTURE.md: the preamble's session-tracking item claimed an active-session
count and an "ELI16 mode" that no shipped code implements (the count
computation was deleted with the inline preamble); describe the real
touch-and-prune behavior instead.

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

* docs(changelog): correct numeric claims against measured counts

50 of 62 installed skills dropped (fixture/alias entries have no preamble);
11 new carves + a deeper office-hours carve = 9→20; test counts match the
files (13 / 11 / 3 / 7).

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

* docs: repoint the preamble-runtime version reference after the queue rebump (v1.71.0.0)

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

* test(e2e-design): widen the Aesthetic synonym set — vocabulary variance, not a regression

Both attempts in run 33090283032 produced judge-praised DESIGN.md files
phrased as 'design principles'/'design language' without any of the four
original literals; inputs were identical to the prior passing run
32899975845 (design-consultation untouched by the intervening merge).

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

* fix(test): stage design-consultation's sections/ into the E2E fixture

The skill has been carved since v1.57.0.0 — the DESIGN.md structure
prescription (the AESTHETIC proposal template) lives in
sections/proposal-and-preview.md behind a STOP-read. The fixture only
copied SKILL.md, so the agent improvised structure from the skeleton and
the section-synonym check has been a coin flip since the carve (CI run
33090283032 trace shows 'no sections dir'; the local eval store has the
same failure on 2026-08-25 while that day's CI run passed on lucky
vocabulary).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-27 09:50:31 -07:00
committed by GitHub
co-authored by Claude Fable 5
parent a3749bfa4b
commit 394db326f2
227 changed files with 16814 additions and 34123 deletions
+149
View File
@@ -0,0 +1,149 @@
/**
* Context-budget capture — the ratchet's write side.
*
* Captures the current ALWAYS-ON + EAGER token ledgers from
* `lib/context-bill.ts` into `test/fixtures/context-budget.json`, with
* deliberate headroom baked into every ceiling:
*
* - alwaysOnTotal: actual × 1.05 (full-frontmatter catalog, aggregate)
* - eagerPerInvocation: actual × 1.10 (per-skill SKILL.md + forced refs)
*
* Why these two ledgers and no others: they are the ledgers nothing else
* measures (plan OV8). The shrink floor lives in skill-size-budget.test.ts,
* growth ratios + minBytes floors in parity-suite.test.ts, the name+description
* discovery cap in catalog-budget.test.ts. TOTAL overlaps those guards, so it
* is deliberately not budgeted here.
*
* Ratchet protocol (mirrors catalog-budget.test.ts):
* - Legitimate growth (a real feature grew a skill past its ceiling):
* re-run `bun test/helpers/capture-context-budget.ts` and commit the
* refreshed fixture IN THE SAME COMMIT as the growth, so the diff shows
* the conscious decision.
* - After a reduction phase lands: re-run the capture so the ceilings
* ratchet DOWN and the win is locked.
*
* Test-fixture skill trees under test/fixtures/ are excluded — they exist to
* test context-bill itself and must not couple the ratchet to test data.
*/
import * as fs from 'fs';
import * as path from 'path';
import { buildBill, type Bill } from '../../lib/context-bill';
export const REPO_ROOT = path.resolve(import.meta.dir, '..', '..');
export const BUDGET_FIXTURE_PATH = path.join(REPO_ROOT, 'test', 'fixtures', 'context-budget.json');
export const ALWAYS_ON_HEADROOM = 1.05;
export const EAGER_HEADROOM = 1.10;
/**
* Skill names come from path.relative in buildBill, which yields backslash
* separators on Windows. The fixture keys are POSIX. Normalize once here so
* the filter, the fixture keys, and checkBudget's name matching agree on
* every platform (the ratchet test runs in the curated Windows lane).
*/
export function toPosixName(name: string): string {
return name.split(path.sep).join('/');
}
/** Skills that exist only as context-bill test data — never budgeted. */
export function isFixtureSkill(name: string): boolean {
return toPosixName(name).startsWith('test/fixtures/');
}
export interface ContextBudget {
_comment: string;
alwaysOnTotal: number;
eagerPerInvocation: Record<string, number>;
}
/**
* The root SKILL.md's bill name falls back to the checkout directory's
* basename (path.relative gives '' at the root), which is machine-specific:
* a Conductor worktree named anything but "gstack" would mismatch the fixture
* key, and the documented "re-run the capture" recovery would then bake the
* local dirname INTO the committed fixture. Pin it to the skill's frontmatter
* name instead — stable across every clone.
*/
export const ROOT_SKILL_KEY = 'gstack';
/**
* The bill the ratchet grades: repo tree minus test-fixture skill dirs, with
* POSIX-normalized names, the root skill pinned to ROOT_SKILL_KEY, symlink
* aliases deduped by realpath (connect-chrome -> open-gstack-browser; on
* Windows checkouts the symlink materializes as a plain file and the alias
* dir vanishes, so budgeting it would make the stale-ceiling test
* platform-dependent — same dedupe the skill census uses), and ALL totals
* rebuilt from the filtered list (a partially-updated totals object would
* hand fixture-polluted numbers to any future consumer of the
* perInvocation/totalMd fields).
*/
export function buildRatchetBill(root: string = REPO_ROOT): Bill {
const bill = buildBill(root);
const candidates = bill.skills
.map((s) => ({
...s,
name: s.dir === bill.root ? ROOT_SKILL_KEY : toPosixName(s.name),
}))
.filter((s) => !isFixtureSkill(s.name));
// One ceiling per PHYSICAL skill: group by realpath, prefer the entry whose
// dir IS the realpath (the real dir) over symlink aliases.
const byReal = new Map<string, (typeof candidates)[number]>();
for (const s of candidates) {
let real: string;
try {
real = fs.realpathSync(s.dir);
} catch {
real = s.dir;
}
const cur = byReal.get(real);
if (!cur || (s.dir === real && cur.dir !== real)) byReal.set(real, s);
}
const kept = new Set(byReal.values());
const skills = candidates.filter((s) => kept.has(s));
return {
...bill,
skills,
totals: {
skillCount: skills.length,
alwaysOnBytes: skills.reduce((n, s) => n + s.frontmatterBytes, 0),
alwaysOnTokens: skills.reduce((n, s) => n + s.frontmatterTokens, 0),
eagerBytesBySkill: Object.fromEntries(skills.map((s) => [s.name, s.eagerBytes])),
eagerTokensBySkill: Object.fromEntries(skills.map((s) => [s.name, Math.round(s.eagerTokens)])),
perInvocationBytesBySkill: Object.fromEntries(skills.map((s) => [s.name, s.perInvocationBytes])),
perInvocationTokensBySkill: Object.fromEntries(
skills.map((s) => [s.name, Math.round(s.perInvocationTokens)]),
),
totalMdBytes: skills.reduce((n, s) => n + s.totalMdBytes, 0),
totalMdTokens: skills.reduce((n, s) => n + s.totalMdTokens, 0),
},
};
}
export function captureContextBudget(root: string = REPO_ROOT): ContextBudget {
const bill = buildRatchetBill(root);
const eagerPerInvocation: Record<string, number> = {};
for (const s of [...bill.skills].sort((a, b) => a.name.localeCompare(b.name))) {
eagerPerInvocation[s.name] = Math.ceil(s.eagerTokens * EAGER_HEADROOM);
}
return {
_comment:
'Context-budget ratchet ceilings (~tokens). Regenerate: bun test/helpers/capture-context-budget.ts. ' +
`Headroom: alwaysOnTotal x${ALWAYS_ON_HEADROOM}, eagerPerInvocation x${EAGER_HEADROOM}. ` +
'Graded by test/context-budget-ratchet.test.ts via lib/context-bill.ts checkBudget.',
alwaysOnTotal: Math.ceil(bill.totals.alwaysOnTokens * ALWAYS_ON_HEADROOM),
eagerPerInvocation,
};
}
// CLI: write the fixture atomically (temp + rename) — an interrupted capture
// must never leave truncated JSON that breaks the suite at module load.
if (import.meta.main) {
const budget = captureContextBudget();
const tmp = `${BUDGET_FIXTURE_PATH}.tmp-${process.pid}`;
fs.writeFileSync(tmp, JSON.stringify(budget, null, 2) + '\n');
fs.renameSync(tmp, BUDGET_FIXTURE_PATH);
const n = Object.keys(budget.eagerPerInvocation).length;
console.log(
`Wrote ${path.relative(REPO_ROOT, BUDGET_FIXTURE_PATH)}: alwaysOnTotal=${budget.alwaysOnTotal} tok, ${n} eager ceilings`,
);
}
+333 -23
View File
@@ -150,8 +150,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
},
behavioral: 'external',
externalTest: 'test/skill-e2e-ship-section-loading.test.ts',
maxSkeletonBytes: 92_300, // document-release visibility restore: named trigger (renders twice) + Step 17 handoff + hoisted doc-sync invariant; measured 91,764
minUnionBytes: 120_000,
maxSkeletonBytes: 71_300, // token-reduction Phases 1-2 + #2700 document-release anchors; measured 70,568 post-merge regen
minUnionBytes: 181_000, // token-reduction Phases 1-2 (v1.69.x branch); measured union 201,464
mustContain: ['VERSION', 'CHANGELOG', 'review', 'merge', 'PR'],
// v1.58.5.0: pre-push-guard install (#2077) stacks on the shared first-run-guidance preamble.
// Fork port wave 2: multi-ecosystem test-detection evidence (Django/JVM
@@ -181,8 +181,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// v1.65 merge: provisional larger-of-both-waves budget; re-measured below.
// Fork port wave 2 (#703): the repo-doc-preference block in the design
// check grew every plan-review skeleton ~0.7KB. Measured values noted.
maxSkeletonBytes: 93_900, // v1.68 fix wave: #2402 learnings capture + spool queue-depth lines; measured 93,345
minUnionBytes: 80_000,
maxSkeletonBytes: 73_980, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 73,381
minUnionBytes: 123_600, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured union 137,346
mustContain: ['SCOPE EXPANSION', 'SELECTIVE EXPANSION', 'HOLD SCOPE', 'SCOPE REDUCTION'],
// Default-on Codex outside-voice (codexPreflight block + CODEX_MODE branch
// prose replacing the smaller opt-in question) lands this ~5.2% over baseline.
@@ -207,8 +207,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// check grew every plan-review skeleton ~0.7KB. Measured values noted.
// #2499 project-scope MCP jq in the brain-sync block grew every tier-2+
// skeleton ~1.5KB (entry resolution emitted once per SKILL.md).
maxSkeletonBytes: 71_800, // v1.68 fix wave (#2402); measured 71,228
minUnionBytes: 70_000,
maxSkeletonBytes: 51_860, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 51,264
minUnionBytes: 99_800, // token-reduction Phases 1-2 (v1.69.x branch); measured union 110,910
mustContain: ['Architecture', 'Code Quality', 'Test', 'Performance'],
// Cross-cutting preamble growth (v1.57.2.0 AUQ-failure prose fallback + the
// decision-memory nudge + the v1.57.4.0 Boil-the-Ocean rename) plus the
@@ -240,8 +240,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// tier-2+ skeleton (measured 89,184). Main's v1.64.0.0 adds ~340 B more
// (telemetry --error-message/--failed-step preamble prose, PR #769).
// Budget covers the sum of both waves.
maxSkeletonBytes: 91_700, // v1.68 fix wave (#2402); measured 91,176
minUnionBytes: 70_000,
maxSkeletonBytes: 71_840, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 71,242
minUnionBytes: 99_200, // token-reduction Phases 1-2 (v1.69.x branch); measured union 110,293
mustContain: ['design', 'visual'],
maxSizeRatio: 1.12, // D1 1.104 + main's ~0.008
},
@@ -264,8 +264,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// check grew every plan-review skeleton ~0.7KB. Measured values noted.
// #2499 project-scope MCP jq in the brain-sync block grew every tier-2+
// skeleton ~1.5KB (entry resolution emitted once per SKILL.md).
maxSkeletonBytes: 83_500, // v1.68 fix wave (#2402); measured 82,941
minUnionBytes: 70_000,
maxSkeletonBytes: 63_580, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 62,977
minUnionBytes: 99_700, // token-reduction Phases 1-2 (v1.69.x branch); measured union 110,833
mustContain: ['developer experience', 'Getting Started'],
// Default-on Codex outside-voice (codexPreflight block + CODEX_MODE branch
// prose replacing the smaller opt-in question) lands this ~5.7% over baseline.
@@ -273,16 +273,17 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
},
'office-hours': {
skill: 'office-hours',
expectedSections: ['design-and-handoff.md'],
expectedSections: ['design-and-handoff.md', 'phase-2a-startup-diagnostic.md', 'phase-2b-builder-brainstorm.md'],
// Phase sections are mode-exclusive (a session runs exactly one of 2A/2B),
// so only the always-reached design/handoff section is a deterministic read.
requiredReads: ['design-and-handoff.md'],
scenario:
'Run office hours for this product idea through to the end: have the diagnostic conversation, explore alternatives, then write the design doc and run the relationship handoff (Phases 5-6).',
staticInvariants: {
mustStayInSkeleton: [],
mustMoveToSection: [],
// office-hours is conversational; the design-doc/handoff section has no
// post-STOP review gate in the skeleton.
gateAfterStop: undefined,
mustMoveToSection: ['### The Six Forcing Questions', '### Pushback Patterns', 'Anti-Sycophancy Rules', 'Wild exemplar'],
mustPrecedeStop: ['**Mode mapping:**'],
gateAfterStop: '## Section self-check',
},
behavioral: 'prompt',
// v1.2.0 activation lift: first-run-guidance section in the shared preamble,
@@ -294,8 +295,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// the #538 opt-out + D1 evidence directive — ratio 1.104 measured.
// #2499 project-scope MCP jq in the brain-sync block grew every tier-2+
// skeleton ~1.5KB (entry resolution emitted once per SKILL.md).
maxSkeletonBytes: 102_800, // v1.68 fix wave (#2402); measured 102,220
minUnionBytes: 70_000,
maxSkeletonBytes: 68_200, // token-reduction Phase 4 wave 4 (v1.69.x branch): 2A/2B carved out; measured 66,852
minUnionBytes: 115_800, // Phase 4 wave 4; measured union 118,175
mustContain: ['design doc', 'problem statement'],
maxSizeRatio: 1.12,
},
@@ -315,8 +316,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// +Conductor AUQ-default-prose rule + one-way/continuation safety in the
// always-loaded AskUserQuestion Format section.
// v1.2.0 activation lift: first-run-guidance section in the shared preamble.
maxSkeletonBytes: 57_900, // v1.68 fix wave (#2402); measured 57,385
minUnionBytes: 55_000,
maxSkeletonBytes: 38_070, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 37,469
minUnionBytes: 56_700, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured union 63,018
mustContain: ['CHANGELOG', 'Diataxis', 'coverage'],
// Two intentional additions stack on this small skill: the AUQ-failure prose
// fallback (v1.57.2.0, ~2KB to every preamble) AND the new default-on Codex
@@ -346,8 +347,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// v1.65 merge: provisional larger-of-both-waves budget; re-measured below.
// v1.64.1.0: shared-preamble prose from the two parallel v1.64 waves lands
// the skeleton at 69,022 B; +~1 KB headroom.
maxSkeletonBytes: 71_400, // v1.68 fix wave (#2402); measured 70,815
minUnionBytes: 72_000,
maxSkeletonBytes: 51_500, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured 50,899
minUnionBytes: 65_000, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured union 72,252
mustContain: ['Typography', 'Color', 'Aesthetic Direction'],
// Cross-cutting preamble growth (v1.57.2.0 AUQ-failure prose fallback ~2KB +
// the cross-session decision-memory nudge) lands this carved skeleton just over
@@ -386,8 +387,8 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// +Conductor AUQ-default-prose rule + one-way/continuation safety in the
// always-loaded AskUserQuestion Format section.
// v1.2.0 activation lift: first-run-guidance section in the shared preamble.
maxSkeletonBytes: 77_300, // v1.68 fix wave (#2402); measured 76,705
minUnionBytes: 72_000,
maxSkeletonBytes: 57_390, // token-reduction Phases 1-2 (v1.69.x branch); measured 56,789
minUnionBytes: 64_200, // token-reduction Phases 1-2 (v1.69.x branch); measured union 71,379
mustContain: ['OWASP', 'STRIDE', 'daily', 'comprehensive', 'verif'],
// cso keeps its mode-dispatch + FP-filtering phases always-loaded, so the
// cross-cutting preamble growth (v1.57.2.0 AUQ-failure prose fallback ~2KB + the
@@ -395,6 +396,315 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
// v1.64+v1.65 merge sums both waves' preamble growth; measured 1.073.
maxSizeRatio: 1.08,
},
// ── Token-reduction Phase 4 wave 1 (v1.69.x branch) ──────────────────────
review: {
skill: 'review',
expectedSections: ['plan-completion.md', 'review-army.md', 'adversarial.md'],
requiredReads: ['plan-completion.md', 'review-army.md'],
scenario:
"The working tree has a real diff against the base branch (assume Step 1's git checks passed; the diff implements the PLAN.md cache layer). Run the /review flow: the scope-drift and plan-completion deep pass against PLAN.md, then the critical pass, then the Review Army specialist dispatch — apply the specialist checklists yourself instead of launching subagents. Produce the review report. Do NOT commit, push, or create a PR.",
staticInvariants: {
mustStayInSkeleton: [
'## Step 0: Detect platform and base branch',
'## Step 1: Check branch',
'## Step 1.5: Scope Drift Detection',
'## Step 4: Critical pass (core review)',
'## Confidence Calibration',
'## Step 5: Fix-First Review',
'## Important Rules',
'Persist Eng Review result',
],
mustPrecedeStop: ['## Step 0: Detect platform and base branch'],
mustMoveToSection: [
'Plan File Discovery',
'MULTI-SPECIALIST CONFIRMED',
'Cross-model synthesis',
'codex review --base',
],
gateAfterStop: undefined, // operational multi-STOP skill, like ship
},
behavioral: 'plan',
maxSkeletonBytes: 55_600, // Phase 4 wave 1; measured 55,010
minUnionBytes: 89_000, // Phase 4 wave 1; measured union 93,357
mustContain: ['confidence', 'P1', 'P2', 'Review Army', 'adversarial'],
},
codex: {
skill: 'codex',
expectedSections: ['review-mode.md', 'challenge-mode.md', 'consult-mode.md'],
requiredReads: ['review-mode.md', 'consult-mode.md'],
scenario:
"Run the /codex skill twice: first Review mode against this branch's diff (produce the GATE verdict), then Consult mode with the follow-up 'is the strongest finding worth fixing before ship?'. Follow the Step 1 dispatch and read each selected mode's section before executing it; if the codex CLI is unavailable, still walk the mode instructions and report what you would run.",
staticInvariants: {
mustStayInSkeleton: [
'## Step 1: Detect mode',
'## Filesystem Boundary',
'Synthesis recommendation (REQUIRED)',
'Recommendation: <action> because',
'UNDER_CODEX',
],
mustPrecedeStop: ['## Step 1: Detect mode', '## Filesystem Boundary'],
mustMoveToSection: [
'The gate FAILS CLOSED',
'Think like an attacker and a chaos engineer',
'codex exec resume',
],
gateAfterStop: 'EXIT PLAN MODE GATE',
},
behavioral: 'prompt',
maxSkeletonBytes: 55_760, // Phase 4 wave 1; measured 55,155
minUnionBytes: 83_400, // Phase 4 wave 1; measured union 84,304
mustContain: ['GATE: PASS', 'CROSS-MODEL ANALYSIS', 'codex exec resume', 'sandbox_mode="read-only"', 'mktemp'],
maxSizeRatio: 1.06, // measured 1.040 vs the v1.64.1.0 parity baseline
},
'land-and-deploy': {
skill: 'land-and-deploy',
expectedSections: ['first-run-validation.md', 'readiness-gate.md', 'merge-and-deploy.md'],
requiredReads: ['readiness-gate.md', 'merge-and-deploy.md'],
scenario:
'This project has a confirmed prior /land-and-deploy run (treat the Step 1.5 check as CONFIRMED). A PR exists for this branch and CI is green. Simulate — do not run gh or actually merge: run the pre-merge readiness gate and produce the readiness report, then walk the merge and deploy-strategy steps, stating which merge path and deploy strategy you would take. Do NOT use AskUserQuestion.',
staticInvariants: {
mustStayInSkeleton: [
'land-deploy-confirmed',
'## Step 3.4: VERSION drift detection',
'## Step 6: Wait for deploy',
],
mustPrecedeStop: ['land-deploy-confirmed'],
mustMoveToSection: [
'PRE-MERGE READINESS REPORT',
'gh pr merge --squash --auto --delete-branch',
'DEPLOY INFRASTRUCTURE VALIDATION',
],
gateAfterStop: undefined, // operational skill
},
behavioral: 'prompt',
maxSkeletonBytes: 57_500, // Phase 4 wave 1; estimated ~56.2KB rendered — re-measured at regen
minUnionBytes: 91_000, // Phase 4 wave 1; estimated union ~94.9KB
mustContain: ['readiness', 'merge', 'canary', 'revert', 'staging'],
},
// ── Token-reduction Phase 4 wave 2 (v1.69.x branch) ──────────────────────
autoplan: {
skill: 'autoplan',
expectedSections: ['ceo-phase.md', 'design-phase.md', 'eng-phase.md', 'dx-phase.md', 'tasks-aggregator.md'],
requiredReads: ['ceo-phase.md', 'eng-phase.md', 'tasks-aggregator.md'],
scenario:
'Run the /autoplan pipeline against the plan in PLAN.md. Codex and subagent tools are unavailable — note both voices unavailable (single-reviewer mode) and keep going. The plan has no UI scope and no developer-facing scope, so Phase 2 and Phase 3.5 are skipped (do not read their sections). Execute Phase 1 (CEO) and Phase 3 (Eng) at full depth, run the Phase 4 aggregator step, and produce the Final Approval Gate summary as the report.',
staticInvariants: {
mustStayInSkeleton: [
'## The 6 Decision Principles',
'## Sequential Execution — MANDATORY',
'## Decision Classification',
'## Filesystem Boundary — Codex Prompts',
'## Phase 0.5: Codex auth + version preflight',
'## Pre-Gate Verification',
'## Phase 2: Design Review (conditional — skip if no UI scope)',
'## Phase 3.5: DX Review (conditional — skip if no developer-facing scope)',
'- Scope gate (the plan under review is already the target)',
],
mustPrecedeStop: ['## The 6 Decision Principles', '## Sequential Execution — MANDATORY', '## Decision Classification'],
mustMoveToSection: [
'CEO DUAL VOICES — CONSENSUS TABLE:',
'CODEX SAYS (design — UX challenge)',
'ENG DUAL VOICES — CONSENSUS TABLE:',
'DX DUAL VOICES — CONSENSUS TABLE:',
'## Implementation Tasks aggregator',
],
gateAfterStop: 'AskUserQuestion options:',
},
behavioral: 'external',
externalTest: 'test/skill-e2e-autoplan-chain.test.ts', // phase-complete markers live ONLY in sections — its assertions ARE section-read proof
maxSkeletonBytes: 59_300, // Phase 4 wave 2; measured 58,696
minUnionBytes: 85_000, // measured union 86,926
mustContain: ['6 Decision Principles', 'TASTE DECISION', 'USER CHALLENGE', 'consensus', 'Restore Point'],
},
spec: {
skill: 'spec',
expectedSections: ['gate-and-file.md'],
requiredReads: ['gate-and-file.md'],
scenario:
"The user already completed Phases 1-4 of /spec for the request 'add a --json output flag to the CLI status command'. Treat the five Phase 1 answers, the scope lock, and the technical interrogation as settled, and the Phase 4 draft as CONFIRMED by the user. Continue from that point in file-only mode (--no-execute is set): run the Phase 4.5 sequence and Phase 5, simulating every external command (codex, gh, the redaction bin) by stating what you would run and the expected outcome instead of executing it. Do NOT use AskUserQuestion.",
staticInvariants: {
mustStayInSkeleton: [
'HARD GATE',
'### Phase 1: Understand the "Why"',
'### Phase 3: Technical Interrogation',
'### Phase 4: Draft Review',
'gstack-issue-guard',
'## Issue Structure Templates',
],
mustPrecedeStop: ['## Flag Reference', '### Phase 1: Understand the "Why"'],
mustMoveToSection: [
'<<<USER_SPEC>>>',
'SEMANTIC_REVIEW: clean',
'gh issue create --title',
'PIN_SHA=$(git rev-parse HEAD)',
],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 51_200, // Phase 4 wave 2: Phases 4.5-5 carved at the post-confirmation boundary; measured 50,681
minUnionBytes: 64_500, // measured union 67,430
mustContain: ['HARD GATE', 'dedupe', 'quality gate', 'acceptance criteria', 'archive'],
},
'setup-gbrain': {
skill: 'setup-gbrain',
expectedSections: ['engine-remediation.md', 'brain-init.md', 'transcript-gate.md', 'claude-md-persist.md'],
requiredReads: ['brain-init.md', 'claude-md-persist.md'],
scenario:
"Walk /setup-gbrain in SIMULATION — do not execute any bash, install anything, or register MCP; for each step state the exact commands you WOULD run. Treat Step 1 detect as: gbrain_on_path=false, gbrain_local_status=missing-config, no shortcut flags. Treat Path 3 (PGLite local) as already picked at Step 2 — do not use AskUserQuestion. Walk Steps 3, 4 (Path 3 init), 5, 5a, and 8: read each step's pointed section before doing it, and write out the exact CLAUDE.md block Step 8 would persist. Skip Steps 6, 7, 7.5, 9, 9.5, and 10. End with a one-paragraph setup summary.",
staticInvariants: {
mustStayInSkeleton: [
'## Step 2: Pick a path (AskUserQuestion)',
'## Step 1: Detect current state',
'gbrain_mcp_mode=remote-http',
'claude mcp add --scope user --transport http gbrain',
'SKIP entirely on Path 4 (Remote MCP)',
'<YOUR_TOKEN>',
],
mustPrecedeStop: ['## Step 1: Detect current state'],
mustMoveToSection: [
'### Path 1 (Supabase, existing URL)',
'read_secret_to_env GBRAIN_MCP_TOKEN',
'Mode: remote-http',
'gstack-memory-ingest.ts --probe',
],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 57_600, // Phase 4 wave 2; measured 56,954
minUnionBytes: 78_300, // measured union 79,139
mustContain: ['PGLite', 'Supabase', 'claude mcp add', 'read_secret_to_env', 'pooler'],
maxSizeRatio: 1.07, // measured 1.051 vs the branch monolith: index + stubs + 4 STOP pointers
},
// ── Token-reduction Phase 4 wave 3 (v1.69.x branch) ──────────────────────
qa: {
skill: 'qa',
expectedSections: ['test-bootstrap.md', 'qa-patterns.md'],
requiredReads: ['qa-patterns.md'],
scenario:
'Walk /qa in SIMULATION — do not launch a browser, run any $B command, or execute bash; treat the working tree as clean, the tier as Quick, and the target app as http://localhost:3000 with a small feature-branch diff touching one page. Skip the test-framework bootstrap (assume CLAUDE.md documents the test command). Read each pointed section before doing its step, then produce the QA plan as the report: the mode you selected and why, the Phase 1-6 steps you would run, and a worked health-score computation from the rubric. Do NOT use AskUserQuestion.',
staticInvariants: {
mustStayInSkeleton: [
'## Setup',
'## SETUP (run this check BEFORE any browse command)',
'## Phases 1-6: QA Baseline',
'## Phase 7: Triage',
'## Phase 8: Fix Loop',
'8e.5. Regression Test',
'WTF-LIKELIHOOD',
'## Additional Rules (qa-specific)',
'## Output Structure',
],
mustPrecedeStop: ['## Setup'],
mustMoveToSection: [
'## Test Framework Bootstrap',
'BOOTSTRAP_DECLINED',
'## Health Score Rubric',
'### Diff-aware (automatic when on a feature branch with no URL)',
'Never refuse to use the browser',
],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 48_750, // Phase 4 wave 3; measured 48,151
minUnionBytes: 69_500, // measured union 70,385
mustContain: ['bug', 'browse', 'fix', 'Health Score Rubric', 'regression'],
},
browse: {
skill: 'browse',
expectedSections: ['command-list.md'],
requiredReads: ['command-list.md'],
scenario:
'QA a static page: before driving it, plan the full audit — enumerate which browse commands and snapshot flags you would use, including extraction/tab/dialog commands beyond the Most-Used table, reading the full command reference first. Do not launch the browser or run any $B command; produce the command plan as the report.',
staticInvariants: {
mustStayInSkeleton: ['## SETUP', '## Core QA Patterns', '## CSS Inspector', '## Most-Used Commands'],
mustPrecedeStop: ['## SETUP'],
mustMoveToSection: ['## Full Command List', '## Snapshot Flags', '### Navigation'],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 27_500, // Phase 4 wave 3; measured 26,875
minUnionBytes: 39_500, // measured union 41,115
// 'BEGIN/END UNTRUSTED EXTERNAL' pins the untrusted-content warning; the full
// envelope phrase wraps across lines in the rendered blockquote, so the
// contiguous-substring check needs the single-line prefix form.
mustContain: ['BEGIN/END UNTRUSTED EXTERNAL', 'snapshot -i', '@e refs', 'deviceScaleFactor', 'handoff'],
},
retro: {
skill: 'retro',
expectedSections: ['report-format.md'],
requiredReads: ['report-format.md'],
scenario:
'Run the repo-scoped weekly retrospective for the last 7 days on this repo. There is no origin remote — proceed with the local branch per the guard disclosure rules. The gstack-retro-metrics script is not installed, so follow the degraded path (compute the metrics manually with git). Skip any AskUserQuestion calls — this is non-interactive. Produce the full narrative retrospective report.',
staticInvariants: {
mustStayInSkeleton: ['gstack-retro-metrics', '### Step 2: Compute Metrics', '### Step 13: Save Retro History'],
mustPrecedeStop: ['### Step 2: Compute Metrics'],
mustMoveToSection: ['## Engineering Retro: [date range]', '### Team Breakdown', 'Plan Completion This Period'],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 69_500, // Phase 4 wave 3; measured 68,483 (script absorption -5.4KB)
minUnionBytes: 66_000, // measured union 73,496
mustContain: ['retrospective', '45-minute gap', 'Ship of the week', 'Praise'],
},
// ── Token-reduction Phase 4 wave 4 (v1.69.x branch): design doctrine carve ──
// (D3A: read-on-demand doctrine, requiredReads-guarded + loading eval)
'design-html': {
skill: 'design-html',
expectedSections: ['doctrine.md', 'pretext-patterns.md'],
requiredReads: ['doctrine.md', 'pretext-patterns.md'],
scenario:
'Walk /design-html in SIMULATION — do not run bash, start servers, launch a browser, or take screenshots. Treat Step 0 as already resolved: no CEO plan, no approved mockup, no variants, no DESIGN.md, no prior finalized.html — freeform mode (Case C option D), screen name "pricing", the user wants a pricing page for a developer-tools SaaS (dark, dense, three tiers, monospace-leaning). Do NOT use AskUserQuestion — proceed with the stated assumptions. Read each pointed section before doing its step, then execute Steps 1-3: produce the implementation spec, state the chosen Pretext tier and why, and generate the complete Pretext-native HTML — include the HTML in your report instead of writing files. Stop there: skip Step 3.5, Step 4, and Step 5.',
staticInvariants: {
mustStayInSkeleton: [
'## Step 0: Input Detection',
'## Step 2: Smart Pretext API Routing',
'### HTML Generation',
'AI slop blacklist',
'## Step 4: Preview + Refinement Loop',
'## Important Rules',
],
mustPrecedeStop: ['## DESIGN SETUP'],
mustMoveToSection: [
'### The Three Laws of Usability',
'### The Goodwill Reservoir',
'### Pretext Wiring Patterns',
'### Pretext API Reference',
],
gateAfterStop: undefined, // operational skill, no plan-mode gate
},
behavioral: 'prompt',
maxSkeletonBytes: 49_900, // Phase 4 wave 4; measured 48,886
minUnionBytes: 57_500, // Phase 4 wave 4; measured union 58,682
mustContain: ["Don't make me think", "Users scan, they don't read", 'The Goodwill Reservoir', 'PRETEXT API CHEATSHEET', 'Pattern 3: Text around obstacles'],
},
'design-shotgun': {
skill: 'design-shotgun',
expectedSections: ['doctrine.md'],
requiredReads: ['doctrine.md'],
scenario:
'Walk /design-shotgun in SIMULATION — do not run bash, launch a browser, call the design binary, or spawn agents. Treat Step 0 as NO_PREVIOUS_SESSIONS and Step 1 context as fully gathered: a landing page for an open-source CLI tool, audience = developers evaluating it from a GitHub README link, no DESIGN.md, no taste profile or prior approved.json (Step 2 finds nothing). Do NOT use AskUserQuestion — proceed with the stated assumptions. Read each pointed section before doing its step, then run Step 3a at full depth: write three distinct variant concepts with their full variant-specific generation briefs, apply the anti-convergence check, and produce the concept list plus briefs as the report. Stop before Step 3b.',
staticInvariants: {
mustStayInSkeleton: [
'## Step 0: Session Detection',
'## Step 2: Taste Memory',
'Anti-convergence directive',
'### Step 3b: Concept Confirmation',
'## Important Rules',
],
mustPrecedeStop: ['## DESIGN SETUP'],
mustMoveToSection: [
'### The Three Laws of Usability',
'### The Goodwill Reservoir',
"Users scan, they don't read",
],
gateAfterStop: undefined,
},
behavioral: 'prompt',
maxSkeletonBytes: 50_600, // Phase 4 wave 4; measured 49,578
minUnionBytes: 53_200, // Phase 4 wave 4; measured union 54,290
mustContain: ["Don't make me think", "Users scan, they don't read", 'trunk test', '44px minimum'],
},
};
/** Sorted carved-skill names. Consumers derive their lists from this — no parallel lists. */
+6 -1
View File
@@ -267,10 +267,15 @@ export async function finalizeEvalCollector(evalCollector: EvalCollector | null)
// Pre-seed preamble state files so E2E tests don't waste turns on lake intro + telemetry prompts.
// These are one-time interactive prompts that burn 3-7 turns per test if not pre-seeded.
// NOTE: since gstack-skill-start honors GSTACK_HOME (EOV7), hermetic children read the
// temp GSTACK_HOME that hermetic-env.ts seeds (the canonical marker list lives there);
// this operator-HOME seeding only serves EVALS_HERMETIC=0 debug runs.
if (evalsEnabled) {
const gstackDir = path.join(os.homedir(), '.gstack');
fs.mkdirSync(gstackDir, { recursive: true });
for (const f of ['.completeness-intro-seen', '.telemetry-prompted', '.proactive-prompted']) {
// Marker list kept at parity with hermetic-env.ts's child-GSTACK_HOME seed
// (the canonical set for the emission layer's gates).
for (const f of ['.activated', '.completeness-intro-seen', '.telemetry-prompted', '.proactive-prompted', '.first-loop-tip-shown']) {
const p = path.join(gstackDir, f);
if (!fs.existsSync(p)) fs.writeFileSync(p, '');
}
+13
View File
@@ -211,6 +211,19 @@ export function getHermeticDirs(): HermeticDirs {
trustedDirs: [repoRoot()],
});
fs.writeFileSync(path.join(configDir, '.claude.json'), JSON.stringify(seed, null, 2));
// Seed one-time onboarding markers into the CHILD's GSTACK_HOME.
// bin/gstack-skill-start reads ${GSTACK_HOME:-$HOME/.gstack} (EOV7), so
// the operator-HOME seeding in e2e-helpers.ts no longer reaches hermetic
// children — without these, the emission layer fires lake-intro/telemetry
// prompts that burn turns and can stall PTY tests waiting on an answer.
// Tests that exercise onboarding itself override GSTACK_HOME per-test.
for (const f of ['.activated', '.completeness-intro-seen', '.telemetry-prompted', '.proactive-prompted', '.first-loop-tip-shown']) {
fs.writeFileSync(path.join(gstackHome, f), '');
}
// The privacy stop-gate is config-keyed, not marker-keyed: on machines
// with gbrain installed it fires whenever artifacts_sync_mode is off and
// the consent prompt is unrecorded — same PTY-stall class as the markers.
fs.writeFileSync(path.join(gstackHome, 'config.yaml'), 'artifacts_sync_mode_prompted: true\n');
} catch (err) {
try { fs.rmSync(runRoot, { recursive: true, force: true }); } catch { /* best-effort */ }
throw err;
+2 -36
View File
@@ -206,32 +206,8 @@ export function runParityChecks(opts: {
*/
const MONOLITH_INVARIANTS: ParityInvariant[] = [
// cso is now carved — its invariant is generated from CARVE_GUARDS below.
{
skill: 'review',
mustContain: ['confidence', 'P1', 'P2'],
mustHaveHeadings: ['## Preamble', '## When to invoke'],
// The adversarial step swapped its bare `command -v codex` check for the shared
// codexPreflight() block (install + auth tri-state + CODEX_MODE branch prose),
// landing ~6.3% over the v1.53.0.0 baseline. Intentional: it adds proper
// not-installed vs not-authed handling, not slop.
// v1.64+v1.65 merge: both waves grew the shared preamble (evidence
// directive + telemetry failure flags); measured 1.094.
maxSizeRatio: 1.10,
minBytes: 70_000,
},
{
skill: 'qa',
mustContain: ['bug', 'browse', 'fix'],
mustHaveHeadings: ['## Preamble', '## When to invoke'],
// v1.2.0 activation lift: the unified first-run-guidance section (P4 scaffold +
// P3 loop tip) is added to every skill's shared preamble — intentional, ~1KB.
// Fork port wave 2: the shared coverage-audit detection block gained the
// multi-ecosystem markers (Django/JVM, script/target/test-file census —
// e3259078 port); measured 1.111x. v1.64+v1.65 merge sums both waves'
// preamble growth; measured 1.125.
maxSizeRatio: 1.13,
minBytes: 50_000,
},
// review, codex, land-and-deploy (w1), autoplan (w2), qa (w3) carved in token-reduction Phase 4
// wave 1 (v1.69.x branch) — their invariants generate from CARVE_GUARDS too.
{
skill: 'investigate',
mustContain: ['root cause', 'hypothes'],
@@ -248,16 +224,6 @@ const MONOLITH_INVARIANTS: ParityInvariant[] = [
maxSizeRatio: 1.12, // D1 measured
minBytes: 30_000,
},
{
skill: 'autoplan',
mustContain: ['ceo', 'eng', 'design'],
mustHaveHeadings: ['## Preamble', '## When to invoke'],
// v1.2.0 activation lift: shared first-run-guidance preamble section.
// Fork port wave 2 (D1): the evidence-before-claimed-limitations preamble
// directive adds ~0.45KB to every tier-2+ skill. Measured values noted.
maxSizeRatio: 1.09, // D1 measured
minBytes: 70_000,
},
];
/**
+107
View File
@@ -0,0 +1,107 @@
/**
* setup-gbrain E2E fixture builder — carve-aware (token-reduction Phase 4).
*
* setup-gbrain is carved: the generated SKILL.md is a decision-tree skeleton
* whose STOP-Read pointers reference install paths
* (`~/.claude/skills/gstack/setup-gbrain/sections/*.md`) that do not exist in
* a hermetic E2E sandbox. Pointing an agent at the raw skeleton would burn
* turns on failed Reads and never reach the per-path init procedures under
* test. This builder reconstructs a runnable single-file fixture, wave-1
* style (see the codex fixture in test/skill-e2e-workflow.test.ts):
*
* 1. slice the skeleton from the skill title (dropping the shared preamble —
* CLAUDE.md rule: "E2E test fixtures: extract, don't copy"),
* 2. cut the Section index table (its sections/ paths don't resolve here),
* 3. replace each STOP pointer with the section body the test needs, or an
* explicit "not needed" stub for the rest, and
* 4. run a non-empty guard: every needed section's distinctive anchor must
* be present in the result, so a renamed/emptied section fails loudly
* instead of shipping a silently hollow fixture.
*
* Monolith-tolerant: if the generated SKILL.md has no STOP pointers (pre-carve
* checkout, or a regen that un-carves), the bodies are still inline and the
* anchor guard passes — the builder works on both shapes.
*/
import * as fs from 'fs';
import * as path from 'path';
const ROOT = path.resolve(import.meta.dir, '..', '..');
const SKILL_MD = path.join(ROOT, 'setup-gbrain', 'SKILL.md');
const SECTIONS_DIR = path.join(ROOT, 'setup-gbrain', 'sections');
const TITLE = '# /setup-gbrain — Coding-Agent Onboarding for gbrain';
/** Matches one generated STOP-Read pointer (two lines) and captures the section file name. */
const STOP_POINTER =
/^> \*\*STOP\.\*\* Before [^\n]*sections\/([a-z0-9-]+\.md)[^\n]*\n> in full\.[^\n]*/gm;
/** Distinctive per-section anchors — the non-empty guard for inlined content. */
export const SECTION_ANCHORS: Record<string, string> = {
'brain-init.md': '### Path 4 (Remote gbrain MCP',
'claude-md-persist.md': 'Mode: remote-http',
'engine-remediation.md': "Your local gbrain engine isn't responding",
'transcript-gate.md': 'gstack-memory-ingest.ts --probe',
};
/**
* Build the fixture text: skeleton (preamble dropped, Section index cut) with
* `neededSections` inlined at their STOP pointers and every other pointer
* replaced by an explicit not-needed stub. Throws on any missing anchor.
*/
export function buildSetupGbrainFixture(neededSections: string[]): string {
for (const file of neededSections) {
if (!(file in SECTION_ANCHORS)) {
throw new Error(
`setup-gbrain fixture: unknown section "${file}" — known: ${Object.keys(SECTION_ANCHORS).join(', ')}`,
);
}
}
let full = fs.readFileSync(SKILL_MD, 'utf-8');
const titleIdx = full.indexOf(TITLE);
if (titleIdx < 0) throw new Error(`setup-gbrain fixture: title heading not found: "${TITLE}"`);
full = full.slice(titleIdx);
// Cut the Section index table (heading through its closing --- separator).
const idxStart = full.indexOf('## Section index');
if (idxStart >= 0) {
const idxEnd = full.indexOf('\n---\n', idxStart);
if (idxEnd < 0) throw new Error('setup-gbrain fixture: Section index has no closing ---');
full = full.slice(0, idxStart) + full.slice(idxEnd + '\n---\n'.length);
}
full = full.replace(STOP_POINTER, (_m, file: string) => {
if (!neededSections.includes(file)) {
return '_(Section not included in this fixture — not needed for this run. Continue with the next step.)_';
}
const secPath = path.join(SECTIONS_DIR, file);
if (!fs.existsSync(secPath)) {
throw new Error(
`setup-gbrain fixture: sections/${file} not generated — run bun run gen:skill-docs`,
);
}
const body = fs
.readFileSync(secPath, 'utf-8')
.replace(/^<!--[^\n]*-->\n/gm, '') // strip AUTO-GENERATED header comments
.trim();
if (body.length < 500) {
throw new Error(`setup-gbrain fixture: sections/${file} is unexpectedly small/empty`);
}
return body;
});
// Non-empty guard on the RESULT — holds for both the carved shape (section
// inlined above) and the monolith shape (body was never carved out).
for (const file of neededSections) {
if (!full.includes(SECTION_ANCHORS[file])) {
throw new Error(
`setup-gbrain fixture: needed section "${file}" content missing from fixture ` +
`(anchor not found: "${SECTION_ANCHORS[file]}")`,
);
}
}
return full;
}
+44 -40
View File
@@ -31,8 +31,10 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
'hermetic-sentinel': ['test/helpers/hermetic-env.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-hermetic-canary.test.ts', 'lib/conductor-env-shim.ts'],
// P4 first-run scaffold (activation lift) — the detection binary end-to-end
// through the real runner, plus the preamble wiring that gates + maps it.
'first-task-scaffold': ['bin/gstack-first-task-detect', 'scripts/resolvers/preamble/generate-first-run-guidance.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/skill-e2e-first-task-scaffold.test.ts', 'test/helpers/session-runner.ts'],
// through the real runner, plus the script wiring that gates + maps it
// (token-reduction Phase 2: generate-first-run-guidance.ts was deleted; the
// gate + token→tip map live in bin/gstack-skill-start's emission layer).
'first-task-scaffold': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-first-task-detect', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/skill-e2e-first-task-scaffold.test.ts', 'test/helpers/session-runner.ts'],
// SKILL.md setup + preamble (depend on ROOT SKILL.md + gen-skill-docs)
'skillmd-setup-discovery': ['SKILL.md', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
@@ -87,17 +89,17 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
// include question-tuning.ts and generate-ask-user-format.ts because the
// AUTO_DECIDE preamble injection lives there and changes can flip the
// regression test outcome between 'asked' and 'auto_decided'.
'plan-ceo-review-plan-mode': ['plan-ceo-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts'],
'plan-eng-review-plan-mode': ['plan-eng-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-eng-plan-mode.test.ts'],
'plan-design-review-plan-mode': ['plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-design-plan-mode.test.ts'],
'plan-devex-review-plan-mode': ['plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-devex-plan-mode.test.ts'],
'plan-ceo-review-plan-mode': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts'],
'plan-eng-review-plan-mode': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-eng-plan-mode.test.ts'],
'plan-design-review-plan-mode': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-design-plan-mode.test.ts'],
'plan-devex-review-plan-mode': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-devex-plan-mode.test.ts'],
// Covers ceo (preamble misfire) + eng/design (scope-gate bypass must not
// fire outside plan mode) + the named-target exception case. 4 PTY runs;
// in CI these run CONCURRENT with the rest of the pty-plan-smoke suite
// (--max-concurrency + --retry 1), so worst-case cost is ~2x a single
// pass of each, sharing the API budget with sibling tests — not the
// sequential ~+10min a local read suggests.
'plan-mode-no-op': ['plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-mode-no-op.test.ts'],
'plan-mode-no-op': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-mode-no-op.test.ts'],
// v1.21+ AskUserQuestion-blocked regression tests — Conductor launches
// claude with `--disallowedTools AskUserQuestion --permission-mode default`
@@ -107,24 +109,25 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
// INSIDE the existing 4 plan-X-review-plan-mode test files (covered
// transitively by the entries above). Two new standalone files exist for
// skills with no prior plan-mode test:
'office-hours-auto-mode': ['office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts'],
'office-hours-phase4-fork': ['office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts'],
'llm-judge-recommendation': ['test/helpers/llm-judge.ts', 'test/llm-judge-recommendation.test.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'codex/SKILL.md.tmpl', 'scripts/resolvers/review.ts'],
'office-hours-auto-mode': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts'],
'office-hours-phase4-fork': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts'],
'llm-judge-recommendation': ['codex/**', 'test/helpers/llm-judge.ts', 'test/llm-judge-recommendation.test.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'codex/SKILL.md.tmpl', 'scripts/resolvers/review.ts'],
// v1.21+ AUTO_DECIDE preserve eval (periodic). Verifies the Tool resolution
// fix doesn't trip the legitimate /plan-tune opt-in path: when the user has
// written a never-ask preference, AUQ should still auto-decide rather than
// surfacing the question. Touches the question-tuning + preference
// infrastructure plus the resolvers that own the AUTO_DECIDE preamble.
'auto-decide-preserved': ['scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts'],
'auto-decide-preserved': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts'],
// Conductor → prose decision brief (Conductor signal makes prose the default;
// the PreToolUse hook denies the flaky tool). Touches the resolver that owns
// the Conductor rule, the preamble signal, the hook, and the detection helper.
'conductor-prose': ['scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble.ts', 'plan-eng-review/**', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-conductor-prose.test.ts'],
'conductor-prose': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble.ts', 'plan-eng-review/**', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-conductor-prose.test.ts'],
// Real-PTY E2E batch (#6 new tests on the harness).
// Each one tests behavior the SDK harness can't observe (rendered TTY,
// numbered-option lists, multi-phase ordering, idempotency state echo).
'preamble-script-ab': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-brain-sync-block.ts', 'scripts/resolvers/preamble.ts', 'plan-ceo-review/**', 'test/helpers/auq-sdk-capture.ts', 'test/skill-e2e-preamble-script-ab.test.ts'],
'auq-format-gate': ['plan-ceo-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completeness-section.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/helpers/llm-judge.ts'],
'plan-ceo-mode-routing': ['plan-ceo-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-ceo-mode-routing.test.ts'],
'plan-design-with-ui-scope': ['plan-design-review/**', 'test/fixtures/plans/ui-heavy-feature.md', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-design-with-ui.test.ts'],
@@ -136,49 +139,49 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
// devex, office-hours + future PR2 carves). One file iterating CARVE_GUARDS;
// the selector sets GSTACK_CARVE_SKILL=<name> to scope cost to the changed
// skill (D-CODEX A). Touching the registry/helper or sections.ts runs all.
'carve-section-loading': ['plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'office-hours/**', 'document-release/**', 'design-consultation/**', 'cso/**', 'test/helpers/carve-guards.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts'],
'carve-section-loading': ['design-html/**', 'design-shotgun/**', 'qa/**', 'browse/**', 'retro/**', 'autoplan/**', 'spec/**', 'setup-gbrain/**', 'review/**', 'codex/**', 'land-and-deploy/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'office-hours/**', 'document-release/**', 'design-consultation/**', 'cso/**', 'test/helpers/carve-guards.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts'],
'autoplan-chain-pty': ['autoplan/**', 'plan-ceo-review/**', 'plan-design-review/**', 'plan-eng-review/**', 'plan-devex-review/**', 'test/fixtures/plans/ui-heavy-feature.md', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-autoplan-chain.test.ts'],
'e2e-harness-audit': ['plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/claude-pty-runner.ts'],
'e2e-harness-audit': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/claude-pty-runner.ts'],
// Per-finding AskUserQuestion count + review-report-at-bottom assertion.
// Each test drives its skill end-to-end; touchfiles include preamble +
// completion-status resolvers because they affect question cadence and
// terminal output (the regression surface this test catches).
'plan-ceo-finding-count': ['plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-ceo-finding-count.test.ts'],
'plan-eng-finding-count': ['plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-eng-finding-count.test.ts'],
'plan-design-finding-count': ['plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-design-finding-count.test.ts'],
'plan-devex-finding-count': ['plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-devex-finding-count.test.ts'],
'plan-ceo-finding-count': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-ceo-finding-count.test.ts'],
'plan-eng-finding-count': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-eng-finding-count.test.ts'],
'plan-design-finding-count': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-design-finding-count.test.ts'],
'plan-devex-finding-count': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/claude-pty-runner.ts', 'test/skill-e2e-plan-devex-finding-count.test.ts'],
// Gate-tier reviewCount-floor counterparts. Catch the May 2026 transcript
// bug (model wrote a plan-mode plan and ExitPlanMode'd without firing any
// review-phase AskUserQuestion). Uses runPlanSkillFloorCheck — minimal
// "did agent fire ANY AUQ?" observer that exits early on first non-permission
// numbered-option render. ~1-3 min typical wall time per test, ~$2-6 total.
'plan-eng-finding-floor': ['plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-finding-floor.test.ts'],
'plan-ceo-finding-floor': ['plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts'],
'plan-design-finding-floor': ['plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-design-finding-floor.test.ts'],
'plan-devex-finding-floor': ['plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-devex-finding-floor.test.ts'],
'plan-eng-finding-floor': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-finding-floor.test.ts'],
'plan-ceo-finding-floor': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts'],
'plan-design-finding-floor': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-design-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-design-finding-floor.test.ts'],
'plan-devex-finding-floor': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-devex-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-devex-finding-floor.test.ts'],
// Multi-finding batching regression — periodic tier complement to the
// gate-tier finding-floor. Catches the May 2026 transcript shape where
// a model fires one AUQ then batches the rest into a "## Decisions to
// confirm" plan write. runPlanSkillFloorCheck cannot detect that shape
// (it exits on first AUQ); runPlanSkillCounting can.
'plan-eng-multi-finding-batching': ['plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-multi-finding-batching.test.ts'],
'plan-eng-multi-finding-batching': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-eng-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-eng-multi-finding-batching.test.ts'],
'plan-ceo-split-overflow': ['plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'bin/gstack-question-preference', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-split-overflow.test.ts'],
'brain-privacy-gate': ['scripts/resolvers/preamble/generate-brain-sync-block.ts', 'scripts/resolvers/preamble.ts', 'bin/gstack-brain-sync', 'bin/gstack-artifacts-init', 'bin/gstack-config', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-brain-privacy-gate.test.ts'],
'brain-privacy-gate': ['bin/gstack-skill-start', 'bin/gstack-skill-end', 'scripts/resolvers/preamble/generate-brain-sync-block.ts', 'scripts/resolvers/preamble.ts', 'bin/gstack-brain-sync', 'bin/gstack-artifacts-init', 'bin/gstack-config', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-brain-privacy-gate.test.ts'],
// /setup-gbrain Path 4 (Remote MCP) — happy + bad-token end-to-end via
// Agent SDK. Gate-tier (deterministic stub server, fixed inputs); fires
// when the skill template, the verify helper, the artifacts-init helper,
// or the detect script changes.
'setup-gbrain-remote': ['setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-artifacts-init', 'bin/gstack-gbrain-detect', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-remote.test.ts'],
'setup-gbrain-bad-token': ['setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-bad-token.test.ts'],
'setup-gbrain-remote': ['setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/claude-md-persist.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-artifacts-init', 'bin/gstack-gbrain-detect', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-remote.test.ts'],
'setup-gbrain-bad-token': ['setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-bad-token.test.ts'],
// v1.34.0.0 split-engine Path 4 + Step 4.5 Yes (local PGLite for code).
// Periodic-tier per codex #12 (AgentSDK harness is non-deterministic).
// Fires when the setup-gbrain template, install/verify/init helpers, or
// the agent-sdk-runner harness changes.
'setup-gbrain-path4-local-pglite': ['setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-gbrain-install', 'bin/gstack-gbrain-detect', 'lib/gbrain-local-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-path4-local-pglite.test.ts'],
'setup-gbrain-path4-local-pglite': ['setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/claude-md-persist.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-gbrain-install', 'bin/gstack-gbrain-detect', 'lib/gbrain-local-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-path4-local-pglite.test.ts'],
// AskUserQuestion format regression (RECOMMENDATION + Completeness: N/10)
// Fires when either template OR the two preamble resolvers change.
@@ -226,8 +229,8 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
'review-dashboard-via': ['ship/**', 'scripts/resolvers/review.ts', 'codex/**', 'autoplan/**', 'land-and-deploy/**', 'test/skill-e2e-review-attribution.test.ts'],
// Retro
'retro': ['retro/**', 'test/skill-e2e-retro.test.ts'],
'retro-base-branch': ['retro/**', 'test/skill-e2e-retro.test.ts'],
'retro': ['bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'],
'retro-base-branch': ['bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'],
// Global discover
'global-discover': ['bin/gstack-global-discover.ts', 'test/global-discover.test.ts'],
@@ -413,7 +416,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
// frontmatter. Touched by anything that changes resolver output, gen
// pipeline, detection helper, refresh subcommand, or the on-demand
// docs the resolver points to.
'office-hours-brain-writeback': [
'office-hours-brain-writeback': ['office-hours/sections/**',
'scripts/resolvers/gbrain.ts',
'scripts/gen-skill-docs.ts',
'bin/gstack-gbrain-detect',
@@ -538,6 +541,7 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
// Real-PTY E2E batch — tier classification:
// gate: cheap, deterministic, run on every PR
// periodic: long-running or expensive (>$3/run), run weekly
'preamble-script-ab': 'periodic', // Phase 1-3 A/B: script vs inline preamble; demoted post-Phase-3 (OV7)
'auq-format-gate': 'gate', // ~$0.50/run, SDK capture, single skill probe
'plan-ceo-mode-routing': 'periodic', // ~$3/run, deep navigation through 8-12 prior AskUserQuestions
'plan-design-with-ui-scope': 'gate', // ~$0.80/run
@@ -750,16 +754,16 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
* LLM-judge test touchfiles — keyed by test description string.
*/
export const LLM_JUDGE_TOUCHFILES: Record<string, string[]> = {
'command reference table': ['SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts'],
'snapshot flags reference': ['SKILL.md', 'SKILL.md.tmpl', 'browse/src/snapshot.ts'],
'browse/SKILL.md reference': ['browse/SKILL.md', 'browse/SKILL.md.tmpl', 'browse/src/**'],
'command reference table': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts'],
'snapshot flags reference': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/snapshot.ts'],
'browse/SKILL.md reference': ['browse/sections/**', 'browse/SKILL.md', 'browse/SKILL.md.tmpl', 'browse/src/**'],
'setup block': ['SKILL.md', 'SKILL.md.tmpl'],
'regression vs baseline': ['SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts', 'test/fixtures/eval-baselines.json'],
'qa/SKILL.md workflow': ['qa/SKILL.md', 'qa/SKILL.md.tmpl'],
'qa/SKILL.md health rubric': ['qa/SKILL.md', 'qa/SKILL.md.tmpl'],
'qa/SKILL.md anti-refusal': ['qa/SKILL.md', 'qa/SKILL.md.tmpl', 'qa-only/SKILL.md', 'qa-only/SKILL.md.tmpl'],
'regression vs baseline': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts', 'test/fixtures/eval-baselines.json'],
'qa/SKILL.md workflow': ['qa/sections/**', 'qa/SKILL.md', 'qa/SKILL.md.tmpl'],
'qa/SKILL.md health rubric': ['qa/sections/**', 'qa/SKILL.md', 'qa/SKILL.md.tmpl'],
'qa/SKILL.md anti-refusal': ['qa/sections/**', 'qa/SKILL.md', 'qa/SKILL.md.tmpl', 'qa-only/SKILL.md', 'qa-only/SKILL.md.tmpl'],
'cross-skill greptile consistency': ['review/SKILL.md', 'review/SKILL.md.tmpl', 'ship/SKILL.md', 'ship/SKILL.md.tmpl', 'review/greptile-triage.md', 'retro/SKILL.md', 'retro/SKILL.md.tmpl'],
'baseline score pinning': ['SKILL.md', 'SKILL.md.tmpl', 'test/fixtures/eval-baselines.json'],
'baseline score pinning': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'test/fixtures/eval-baselines.json'],
// Ship & Release
'ship/SKILL.md workflow': ['ship/SKILL.md', 'ship/SKILL.md.tmpl'],
@@ -781,13 +785,13 @@ export const LLM_JUDGE_TOUCHFILES: Record<string, string[]> = {
'office-hours/SKILL.md design sketch': ['office-hours/SKILL.md', 'office-hours/SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
// Deploy skills
'land-and-deploy/SKILL.md workflow': ['land-and-deploy/SKILL.md', 'land-and-deploy/SKILL.md.tmpl'],
'land-and-deploy/SKILL.md workflow': ['land-and-deploy/SKILL.md', 'land-and-deploy/SKILL.md.tmpl', 'land-and-deploy/sections/**'],
'canary/SKILL.md monitoring loop': ['canary/SKILL.md', 'canary/SKILL.md.tmpl'],
'benchmark/SKILL.md perf collection': ['benchmark/SKILL.md', 'benchmark/SKILL.md.tmpl'],
'setup-deploy/SKILL.md platform setup': ['setup-deploy/SKILL.md', 'setup-deploy/SKILL.md.tmpl'],
// Other skills
'retro/SKILL.md instructions': ['retro/SKILL.md', 'retro/SKILL.md.tmpl'],
'retro/SKILL.md instructions': ['retro/sections/**', 'retro/SKILL.md', 'retro/SKILL.md.tmpl'],
'qa-only/SKILL.md workflow': ['qa-only/SKILL.md', 'qa-only/SKILL.md.tmpl'],
'gstack-upgrade/SKILL.md upgrade flow': ['gstack-upgrade/SKILL.md', 'gstack-upgrade/SKILL.md.tmpl'],