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
+160 -790
View File
File diff suppressed because it is too large Load Diff
+87 -307
View File
@@ -65,11 +65,13 @@ When the user types `/retro`, run this skill.
{{GBRAIN_CONTEXT_LOAD}}
{{SECTION_INDEX:retro}}
## Instructions
Parse the argument to determine the time window. Default to 7 days if no argument given. All times should be reported in the user's **local timezone** (use the system default — do NOT set `TZ`).
**Midnight-aligned windows:** For day (`d`) and week (`w`) units, compute an absolute start date at local midnight, not a relative string. For example, if today is 2026-03-18 and the window is 7 days: the start date is 2026-03-11. Use `--since="2026-03-11T00:00:00"` for git log queries — the explicit `T00:00:00` suffix ensures git starts from midnight. Without it, git uses the current wall-clock time (e.g., `--since="2026-03-11"` at 11pm means 11pm, not midnight). For week units, multiply by 7 to get days (e.g., `2w` = 14 days back). For hour (`h`) units, use `--since="N hours ago"` since midnight alignment does not apply to sub-day windows.
**Midnight-aligned windows:** For day (`d`) and week (`w`) units, compute an absolute start date at local midnight, not a relative string. For example, if today is 2026-03-18 and the window is 7 days: the start date is 2026-03-11. Use `--since "2026-03-11T00:00:00"` — the explicit `T00:00:00` suffix ensures git starts from midnight. Without it, git uses the current wall-clock time (e.g., `--since "2026-03-11"` at 11pm means 11pm, not midnight). For week units, multiply by 7 to get days (e.g., `2w` = 14 days back). For hour (`h`) units, use `--since "N hours ago"` since midnight alignment does not apply to sub-day windows. Compute "today" from the user-visible `## currentDate` tag in the session reminder — NEVER from `date` (the system clock can be hours off in containerized harnesses). If you cannot reliably compute "today", stop and ask the user via AskUserQuestion rather than proceeding.
**Argument validation:** If the argument doesn't match a number followed by `d`, `h`, or `w`, the word `compare` (optionally followed by a window), or the word `global` (optionally followed by a window), show this usage and stop:
```
@@ -88,142 +90,89 @@ Usage: /retro [window | compare | global]
{{LEARNINGS_SEARCH}}
### Non-git context (optional)
### Step 0.5: Freshness pre-flight (fetch)
Check for non-git context that should be included in the retro:
Refresh `origin/<default>` so the retro doesn't misreport against a stale local ref. If the repo has no `origin` remote this fails harmlessly — the metrics script (Step 1) falls back to the local branch and its guard lines disclose it:
```bash
[ -f ~/.gstack/retro-context.md ] && echo "RETRO_CONTEXT_FOUND" || echo "NO_RETRO_CONTEXT"
git fetch origin <default> --quiet 2>/dev/null \
|| echo "RETRO_FETCH: failed (offline or no remote) — proceeding against last-known refs"
```
If `RETRO_CONTEXT_FOUND`: read `~/.gstack/retro-context.md`. This file is user-authored and may contain meeting notes, calendar events, decisions, and other context that doesn't appear in git history. Incorporate this context into the retro narrative where relevant.
Remember whether the fetch succeeded — the stale-base guard in Step 1 only BLOCKs when it did.
### Step 0.5: Stale-base + bad-today-anchor pre-flight guard
### Step 1: Gather Metrics (one command)
The retro skill computes a window from "today" and queries `git log --since=<window> origin/<default>`. If "today" drifts (model session-context error) or the local worktree's `origin/<default>` is materially behind the actual remote, the window can return zero or near-zero commits and the retro will fabricate a coherent-looking narrative from nothing. This guard prevents silent confidently-wrong output.
Run the pre-flight in this exact order. The first branch that matches wins:
All raw data gathering and metric computation runs through `gstack-retro-metrics` — one command instead of a dozen git pipelines. Substitute the base branch detected in Step 0 and the midnight-aligned start computed above:
```bash
# Pre-check A: no remote configured?
_RETRO_HAS_REMOTE=$(git remote 2>/dev/null | grep -c '^origin$' || echo 0)
if [ "$_RETRO_HAS_REMOTE" = "0" ]; then
echo "RETRO_GUARD: no 'origin' remote, base freshness not verified — proceeding"
_RETRO_GUARD_VERDICT="skip-no-remote"
fi
# Pre-check B: detached HEAD or no current base?
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
_RETRO_HEAD_REF=$(git symbolic-ref --quiet HEAD 2>/dev/null || echo "")
if [ -z "$_RETRO_HEAD_REF" ]; then
echo "RETRO_GUARD: detached HEAD, base freshness not verified — proceeding"
_RETRO_GUARD_VERDICT="skip-detached"
fi
fi
# Pre-check C: fetch origin <default>; if it fails, warn but proceed.
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
if ! git fetch origin <default> --quiet 2>/dev/null; then
echo "RETRO_GUARD: 'git fetch origin <default>' failed (offline?) — proceeding against last-known origin/<default>"
_RETRO_GUARD_VERDICT="warn-fetch-failed"
fi
fi
# Pre-check D: BLOCK only when fetch succeeded AND the latest origin/<default>
# commit predates the retro window. Today's date should be loaded from the
# user-visible "## currentDate" tag in the session reminder; if the gap between
# origin/<default>'s newest commit and today exceeds the window, the model's
# "today" is almost certainly stale (or the worktree is wildly behind).
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
_RETRO_LATEST_ISO=$(git log -1 --format=%ci origin/<default> 2>/dev/null | awk '{print $1}')
if [ -n "$_RETRO_LATEST_ISO" ]; then
# The model computes today from the session reminder (NEVER from `date` —
# the system clock can be hours off in containerized harnesses).
# Compute window in DAYS (default 7): if today - latest-commit-date > window-days,
# BLOCK. If the model cannot reliably compute "today", it MUST stop here and
# ask the user via AskUserQuestion rather than proceeding.
echo "RETRO_GUARD: latest origin/<default> commit on $_RETRO_LATEST_ISO"
_RETRO_GUARD_VERDICT="check-gap"
fi
fi
_RM="$HOME/.claude/skills/gstack/bin/gstack-retro-metrics"
[ -x "$_RM" ] || _RM=".claude/skills/gstack/bin/gstack-retro-metrics"
"$_RM" --base "<default>" --since "<since>" \
|| echo "RETRO_METRICS: unavailable — stale install (compute metrics manually from the steps below)"
```
After running the bash block, the model evaluates `RETRO_GUARD: latest origin/<default> commit on <DATE>` against today and the window:
Read the labeled `METRIC_NAME: value` lines — they feed every step below. **Degraded mode:** if `RETRO_METRICS_PROTO: 1` is missing from the output, the install is stale; compute each metric manually with git commands, using the metric definitions in Steps 2-11 as the spec.
- If the **latest-commit date is older than (today window-days)**, BLOCK with: "Retro window is stale. Latest commit on `origin/<default>` was `<DATE>`, but the window covers `<since>` to `<today>`. This usually means either (a) today's date is wrong in this session or (b) `origin/<default>` is materially behind the remote. Confirm today's date via the session reminder; if today is correct, run `git fetch origin <default>` manually and re-run /retro." Stop the skill until the user resolves.
- Otherwise, write: "RETRO_GUARD: latest commit `<DATE>` within window — proceeding."
**Identity:** `USER_NAME` is **"you"** — the person reading this retro. All other authors are teammates. Orient the narrative around this: "your" commits vs teammate contributions.
Skip paths (`skip-no-remote`, `skip-detached`, `warn-fetch-failed`) all proceed to Step 1 with the cited reason on a single stderr line so the retro narrative carries the disclosure ("offline run, window not freshness-verified") rather than silently misreporting.
**Stale-base + bad-today-anchor guard.** The script echoes `GUARD_LATEST_COMMIT: <DATE>` (newest commit on the analyzed ref). If "today" drifts (model session-context error) or the local `origin/<default>` is materially behind the remote, the window returns zero or near-zero commits and the retro would fabricate a coherent-looking narrative from nothing. Evaluate in this order:
### Step 1: Gather Raw Data
1. If `GUARD_REMOTE: none` or `GUARD_HEAD: detached` or the Step 0.5 fetch failed: proceed, but carry the disclosure into the narrative ("offline run, window not freshness-verified") rather than silently misreporting.
2. If the Step 0.5 fetch succeeded AND the `GUARD_LATEST_COMMIT` date is **older than (today window-days)**: BLOCK with: "Retro window is stale. Latest commit on `origin/<default>` was `<DATE>`, but the window covers `<since>` to `<today>`. This usually means either (a) today's date is wrong in this session or (b) `origin/<default>` is materially behind the remote. Confirm today's date via the session reminder; if today is correct, run `git fetch origin <default>` manually and re-run /retro." Stop the skill until the user resolves.
3. Otherwise, write: "RETRO_GUARD: latest commit `<DATE>` within window — proceeding."
First, fetch origin and identify the current user:
```bash
git fetch origin <default> --quiet
# Identify who is running the retro
git config user.name
git config user.email
```
Also check `RETRO_REF`: if it is not `origin/<default>` (local-only repo, missing remote branch), disclose which ref the retro analyzed.
The name returned by `git config user.name` is **"you"** — the person reading this retro. All other authors are teammates. Use this to orient the narrative: "your" commits vs teammate contributions.
**Metric line reference** (what the script emits):
Run ALL of these git commands in parallel (they are independent):
| Line | Meaning |
|------|---------|
| `COMMIT: hash\|author\|datetime\|+ins/-del\|subject` | One per commit, newest first (capped at 300) — the raw material for narrative anchoring |
| `COMMITS` / `MERGE_COMMITS` / `CONTRIBUTORS` | Window totals on the analyzed ref |
| `INSERTIONS` / `DELETIONS` / `NET_LOC` | Raw LOC |
| `LOGICAL_SLOC_ADDED` | Non-blank, non-comment added lines — the primary code-volume metric |
| `TEST_INSERTIONS` / `TEST_RATIO` | Test LOC (test/spec paths + .test./.spec. suffixes) and its share of insertions |
| `WEIGHTED_COMMITS` | Commits × files-touched, capped at 20 per commit |
| `ACTIVE_DAYS` | Distinct local dates with commits |
| `SESSIONS` / `DEEP_SESSIONS` / `MEDIUM_SESSIONS` / `MICRO_SESSIONS` | 45-minute-gap session detection: deep 50+ min, medium 20-50, micro <20 |
| `TOTAL_ACTIVE_MINUTES` / `AVG_SESSION_MINUTES` / `LOC_PER_SESSION_HOUR` | Session time aggregates (LOC/hour pre-rounded to nearest 50) |
| `COMMIT_TYPES` / `FIX_RATIO` | Conventional-commit prefix mix |
| `COMMIT_SIZE_BUCKETS` | small <100 / medium 100-500 / large 500-1500 / xl 1500+ LOC per commit |
| `HOURS` / `PEAK_HOUR` | Hourly commit histogram (local time), nonzero hours only |
| `FOCUS_SCORE` | % of file changes in the single busiest top-level directory |
| `BIGGEST_COMMIT` | Highest-LOC commit in the window (ship-of-the-week candidate) |
| `HOTSPOT: count file` | Top 10 most-changed files |
| `AUTHOR: name\|commits\|ins\|del\|test_ratio\|top_areas\|types\|peak_hour` | Per-contributor rollup, sorted by commits desc |
| `AUTHOR_BIGGEST: name\|hash\|loc\|subject` | Each contributor's biggest ship |
| `COAUTHOR: hash\|name` / `AI_ASSISTED_COMMITS` | Human co-author credit lines; count of commits with AI trailers |
| `WEEK: wN\|commits\|ins\|del\|test_ratio` | Weekly buckets, w0 = newest (for Step 10 trends) |
| `PR_REFS` / `PRS_REFERENCED` | PR/MR numbers from commit subjects (GitHub #NNN, GitLab !NNN) |
| `TEST_FILES_TOTAL` / `TEST_FILES_CHANGED` / `REGRESSION_TEST_COMMITS` / `REGRESSION_COMMIT` | Test health: repo-wide test file count, test files changed in window, `test(qa):` / `test(design):` / `test: coverage` commits |
| `VERSION_RANGE` | First → last VERSION file value in the window (when tracked) |
| `TEAM_STREAK` / `USER_STREAK` | Consecutive commit days with anchor date (Step 11) |
| `RETRO_CONTEXT` / `GREPTILE_HISTORY` / `TODOS_FILE` / `SKILL_USAGE_LOG` / `EUREKA_LOG` | Presence of optional inputs — Read the ones marked present |
```bash
# 1. All commits in window with timestamps, subject, hash, AUTHOR, files changed, insertions, deletions
git log origin/<default> --since="<window>" --format="%H|%aN|%ae|%ai|%s" --shortstat
**Optional inputs** (Read each file the script marks `present`):
# 2. Per-commit test vs total LOC breakdown with author
# Each commit block starts with COMMIT:<hash>|<author>, followed by numstat lines.
# Separate test files (matching test/|spec/|__tests__/) from production files.
git log origin/<default> --since="<window>" --format="COMMIT:%H|%aN" --numstat
# 3. Commit timestamps for session detection and hourly distribution (with author)
git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n
# 4. Files most frequently changed (hotspot analysis)
git log origin/<default> --since="<window>" --format="" --name-only | grep -v '^$' | sort | uniq -c | sort -rn
# 5. PR/MR numbers from commit messages (GitHub #NNN, GitLab !NNN)
git log origin/<default> --since="<window>" --format="%s" | grep -oE '[#!][0-9]+' | sort -t'#' -k1 | uniq
# 6. Per-author file hotspots (who touches what)
git log origin/<default> --since="<window>" --format="AUTHOR:%aN" --name-only
# 7. Per-author commit counts (quick summary)
git shortlog origin/<default> --since="<window>" -sn --no-merges
# 8. Greptile triage history (if available)
cat ~/.gstack/greptile-history.md 2>/dev/null || true
# 9. TODOS.md backlog (if available)
cat TODOS.md 2>/dev/null || true
# 10. Test file count
git ls-files 2>/dev/null | grep -E '(\.test\.|\.spec\.|_test\.|_spec\.)' | wc -l
# 11. Regression test commits in window
git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep="test(design):" --grep="test: coverage"
# 12. gstack skill usage telemetry (if available)
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
# 12. Test files changed in window
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
```
- `RETRO_CONTEXT: present` → Read `~/.gstack/retro-context.md`. It is user-authored and may contain meeting notes, calendar events, decisions, and other context that doesn't appear in git history. Incorporate it into the retro narrative where relevant.
- `GREPTILE_HISTORY: present` → Read `~/.gstack/greptile-history.md`. Filter entries to the retro window by date. Count by type: `fix`, `fp`, `already-fixed`. Signal ratio = `(fix + already-fixed) / (fix + already-fixed + fp)`. Skip unparseable lines silently; if no entries fall in the window, skip the Greptile metric row.
- `TODOS_FILE: present` → Read `TODOS.md`. Compute: total open TODOs (exclude the `## Completed` section), P0/P1 count, P2 count, items completed this period (Completed entries dated within the window), items added this period (cross-reference `COMMIT:` lines that touched TODOS.md).
- `SKILL_USAGE_LOG: present` → Read `~/.gstack/analytics/skill-usage.jsonl`. Filter to the window by `ts`. Separate skill activations (no `event` field) from hook fires (`event: "hook_fire"`). Aggregate by skill name.
- `EUREKA_LOG: present` → Read `~/.gstack/analytics/eureka.jsonl`. Filter to the window by `ts`. For each eureka moment note the skill that flagged it, the branch, and a one-line summary of the insight.
### Step 2: Compute Metrics
Calculate and present these metrics in a summary table:
Present these metrics in a summary table, straight from the metric lines:
| Metric | Value |
|--------|-------|
| **Features shipped** (from CHANGELOG + merged PR titles) | N |
| Commits to main | N |
| Weighted commits (commits × avg files-touched, capped at 20 per commit) | N |
| Weighted commits (`WEIGHTED_COMMITS`) | N |
| Contributors | N |
| PRs merged | N |
| **Logical SLOC added** (non-blank, non-comment — primary code-volume metric) | N |
| **Logical SLOC added** (`LOGICAL_SLOC_ADDED` — primary code-volume metric) | N |
| Raw LOC: insertions | N |
| Raw LOC: deletions | N |
| Raw LOC: net | N |
@@ -242,7 +191,7 @@ new functionality. Raw LOC is demoted to context because AI inflates it; ten
lines of a good fix is not less shipping than ten thousand lines of scaffold.
See docs/designs/PLAN_TUNING_V1.md §Workstream C.
Then show a **per-author leaderboard** immediately below:
Then show a **per-author leaderboard** immediately below, from the `AUTHOR:` lines:
```
Contributor Commits +/- Top area
@@ -251,49 +200,25 @@ alice 12 +800/-150 app/services/
bob 3 +120/-40 tests/
```
Sort by commits descending. The current user (from `git config user.name`) always appears first, labeled "You (name)".
Sort by commits descending. The current user (`USER_NAME`) always appears first, labeled "You (name)".
**Greptile signal (if history exists):** Read `~/.gstack/greptile-history.md` (fetched in Step 1, command 8). Filter entries within the retro time window by date. Count entries by type: `fix`, `fp`, `already-fixed`. Compute signal ratio: `(fix + already-fixed) / (fix + already-fixed + fp)`. If no entries exist in the window or the file doesn't exist, skip the Greptile metric row. Skip unparseable lines silently.
Conditional rows (skip each when its input is absent or empty in the window):
**Backlog Health (if TODOS.md exists):** Read `TODOS.md` (fetched in Step 1, command 9). Compute:
- Total open TODOs (exclude items in `## Completed` section)
- P0/P1 count (critical/urgent items)
- P2 count (important items)
- Items completed this period (items in Completed section with dates within the retro window)
- Items added this period (cross-reference git log for commits that modified TODOS.md within the window)
Include in the metrics table:
```
| Backlog Health | N open (X P0/P1, Y P2) · Z completed this period |
```
If TODOS.md doesn't exist, skip the Backlog Health row.
**Skill Usage (if analytics exist):** Read `~/.gstack/analytics/skill-usage.jsonl` if it exists. Filter entries within the retro time window by `ts` field. Separate skill activations (no `event` field) from hook fires (`event: "hook_fire"`). Aggregate by skill name. Present as:
```
| Skill Usage | /ship(12) /qa(8) /review(5) · 3 safety hook fires |
```
If the JSONL file doesn't exist or has no entries in the window, skip the Skill Usage row.
**Eureka Moments (if logged):** Read `~/.gstack/analytics/eureka.jsonl` if it exists. Filter entries within the retro time window by `ts` field. For each eureka moment, show the skill that flagged it, the branch, and a one-line summary of the insight. Present as:
```
| Eureka Moments | 2 this period |
```
If moments exist, list them:
If eureka moments exist, list them:
```
EUREKA /office-hours (branch: garrytan/auth-rethink): "Session tokens don't need server storage — browser crypto API makes client-side JWT validation viable"
EUREKA /plan-eng-review (branch: garrytan/cache-layer): "Redis isn't needed here — Bun's built-in LRU cache handles this workload"
```
If the JSONL file doesn't exist or has no entries in the window, skip the Eureka Moments row.
### Step 3: Commit Time Distribution
Show hourly histogram in local time using bar chart:
Render the `HOURS` line as an hourly histogram in local time:
```
Hour Commits ████████████████
@@ -310,24 +235,14 @@ Identify and call out:
### Step 4: Work Session Detection
Detect sessions using **45-minute gap** threshold between consecutive commits. For each session report:
- Start/end time (Pacific)
- Number of commits
- Duration in minutes
Classify sessions:
- **Deep sessions** (50+ min)
- **Medium sessions** (20-50 min)
- **Micro sessions** (<20 min, typically single-commit fire-and-forget)
Calculate:
- Total active coding time (sum of session durations)
- Average session length
- LOC per hour of active time
Sessions are pre-computed with a **45-minute gap** threshold between consecutive commits (`SESSIONS`, `DEEP_SESSIONS` 50+ min, `MEDIUM_SESSIONS` 20-50 min, `MICRO_SESSIONS` <20 min — typically single-commit fire-and-forget). Report:
- Session count and the deep/medium/micro split
- Total active coding time (`TOTAL_ACTIVE_MINUTES`) and average session length
- LOC per hour of active time (`LOC_PER_SESSION_HOUR`)
### Step 5: Commit Type Breakdown
Categorize by conventional commit prefix (feat/fix/refactor/test/chore/docs). Show as percentage bar:
Render `COMMIT_TYPES` (feat/fix/refactor/test/chore/docs) as a percentage bar:
```
feat: 20 (40%) ████████████████████
@@ -335,18 +250,18 @@ fix: 27 (54%) ███████████████████
refactor: 2 ( 4%) ██
```
Flag if fix ratio exceeds 50% — this signals a "ship fast, fix fast" pattern that may indicate review gaps.
Flag if `FIX_RATIO` exceeds 50% — this signals a "ship fast, fix fast" pattern that may indicate review gaps.
### Step 6: Hotspot Analysis
Show top 10 most-changed files. Flag:
Show the `HOTSPOT` lines (top 10 most-changed files). Flag:
- Files changed 5+ times (churn hotspots)
- Test files vs production files in the hotspot list
- VERSION/CHANGELOG frequency (version discipline indicator)
### Step 7: PR Size Distribution
From commit diffs, estimate PR sizes and bucket them:
Report `COMMIT_SIZE_BUCKETS`:
- **Small** (<100 LOC)
- **Medium** (100-500 LOC)
- **Large** (500-1500 LOC)
@@ -354,23 +269,16 @@ From commit diffs, estimate PR sizes and bucket them:
### Step 8: Focus Score + Ship of the Week
**Focus score:** Calculate the percentage of commits touching the single most-changed top-level directory (e.g., `app/services/`, `app/views/`). Higher score = deeper focused work. Lower score = scattered context-switching. Report as: "Focus score: 62% (app/services/)"
**Focus score:** `FOCUS_SCORE` is the percentage of file changes touching the single most-changed top-level directory (e.g., `app/services/`). Higher score = deeper focused work. Lower score = scattered context-switching. Report as: "Focus score: 62% (app/services/)"
**Ship of the week:** Auto-identify the single highest-LOC PR in the window. Highlight it:
- PR number and title
**Ship of the week:** `BIGGEST_COMMIT` is the highest-LOC change in the window. Highlight it:
- PR number (match against `PR_REFS` / the subject) and title
- LOC changed
- Why it matters (infer from commit messages and files touched)
### Step 9: Team Member Analysis
For each contributor (including the current user), compute:
1. **Commits and LOC** — total commits, insertions, deletions, net LOC
2. **Areas of focus** — which directories/files they touched most (top 3)
3. **Commit type mix** — their personal feat/fix/refactor/test breakdown
4. **Session patterns** — when they code (their peak hours), session count
5. **Test discipline** — their personal test LOC ratio
6. **Biggest ship** — their single highest-impact commit or PR in the window
For each contributor (including the current user), the `AUTHOR:` line carries commits, insertions, deletions, test ratio, top areas, commit type mix, and peak hour; `AUTHOR_BIGGEST:` carries their single highest-impact commit. Use the `COMMIT:` lines to anchor everything in actual work.
**For the current user ("You"):** This section gets the deepest treatment. Include all the detail from the solo retro — session analysis, time patterns, focus score. Frame it in first person: "Your peak hours...", "Your biggest ship..."
@@ -381,7 +289,7 @@ For each contributor (including the current user), compute:
**If only one contributor (solo repo):** Skip the team breakdown and proceed as before — the retro is personal.
**If there are Co-Authored-By trailers:** Parse `Co-Authored-By:` lines in commit messages. Credit those authors for the commit alongside the primary author. Note AI co-authors (e.g., `noreply@anthropic.com`) but do not include them as team members — instead, track "AI-assisted commits" as a separate metric.
**Co-author credit:** `COAUTHOR:` lines carry human `Co-Authored-By:` trailers — credit those authors for the commit alongside the primary author. AI co-authors (e.g., `noreply@anthropic.com`) are counted in `AI_ASSISTED_COMMITS` instead track "AI-assisted commits" as a separate metric, never as a team member.
{{LEARNINGS_LOG}}
@@ -389,28 +297,17 @@ For each contributor (including the current user), compute:
### Step 10: Week-over-Week Trends (if window >= 14d)
If the time window is 14 days or more, split into weekly buckets and show trends:
- Commits per week (total and per-author)
If the time window is 14 days or more, use the `WEEK:` lines (w0 = the week containing the newest commit) to show trends:
- Commits per week (total; per-author from the `COMMIT:` lines)
- LOC per week
- Test ratio per week
- Fix ratio per week
- Session count per week
### Step 11: Streak Tracking
Count consecutive days with at least 1 commit to origin/<default>, going back from today. Track both team streak and personal streak:
```bash
# Team streak: all unique commit dates (local time) — no hard cutoff
git log origin/<default> --format="%ad" --date=format:"%Y-%m-%d" | sort -u
# Personal streak: only the current user's commits
git log origin/<default> --author="<user_name>" --format="%ad" --date=format:"%Y-%m-%d" | sort -u
```
Count backward from today — how many consecutive days have at least one commit? This queries the full history so streaks of any length are reported accurately. Display both:
- "Team shipping streak: 47 consecutive days"
- "Your shipping streak: 32 consecutive days"
`TEAM_STREAK` and `USER_STREAK` count consecutive days with at least 1 commit (full history, no cutoff), anchored at the **newest commit date** — not at today, because the script never trusts the system clock. Interpret against today from the session reminder:
- If the anchor date is today or yesterday, the streak is live: "Team shipping streak: 47 consecutive days" / "Your shipping streak: 32 consecutive days"
- If the anchor is older, the streak is broken: report 0 days and note the last shipping day.
### Step 12: Load History & Compare
@@ -492,7 +389,7 @@ Use the Write tool to save the JSON file with this schema:
}
```
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). If any has no data, omit the field entirely.
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (`TEST_FILES_TOTAL` > 0). If any has no data, omit the field entirely.
Include test health data in the JSON when test files exist:
```json
@@ -517,124 +414,7 @@ Include backlog data in the JSON when TODOS.md exists:
### Step 14: Write the Narrative
Structure the output as:
---
**Tweetable summary** (first line, before everything else):
```
Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm | Streak: 47d
```
## Engineering Retro: [date range]
### Summary Table
(from Step 2)
### Trends vs Last Retro
(from Step 11, loaded before save — skip if first retro)
### Time & Session Patterns
(from Steps 3-4)
Narrative interpreting what the team-wide patterns mean:
- When the most productive hours are and what drives them
- Whether sessions are getting longer or shorter over time
- Estimated hours per day of active coding (team aggregate)
- Notable patterns: do team members code at the same time or in shifts?
### Shipping Velocity
(from Steps 5-7)
Narrative covering:
- Commit type mix and what it reveals
- PR size distribution and what it reveals about shipping cadence
- Fix-chain detection (sequences of fix commits on the same subsystem)
- Version bump discipline
### Code Quality Signals
- Test LOC ratio trend
- Hotspot analysis (are the same files churning?)
- Greptile signal ratio and trend (if history exists): "Greptile: X% signal (Y valid catches, Z false positives)"
### Test Health
- Total test files: N (from command 10)
- Tests added this period: M (from command 12 — test files changed)
- Regression test commits: list `test(qa):` and `test(design):` and `test: coverage` commits from command 11
- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
### Plan Completion
Check review JSONL logs for plan completion data from /ship runs this period:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
```
If plan completion data exists within the retro time window:
- Count branches shipped with plans (entries that have `plan_items_total` > 0)
- Compute average completion: sum of `plan_items_done` / sum of `plan_items_total`
- Identify most-skipped item category if data supports it
Output:
```
Plan Completion This Period:
{N} branches shipped with plans
Average completion: {X}% ({done}/{total} items)
```
If no plan data exists, skip this section silently.
### Focus & Highlights
(from Step 8)
- Focus score with interpretation
- Ship of the week callout
### Your Week (personal deep-dive)
(from Step 9, for the current user only)
This is the section the user cares most about. Include:
- Their personal commit count, LOC, test ratio
- Their session patterns and peak hours
- Their focus areas
- Their biggest ship
- **What you did well** (2-3 specific things anchored in commits)
- **Where to level up** (1-2 specific, actionable suggestions)
### Team Breakdown
(from Step 9, for each teammate — skip if solo repo)
For each teammate (sorted by commits descending), write a section:
#### [Name]
- **What they shipped**: 2-3 sentences on their contributions, areas of focus, and commit patterns
- **Praise**: 1-2 specific things they did well, anchored in actual commits. Be genuine — what would you actually say in a 1:1? Examples:
- "Cleaned up the entire auth module in 3 small, reviewable PRs — textbook decomposition"
- "Added integration tests for every new endpoint, not just happy paths"
- "Fixed the N+1 query that was causing 2s load times on the dashboard"
- **Opportunity for growth**: 1 specific, constructive suggestion. Frame as investment, not criticism. Examples:
- "Test coverage on the payment module is at 8% — worth investing in before the next feature lands on top of it"
- "Most commits land in a single burst — spacing work across the day could reduce context-switching fatigue"
- "All commits land between 1-4am — sustainable pace matters for code quality long-term"
**AI collaboration note:** If many commits have `Co-Authored-By` AI trailers (e.g., Claude, Copilot), note the AI-assisted commit percentage as a team metric. Frame it neutrally — "N% of commits were AI-assisted" — without judgment.
### Top 3 Team Wins
Identify the 3 highest-impact things shipped in the window across the whole team. For each:
- What it was
- Who shipped it
- Why it matters (product/architecture impact)
### 3 Things to Improve
Specific, actionable, anchored in actual commits. Mix personal and team-level suggestions. Phrase as "to get even better, the team could..."
### 3 Habits for Next Week
Small, practical, realistic. Each must be something that takes <5 minutes to adopt. At least one should be team-oriented (e.g., "review each other's PRs same-day").
### Week-over-Week Trends
(if applicable, from Step 10)
{{SECTION:report-format}}
---
@@ -931,8 +711,8 @@ Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.jso
When the user runs `/retro compare` (or `/retro compare 14d`):
1. Compute metrics for the current window (default 7d) using the midnight-aligned start date (same logic as the main retro — e.g., if today is 2026-03-18 and window is 7d, use `--since="2026-03-11T00:00:00"`)
2. Compute metrics for the immediately prior same-length window using both `--since` and `--until` with midnight-aligned dates to avoid overlap (e.g., for a 7d window starting 2026-03-11: prior window is `--since="2026-03-04T00:00:00" --until="2026-03-11T00:00:00"`)
1. Run Steps 0.5-1 for the current window (default 7d) using the midnight-aligned start date (same logic as the main retro — e.g., if today is 2026-03-18 and window is 7d, `--since "2026-03-11T00:00:00"`)
2. Run `gstack-retro-metrics` a second time for the immediately prior same-length window, using both `--since` and `--until` with midnight-aligned dates to avoid overlap (e.g., for a 7d window starting 2026-03-11: `--since "2026-03-04T00:00:00" --until "2026-03-11T00:00:00"`)
3. Show a side-by-side comparison table with deltas and arrows
4. Write a brief narrative highlighting the biggest improvements and regressions
5. Save only the current-window snapshot to `.context/retros/` (same as a normal retro run); do **not** persist the prior-window metrics.
@@ -953,10 +733,10 @@ When the user runs `/retro compare` (or `/retro compare 14d`):
## Important Rules
- ALL narrative output goes directly to the user in the conversation. The ONLY file written is the `.context/retros/` JSON snapshot.
- Use `origin/<default>` for all git queries (not local main which may be stale)
- The metrics script analyzes `origin/<default>` (not local main which may be stale); when `RETRO_REF` says otherwise, disclose it
- Display all timestamps in the user's local timezone (do not override `TZ`)
- If the window has zero commits, say so and suggest a different window
- Round LOC/hour to nearest 50
- If `COMMITS: 0`, say so and suggest a different window
- Round LOC/hour to nearest 50 (the script pre-rounds `LOC_PER_SESSION_HOUR`)
- Treat merge commits as PR boundaries
- Do not read CLAUDE.md or other docs — this skill is self-contained
- On first run (no prior retros), skip comparison sections gracefully
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://gstack.dev/schemas/section-manifest.json",
"skill": "retro",
"version": 1,
"note": "PASSIVE registry (v2 plan T9 / CM2). id/file/title/trigger text ONLY. The skeleton's decision-tree prose decides WHEN to read. No machine predicate here.",
"sections": [
{
"id": "report-format",
"file": "report-format.md",
"title": "Narrative report structure — tweetable summary, section-by-section output template, Test Health, Plan Completion, personal deep-dive, team breakdown, wins/improvements/habits (Step 14)",
"trigger": "writing the retrospective narrative (Step 14, after all metrics are computed and compared)"
}
]
}
+120
View File
@@ -0,0 +1,120 @@
<!-- AUTO-GENERATED from report-format.md.tmpl — do not edit directly -->
<!-- Regenerate: bun run gen:skill-docs -->
Structure the output as:
---
**Tweetable summary** (first line, before everything else):
```
Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm | Streak: 47d
```
## Engineering Retro: [date range]
### Summary Table
(from Step 2)
### Trends vs Last Retro
(from Step 12, loaded before save — skip if first retro)
### Time & Session Patterns
(from Steps 3-4)
Narrative interpreting what the team-wide patterns mean:
- When the most productive hours are and what drives them
- Whether sessions are getting longer or shorter over time
- Estimated hours per day of active coding (team aggregate)
- Notable patterns: do team members code at the same time or in shifts?
### Shipping Velocity
(from Steps 5-7)
Narrative covering:
- Commit type mix and what it reveals
- PR size distribution and what it reveals about shipping cadence
- Fix-chain detection (sequences of fix commits on the same subsystem)
- Version bump discipline
### Code Quality Signals
- Test LOC ratio trend
- Hotspot analysis (are the same files churning?)
- Greptile signal ratio and trend (if history exists): "Greptile: X% signal (Y valid catches, Z false positives)"
### Test Health
- Total test files: N (`TEST_FILES_TOTAL`)
- Tests added this period: M (`TEST_FILES_CHANGED` — test files changed in the window)
- Regression test commits: list the `REGRESSION_COMMIT` lines (`test(qa):`, `test(design):`, and `test: coverage` commits)
- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
### Plan Completion
Check review JSONL logs for plan completion data from /ship runs this period:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
```
If plan completion data exists within the retro time window:
- Count branches shipped with plans (entries that have `plan_items_total` > 0)
- Compute average completion: sum of `plan_items_done` / sum of `plan_items_total`
- Identify most-skipped item category if data supports it
Output:
```
Plan Completion This Period:
{N} branches shipped with plans
Average completion: {X}% ({done}/{total} items)
```
If no plan data exists, skip this section silently.
### Focus & Highlights
(from Step 8)
- Focus score with interpretation
- Ship of the week callout
### Your Week (personal deep-dive)
(from Step 9, for the current user only)
This is the section the user cares most about. Include:
- Their personal commit count, LOC, test ratio
- Their session patterns and peak hours
- Their focus areas
- Their biggest ship
- **What you did well** (2-3 specific things anchored in commits)
- **Where to level up** (1-2 specific, actionable suggestions)
### Team Breakdown
(from Step 9, for each teammate — skip if solo repo)
For each teammate (sorted by commits descending), write a section:
#### [Name]
- **What they shipped**: 2-3 sentences on their contributions, areas of focus, and commit patterns
- **Praise**: 1-2 specific things they did well, anchored in actual commits. Be genuine — what would you actually say in a 1:1? Examples:
- "Cleaned up the entire auth module in 3 small, reviewable PRs — textbook decomposition"
- "Added integration tests for every new endpoint, not just happy paths"
- "Fixed the N+1 query that was causing 2s load times on the dashboard"
- **Opportunity for growth**: 1 specific, constructive suggestion. Frame as investment, not criticism. Examples:
- "Test coverage on the payment module is at 8% — worth investing in before the next feature lands on top of it"
- "Most commits land in a single burst — spacing work across the day could reduce context-switching fatigue"
- "All commits land between 1-4am — sustainable pace matters for code quality long-term"
**AI collaboration note:** If many commits have `Co-Authored-By` AI trailers (e.g., Claude, Copilot), note the AI-assisted commit percentage as a team metric. Frame it neutrally — "N% of commits were AI-assisted" — without judgment.
### Top 3 Team Wins
Identify the 3 highest-impact things shipped in the window across the whole team. For each:
- What it was
- Who shipped it
- Why it matters (product/architecture impact)
### 3 Things to Improve
Specific, actionable, anchored in actual commits. Mix personal and team-level suggestions. Phrase as "to get even better, the team could..."
### 3 Habits for Next Week
Small, practical, realistic. Each must be something that takes <5 minutes to adopt. At least one should be team-oriented (e.g., "review each other's PRs same-day").
### Week-over-Week Trends
(if applicable, from Step 10)
+118
View File
@@ -0,0 +1,118 @@
Structure the output as:
---
**Tweetable summary** (first line, before everything else):
```
Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm | Streak: 47d
```
## Engineering Retro: [date range]
### Summary Table
(from Step 2)
### Trends vs Last Retro
(from Step 12, loaded before save — skip if first retro)
### Time & Session Patterns
(from Steps 3-4)
Narrative interpreting what the team-wide patterns mean:
- When the most productive hours are and what drives them
- Whether sessions are getting longer or shorter over time
- Estimated hours per day of active coding (team aggregate)
- Notable patterns: do team members code at the same time or in shifts?
### Shipping Velocity
(from Steps 5-7)
Narrative covering:
- Commit type mix and what it reveals
- PR size distribution and what it reveals about shipping cadence
- Fix-chain detection (sequences of fix commits on the same subsystem)
- Version bump discipline
### Code Quality Signals
- Test LOC ratio trend
- Hotspot analysis (are the same files churning?)
- Greptile signal ratio and trend (if history exists): "Greptile: X% signal (Y valid catches, Z false positives)"
### Test Health
- Total test files: N (`TEST_FILES_TOTAL`)
- Tests added this period: M (`TEST_FILES_CHANGED` — test files changed in the window)
- Regression test commits: list the `REGRESSION_COMMIT` lines (`test(qa):`, `test(design):`, and `test: coverage` commits)
- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
### Plan Completion
Check review JSONL logs for plan completion data from /ship runs this period:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
```
If plan completion data exists within the retro time window:
- Count branches shipped with plans (entries that have `plan_items_total` > 0)
- Compute average completion: sum of `plan_items_done` / sum of `plan_items_total`
- Identify most-skipped item category if data supports it
Output:
```
Plan Completion This Period:
{N} branches shipped with plans
Average completion: {X}% ({done}/{total} items)
```
If no plan data exists, skip this section silently.
### Focus & Highlights
(from Step 8)
- Focus score with interpretation
- Ship of the week callout
### Your Week (personal deep-dive)
(from Step 9, for the current user only)
This is the section the user cares most about. Include:
- Their personal commit count, LOC, test ratio
- Their session patterns and peak hours
- Their focus areas
- Their biggest ship
- **What you did well** (2-3 specific things anchored in commits)
- **Where to level up** (1-2 specific, actionable suggestions)
### Team Breakdown
(from Step 9, for each teammate — skip if solo repo)
For each teammate (sorted by commits descending), write a section:
#### [Name]
- **What they shipped**: 2-3 sentences on their contributions, areas of focus, and commit patterns
- **Praise**: 1-2 specific things they did well, anchored in actual commits. Be genuine — what would you actually say in a 1:1? Examples:
- "Cleaned up the entire auth module in 3 small, reviewable PRs — textbook decomposition"
- "Added integration tests for every new endpoint, not just happy paths"
- "Fixed the N+1 query that was causing 2s load times on the dashboard"
- **Opportunity for growth**: 1 specific, constructive suggestion. Frame as investment, not criticism. Examples:
- "Test coverage on the payment module is at 8% — worth investing in before the next feature lands on top of it"
- "Most commits land in a single burst — spacing work across the day could reduce context-switching fatigue"
- "All commits land between 1-4am — sustainable pace matters for code quality long-term"
**AI collaboration note:** If many commits have `Co-Authored-By` AI trailers (e.g., Claude, Copilot), note the AI-assisted commit percentage as a team metric. Frame it neutrally — "N% of commits were AI-assisted" — without judgment.
### Top 3 Team Wins
Identify the 3 highest-impact things shipped in the window across the whole team. For each:
- What it was
- Who shipped it
- Why it matters (product/architecture impact)
### 3 Things to Improve
Specific, actionable, anchored in actual commits. Mix personal and team-level suggestions. Phrase as "to get even better, the team could..."
### 3 Habits for Next Week
Small, practical, realistic. Each must be something that takes <5 minutes to adopt. At least one should be team-oriented (e.g., "review each other's PRs same-day").
### Week-over-Week Trends
(if applicable, from Step 10)