Skip to content

v1.75.0.0 feat: ponytail import wave — simplification review lens, arm benchmark, reuse ladder, instruction-tier digest - #2722

Merged
garrytan merged 64 commits into
mainfrom
ponytail-inspiration-review
Aug 29, 2026
Merged

garrytan merged 64 commits into
mainfrom
ponytail-inspiration-review

Conversation

@garrytan

@garrytan garrytan commented Aug 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

The ponytail import wave: everything worth stealing from the ponytail code-minimalism ruleset, imported without its build-less posture (Completeness governs coverage; the ladder governs structure). Plan: 8 workstreams, approved via CEO → outside-voice ×3 → Eng → DX reviews (40 findings absorbed pre-implementation).

Review army: simplification lens (WS1)

  • 8th specialist with ponytail's closed tag vocabulary (delete:/stdlib:/native:/speculative:/shrink:), advisory end to end: excluded from quality score and findings header, ASK-only in Fix-First, [ADVISORY] labels, parent-printed net: -N lines possible footer, Simplification: lean already — nothing to cut. zero-findings line. Precision-guarded by a false-flag fixture eval. Dogfooded on this branch's own diff: net: -37 lines possible.
  • Advisory findings count in specialist stats (else the all-advisory lens would auto-gate itself into silence).
  • gstack-shortcut(dec-*) markers suppress Completeness findings only when the decision id resolves in the ledger — an orphan marker is reported as a forged suppression (cross-model adversarial catch).

Behavioral preamble (WS3, WS7, WS6)

  • Reuse ladder in Search Before Building (repo → stdlib → native platform → installed dep, "then build the complete version of what remains") + root-cause rule.
  • Bounded closer for completion reports, with report-shaped skills exempt.
  • AskUserQuestion repetition cut: −236 B/skill (~9.7 KB corpus), landed ONLY after a live NOT-WORSE A/B (post-cut 7/7 format elements, substance equal to pre-cut; the gate outranks the approval). Terse-mode savings claim corrected to the measured 2.6 KB.

Arm benchmark (WS2) — periodic research instrument, not a gate

  • 3 build-shaped tasks × 2 arms (with/without the behavioral-layer skill) through real claude -p sessions; staged diff judged 0–3 for over-engineering; per-cell functional oracle (checks=pass|fail|none) so refusals, broken code, and working code stay distinguishable; full failure taxonomy (judge_error surfaced, zero-diff cells valid); per-call random sentinels around untrusted diffs; harvest against a recorded seed SHA immune to commit-and-push disobedience; free selftest runs on every PR (test/arm-benchmark-selftest.test.ts).

Shortcut-debt ledger (WS4)

  • Accepted Completeness ≤ 7 durable-scope choices log the ceiling + upgrade trigger to the decision ledger and mark cut corners with gstack-shortcut(dec-<id>); /retro Step 11.5 harvests, joins on decision ids, tags unlinked/no-trigger rot; markers survive the redaction engine (pinned).

Instruction-only host tier (WS5)

  • Committed, generated, 2,048-byte-capped agents-digest/gstack-AGENTS.md; setup explainer arms print its script-anchored path; setup can never write a user's AGENTS.md (tripwire covers cp/ln/mv/tee/install/rsync/dd/truncate, >> and laundered variable-destination writes); digest ladder text is lockstep-pinned to the preamble resolver; README host table fixed to match setup.

/autoplan Eng-terminal reorder (WS8)

  • CEO → Design (if UI scope) → DX (if dev-facing scope) → Eng always last; single final gate; wrong premises queue as User Challenges (accepting one at the gate amends the plan and re-runs Eng); static order pin in test/autoplan-phase-order.test.ts.

Release-path integrity (found by this ship's own review army)

  • gstack-version-bump write --regen-digest (explicit opt-in, never presence-sniffed code exec) regenerates the version-stamped digest in the same mutation — without this, every future release commit of this repo failed the freshness CI check. Ship + land-and-deploy evidence gates allow-list the digest. Proven live by this branch's own v1.75.0.0 bump.

Test-runner + sandbox (infrastructure)

  • GSTACK_FREE_JOBS (digits-only, loud on garbage) and opt-in GSTACK_FREE_RETRY_FLAKY=1 serial retry for syscall-supervised sandboxes; retry vetoes on ANY unattributable failure evidence (headerless failures, unhandled errors, truncated runs); empty shards carry failingFiles: [].
  • scripts/sandbox-doctor.sh: one idempotent command makes a Vercel/Conductor cloud sandbox run the suite green (atomic git-shim patch + backup, loud on pattern drift, :99-socket Xvfb detection, dnf-gated installs, survives missing /dev/shm).
  • gstack-next-version distrusts laundered git twice over: origin-unconfigured guard AND a configured origin advertising zero heads falls back to local refs with a warning (both shim shapes pinned by PATH-shim regression tests).
  • Browse TEMP_DIRS portability with the remote-serving TEMP_ONLY asymmetry test-enforced; an untrustable TMPDIR (/, $HOME, cwd ancestor) is ignored.

Caught up to main (v1.74.0.0, the perth-v2 test/CI overhaul) and rebumped to v1.75.0.0 per the queue invariant. Merge integration fixes: callJudge combines main's eval-model routing with this branch's judge opts; the agents-digest hook honors main's --out-dir outputs-only rule; and duration-packed shard walls keep the historical per-file floor (the committed duration seed is recorded on fast CI — a syscall-supervised sandbox replays the same shard 2-4x slower and was wall-killed at predicted×3 while genuinely progressing).

Test Coverage

Tests: 568 → 573 files (+5 new: 4 coverage files + the freed arm-benchmark selftest). Coverage gate: PASS (85%, min 60 / target 80).

PONYTAIL-IMPORT WAVE (8 workstreams) + SANDBOX-GREEN
│
├─ WS1 Simplification specialist
│   ├─ specialists/simplification.md (closed vocab, NO FINDINGS)   [★ TESTED] gen-skill-docs pins
│   ├─ review-army.ts dispatch rule 8 + --simplification flag      [★ TESTED] rendered-contract pins
│   ├─ advisory carve-out (score excl., ASK-only, net: footer)     [★ TESTED] + [→EVAL] review-army-simplification (periodic)
│   └─ false-flag precision (lean diff → NO FINDINGS)              [→EVAL ★ TESTED] review-army-simplification-precision
├─ WS2 Arm benchmark
│   ├─ armJudge validation/zero-diff/bounded retry (error paths)   [★ TESTED] free selftest
│   ├─ buildArmJudgePrompt injection framing (random sentinels)    [★ TESTED] free selftest
│   ├─ harness: arm asymmetry, diff capture, functional oracles    [★ TESTED] free selftest
│   ├─ paid A/B cells (research instrument)                        [→EVAL ★ TESTED] periodic ×3
│   └─ eval-store schema v2 (harvest fields, tokens_used)          [★ TESTED] schema pins
├─ WS3 Reuse ladder resolver                                       [★ TESTED] goldens + digest lockstep pin
├─ WS4 Shortcut markers (writer grammar, retro harvest, redaction) [★ TESTED — NEW] shortcut-debt-ledger.test.ts
├─ WS5 Agents digest (freshness, 2KB budget, writer tripwires)     [★ TESTED] agents-digest.test.ts
├─ WS6 AUQ repetition cut (14 format pins + NOT-WORSE A/B)         [★ TESTED] + [→EVAL] auq-repetition-cut-ab
├─ WS7 Bounded closer                                              [★ TESTED] extraction pin + goldens
├─ WS8 Autoplan reorder (Eng last, single gate, B2 path)           [★ TESTED] autoplan-phase-order.test.ts
└─ Sandbox-green
    ├─ fullSuiteJobs strict parsing + failingFiles attribution     [★ TESTED — NEW] test-free-shards-sandbox-knobs.test.ts
    ├─ retry veto on unattributable evidence (mixed/truncated)     [★ TESTED — NEW]
    ├─ next-version laundering shims (both configurations)         [★ TESTED — NEW] PATH-shim regression pins
    ├─ browse TEMP_DIRS + remote TEMP_ONLY asymmetry + TMPDIR trust[★ TESTED — NEW] browse/test/temp-dirs.test.ts
    ├─ version-bump digest regen (stub + REAL generator round-trip)[★ TESTED — NEW]
    └─ sandbox-doctor shell guards                                 [★ TESTED — NEW] sandbox-doctor-shell.test.ts

COVERAGE: 28/33 paths (85%) — accepted gaps: flaky-retry orchestration inline in main() (opt-in, default OFF), recordE2E tokens_used one-liner, callJudge opts passthrough, gen-skill-docs fail-open branch

Pre-Landing Review

Full review army (checklist + 7 specialists + red team), 3 cycles to convergence, then a cross-model adversarial pass. 45+ findings, 5 critical — all fixed on-branch:

  1. [CRITICAL] Version-stamped digest went stale on every bump (api-contract + checklist, multi-specialist) → --regen-digest opt-in in gstack-version-bump write; evidence gates allow-list the digest.
  2. [CRITICAL] Presence-sniffed code exec in the first fix (security) → regen only under the explicit flag; plain write is side-effect-free; trust envelope documented.
  3. [CRITICAL] FreeShardOutcome.failingFiles missing on the empty-shard path (api-contract, tsc receipt) → fixed + pinned.
  4. [CRITICAL] Flaky-retry masked unattributable failures (red-team + checklist) → unattributedFailures veto + mixed-shard/truncation pins.
  5. [CRITICAL] AUQ A/B coerced judge errors to substance=0 (testing) → null = inconclusive taxonomy; PRE arm vendored (branch-local SHA died with the branch).

Plus ~30 informational fixes (setup path anchoring, retro grep placeholder filtering, touchfiles dep gaps, autoplan B2 handler, sandbox-doctor hardening set, TMPDIR trust guard, digest↔resolver lockstep pin, laundering-warning wording). Skipped with rationale: 3 simplification advisories (ASK-only by the lens's own contract; user not present mid-run — preserved in the review log), eval-store harvest union (TODOS), evidence allow-path scoping (TODOS). PR Quality Score at convergence: 10/10 (advisories excluded per the carve-out this PR ships).

Design Review

No frontend files changed — design specialist returned NO FINDINGS; design review skipped.

Eval Results

Gate tier (86 tests, 47 shards, sharded paid runner): 46/47 shards passed (~$4).

  • The 1 failure, skillify-provenance-refusal, is pre-existing with receipts: reproduced on origin/main @ b5a951e in the same environment (FAIL → FAIL, identical 2-turn Unknown skill: skillify transcripts). Root cause: claude CLI 2.1.237 sessions with an env HOME override never discover project-scope .claude/skills. Filed as a P1 TODO (gate hygiene).
  • The AUQ repetition-cut NOT-WORSE A/B ran live pre-landing: POST 7/7 format elements, substance equal to PRE — no degradation.
  • New periodic evals (arm benchmark ×3, review-army simplification ×2) are registered in E2E_TIERS/touchfiles with detach-floor alignment green; their free selftests pass on every PR.

Adversarial (always-on): Claude subagent ✓ (11 findings), Codex adversarial ✓ (7 findings, 220K tokens — required installing bubblewrap and running with codex-side sandbox bypassed in this disposable VM; clean git status verified after). Codex structured review: unavailable in this sandbox (its file-reading helper hard-requires codex's bundled bwrap; codex review has no sandbox flag) — missing coverage for that one pass, not a clean bill. High-confidence cross-model agreements (forgeable shortcut markers, digest trust envelope, retry masking) all addressed above.

Scope Drift

Scope Check: CLEAN
Intent: import ponytail's structure-discipline mechanisms per the approved 8-workstream plan; make the cloud sandbox run the suite green; ship.
Delivered: the 8 workstreams + sandbox-green infrastructure + ship-time review fixes scoped to files this branch introduced or touched.

Plan Completion

COMPLETION: 43/50 verified (37 DONE + 6 CHANGED with goals met), 1 DEFERRED with ledger receipt, 4 post-ship checks, 0 NOT DONE.

  • WS1 8/8, WS3 1/1, WS4 5/5, WS7 2/2, WS8 4/4 — done as specced.
  • WS2 9 done + 1 changed (skill content extracted from the current ship render instead of git-worktree ref renders — arms differ by presence, not version, so ref machinery was unnecessary).
  • WS5 4 done + 2 changed (one host-neutral digest instead of byte-identical per-host copies; explainer arms = openclaw + hermes only — opencode has a full install arm).
  • WS6: repetition cut landed gated (−236 B/skill; the rebase note predicted smaller-than-planned); truth-in-labeling done; WS6-2 dead-frontmatter strip DEFERRED — the plan mandates empirical live-host verification this sandbox cannot perform; decision-ledger entry 2026-08-28 + TODOS entry.
  • Post-ship checks (by design, not gaps): WS2 paid periodic run + eval:compare; WS1 specialist-stats after ~10 real reviews; manual /retro with seeded markers; manual /autoplan run on a live host.

Verification Results

Skipped — no dev server (CLI/prompt toolkit; no plan verification section requiring a browser).

TODOS

No TODO items completed in this PR. Filed 5 follow-ups: skillify HOME-override gate red (P1, pre-existing), auq-verbose-vs-carved-ab branch-local ref (P2, same fragility class this PR fixed in its sibling), WS6-2 dead-frontmatter live-host verification (P2), eval-store harvest discriminated union (P3), evidence-gate digest allow-path scoping (P3).

Documentation

Post-ship doc audit for v1.75.0.0 (2 commits: 4217d120, 74549cee).

Doc diff preview:

  • README.md — /review row now names the advisory simplification lens (never blocks, never auto-applies); /autoplan phase order corrected to CEO → design → DX → eng (eng always last) in both the specialist table and the "Which review" table; host table gains the OpenClaw explainer-arm row.
  • docs/skills.md — same two table-row fixes; /autoplan deep-dive states the 4-phase Eng-terminal order and the recommended-option default; /review completeness-gaps documents the gstack-shortcut(dec-<id>) acknowledged-debt suppression and orphan-marker flagging.
  • AGENTS.md — /autoplan row order corrected.
  • docs/PROJECT_STRUCTURE.md — annotated tree gains agents-digest/, scripts/gen-agents-digest.ts, scripts/sandbox-doctor.sh, scripts/test-free-shards.ts.
  • CONTRIBUTING.md — Tier 1 test section documents GSTACK_FREE_JOBS, GSTACK_FREE_RETRY_FLAKY=1, and the sandbox-doctor one-command fixer; host count fixed 8 → 10.
  • docs/TESTING_INTERNALS.md — cloud-sandbox recipe says to open a new shell / source ~/.bashrc after the doctor seeds env; GSTACK_FREE_JOBS wording fixed from "caps" to "overrides in either direction".

Cross-model doc review: Codex probed ready but failed at runtime in this sandbox (missing bwrap); the Claude-subagent fallback reviewed docs vs the shipped diff — 6 verified findings applied, 3 rejected after verification.

Documentation Debt

  • ⚠️ /review simplification lens — reference coverage only; the docs/skills.md /review deep-dive has no explanation subsection. Fill via /document-generate.
  • ⚠️ shortcut-debt ledger — /retro deep-dive doesn't mention the Step 11.5 harvest.
  • ⚠️ arm benchmark — no contributor how-to for running/extending the periodic instrument.
  • ⚠️ Three CHANGELOG wording claims flagged by the doc review but left untouched per never-clobber policy (needs author sign-off): "Cursor side projects" phrasing, "41 skills" count, "5,190-line diff" mid-branch receipt.

Decisions taken autonomously (Conductor cloud session, no operator mid-run)

  1. MINOR bump to v1.75.0.0 — 55 commits, +5K/−500 across 123+ files, multiple new user-facing capabilities: unambiguous under the CHANGELOG scale guidance ("new capability shipped… MINOR"). Decision logged to the ledger. Trivially amendable pre-merge if you want a different level.
  2. The 4 UNVERIFIABLE plan items are the plan's own post-ship/periodic verification steps (weekly paid evals, live-host skill runs, usage telemetry after ~10 reviews) — deferred as listed above rather than blocking an unattended run on questions only these future runs can answer.

Test plan

  • Full free suite green via the strict sharded runner: 3,843 + 903 + 37 tests across shards, exit 0, evidence-ledger receipt (one single-shot browse flake absorbed by the opt-in serial retry with a loud FLAKY-PASS marker)
  • Gate-tier paid evals: 46/47 shards green; the 1 red proven pre-existing on main (receipts above)
  • AUQ NOT-WORSE A/B: passed live pre-landing
  • Review army: 3 cycles to convergence + cross-model adversarial; all criticals fixed on-branch

🤖 Generated with Claude Code


Open workspace in Conductor

garrytan and others added 30 commits August 28, 2026 01:41
…nal amended plan

Reorder the pipeline to CEO -> Design (if UI scope) -> DX (if developer-facing
scope) -> Eng. The old order (CEO -> Design -> Eng -> DX) let DX findings land
AFTER the required gate signed off, so eng validated a stale plan.

Accept-all semantics made explicit: every AskUserQuestion resolves to the
recommended option; premises no longer pause the pipeline mid-run (clearly-wrong
ones queue as User-Challenge items at the single Final Approval Gate). Eng's
Codex voice now sees the DX consensus summary. New free static test pins the
order; the chain E2E gains DX-between and Eng-terminal assertions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ens with ponytail's tag vocabulary

New 8th Review Army specialist (DIFF_LINES > 100, --simplification force flag)
hunting unrequested STRUCTURE only: delete/stdlib/native/speculative/shrink
closed tags, one-line findings, lines_removable field. speculative: replaces
ponytail's yagni: tag — we import the lens, not the posture; coverage stays
sacred (Completeness Gaps owns it, suppressions inlined, shrink needs >=5 lines).

Advisory carve-out in the merge step: advisory findings are excluded from
quality_score and the findings-count header, render with an [ADVISORY] label,
and are ASK-only in Fix-First. Zero-findings case prints the lens-scoped
'Simplification: lean already — nothing to cut.' from the PARENT (the
specialist keeps the exact NO FINDINGS contract); with findings, the parent
prints 'net: -N lines possible' summed from lines_removable.

Tests: static pins for the carve-out + early-out contract (gen-skill-docs),
two periodic e2e cases with planted fixtures — activation (over-build traps:
hand-rolled Intl, one-impl abstract, dead config) and false-flag precision
(a lean ETHOS 'choose A' diff must yield NO FINDINGS).

Inspired by dietrichgebert/ponytail's /ponytail-review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… ponytail's ladder, completeness kept

Tier-3+ skills gain a per-edit reflex the section only stated as research
discipline: before writing new code, stop at the first rung that holds —
repo helper, stdlib, native platform feature, installed dependency — then
build the COMPLETE version of what remains. The closing clause is the
explicit reconciliation with Boil the Ocean: the ladder governs structure,
never coverage. Rungs 1/6/7 (YAGNI / one line / minimum that works) are
deliberately NOT imported.

Also ports ponytail's root-cause rule: one guard in the shared function
beats a guard in every caller.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After completing work, skills report in a few short lines — what changed,
what was skipped, what to watch — and cut any explanation that outgrows the
change. Explicit exemptions protect every mandated output: decision briefs,
completion-status blocks, user-requested explanations, and report-shaped
skills' report formats (the report IS the work in /qa-only, /plan-*-review,
/retro, /document-generate).

Rationale is signal-to-noise, not tokens: ponytail's own benchmark shows
terse prose alone doesn't cut cost (caveman arm: -20% LOC, +7% tokens), and
independent replications found its 'skipped on purpose' essays ate the code
savings. Includes a good/bad closer example pair per the model-overlay
guidance that a positive example beats a 'don't be verbose' instruction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, not 3-5KB

Measured on the v1.71 render: --explain-level=terse saves exactly 2,611 bytes
per tier-2+ skill. The old ~3-5KB claim predated the preamble restructuring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s leave a joined trail

When the user accepts an option that is BOTH Completeness <= 7 AND a
durable-scope call, the decision ledger entry (gstack-decision-log, ceiling +
upgrade trigger in the rationale) is the source of truth, and the agent marks
each cut corner in code with gstack-shortcut(dec-<id>): <ceiling>, upgrade
when <trigger> — same edit, no follow-up question, never agent-initiated.

/retro Step 11.5 harvests markers into a debt ledger (grep || true — zero
matches is the healthy case; skill installs and docs excluded), joins on the
decision id so nothing double-counts, tags unlinked and no-trigger rot risks,
and closes with 'N markers, M with no trigger.'

/review suppressions: a marker with ceiling+trigger downgrades a would-be
Completeness Gaps finding to acknowledged debt. Redaction test pins that the
marker ships untouched (the ledger is the point) — it does not match the
TODO(owner) hygiene shape.

Format from dietrichgebert/ponytail's ponytail-debt; store inverted to gstack's
existing decision ledger.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…adder + bounded closer)

The golden-file regression test pins the rendered ship skill byte-for-byte;
the WS3/WS7 preamble sections are deliberate changes, so the baselines
re-capture per the goldens' own update protocol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… agent host can read

New agents-digest/gstack-AGENTS.md (1,765 bytes, hard 2,048-byte budget):
gstack's ethos one-liners, the reuse ladder, and voice rules for hosts with
no install arm — Zed, Amp, Jules, or any AGENTS.md-reading agent. Generated
by scripts/gen-agents-digest.ts, auto-refreshed by gen:skill-docs, committed
like llms.txt so setup's explainer arms can point at it before any toolchain
exists. First line carries the gstack version as its own staleness nudge.

Delivery is print-path + user-performed copy ONLY: setup never writes or
overwrites a user's AGENTS.md (a test pins this — no cp/ln/mv/redirect into
AGENTS.md anywhere in setup). openclaw and hermes explainer arms print the
path; slate keeps routing to the full Claude install and gbrain ships from
its own repo. HostConfig gains the optional install.instructionTier slot,
declared by both instruction-tier hosts. README host table now matches what
setup actually does.

Inspired by dietrichgebert/ponytail's instruction-tier AGENTS.md fallback —
one generated source, never per-host hand copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…RSE A/B

Removes the duplicate statements v1.71's compaction left in the
AskUserQuestion Format section: the completeness rule restated in the prose
triad, the auto-decide marker syntax stated twice, the Conductor-flakiness
explanation stated twice, and the self-check's full triad restatement. Every
verbosity floor and all 14 format pins stay (Layer 0 green).

The gate this decision rested on ran before landing (new periodic
skill-e2e-auq-repetition-cut-ab.test.ts, pre-cut ref 3263fff vs this
render, same harness as auq-verbose-vs-carved-ab): POST 7/7 format elements,
substance 5 — identical to PRE. No degradation; the load-bearing-repetition
hypothesis did not hold for these duplicates.

Net: -236 bytes per tier-2+ skill (~9.7KB corpus). Golden ship baselines
re-captured for the deliberate change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ther gstack's behavioral layer earns its tokens

Ponytail's honest-benchmark method pointed at gstack itself: 3 build-shaped
tasks (native-platform over-build trap, CRUD endpoint, bug fix with planted
decoys) x 2 arms, real claude -p sessions, scored on the git diff left
behind. A research instrument, not a release gate — no assertion compares
arm scores.

Arms use the PROVEN project-scope pattern: the with-arm installs a
build-discipline skill (extracted reuse-ladder + bounded-closer content, not
whole-file copies) into the fixture's .claude/skills/ with a CLAUDE.md
routing line and an explicit invocation; a live spike confirmed claude -p
discovers and invokes project-scope skills via the Skill tool (3 turns,
exact-output probe). Fixtures are git init + local bare origin; diff capture
is three lines of git, no worktree machinery.

Failure taxonomy: zero-diff arms are VALID scored cells (deterministic
0/none, no API call), harvest failures record harvest:null, judge_error
cells are excluded from aggregates but named in the report — nothing drops
silently. armJudge: fixed sonnet judge, 0-3 unrequested-structure rubric,
must name the construct or say none, bounded retry-on-malformed; callJudge
gains optional temperature/max_tokens (defaults unchanged). recordE2E now
populates tokens_used for every E2E. Eval schema v2: harvest gains
{insertions, deletions, net}, tolerant reads keep v1 runs comparable.

Registered periodic in E2E_TIERS + touchfiles (with the auq-repetition-cut
A/B); periodic detach timeout raised to the new shard-census floor. Free
selftest (8 tests, zero API) pins fixtures, extraction, arm asymmetry, diff
capture, judge plumbing, and the retry bound.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ngs, schema pin, triad phrasing

Skeleton ceilings re-captured for the 17 carved skills the wave deliberately
grew (reuse ladder + bounded closer + shortcut trail, net of the gated -236B
AUQ cut), each with its measured size in the comment per the carve-guards
protocol. eval-store schema pin updated to v2 (harvest gains
insertions/deletions/net). The AUQ prose-triad keeps its pinned per-choice
phrasing ('explicit on EACH choice') while still deferring the score scale to
the canonical Format rule — the shipped cut is strictly closer to the pre-cut
text than the render that already passed the NOT-WORSE gate. Autoplan carve
anchors follow the Phase 2.5 renumbering. Golden ship baselines re-captured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…call-supervised sandboxes

GSTACK_FREE_JOBS overrides the computed shard count (the free runner's
analogue of the paid runner's EVALS_JOBS). On Vercel sandboxes, PID 1
installs a seccomp filter whose supervisor spuriously fails access(2) for
busy processes — measured: 200/200 git-init probes fail 'Cannot access work
tree: Permission denied' while the suite runs at 6 shards, 0/200 idle;
statx succeeds while access fails on the same path in the same process.
One serial mega-shard maximizes per-process pressure and fails too; 2
shards is the measured sweet spot.

GSTACK_FREE_RETRY_FLAKY=1 (default OFF — dev boxes should see flakes)
re-runs attributed failures once, serially, capped at 5 files; a clean
retry downgrades to a loud FLAKY-PASS naming the offenders, a repeat
failure stays red, timeouts and unattributed failures never retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d test files

Local path validation now accepts os.tmpdir() alongside the classic /tmp
(new TEMP_DIRS in platform.ts): on macOS os.tmpdir() is /var/folders/...,
and TMPDIR-honoring CI/sandbox environments point it elsewhere entirely —
both are legitimate scratch space. Remote file serving (TEMP_ONLY) stays
pinned to TEMP_DIR alone; no change to the exfil boundary.

commands.test.ts drops 41 hardcoded /tmp literals for a tmpp() helper on
os.tmpdir() (two message assertions now reference the same variable), and
path-validation's symlink-escape test targets /etc/hosts instead of
/etc/crontab — the target must EXIST for realpath to resolve the link (a
dangling target falls back to the link's own path and passes vacuously),
and /etc/crontab is absent on Amazon Linux.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
resolve-user-slug and endpoint hashing exited 127 on Amazon Linux (shasum
is a macOS/perl tool). New _sha256_hex helper prefers sha256sum and falls
back to shasum, matching gstack-verify-gate's existing pattern; both call
sites converted.

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

Without the guard, git DWIMs the literal 'origin' as an ssh host/path; on
hosts whose transport launders exit codes the probe 'succeeds' with zero
branches and the allocator silently sees an empty queue — the exact
duplicate-allocation failure (#2545) fetchGitClaimed exists to prevent.
git remote get-url origin gates the probe; absence falls through to the
existing local-refs path with its staleness warning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… the suite green

Measured failure taxonomy for Vercel/Conductor sandboxes (missing /dev/fd,
64M /dev/shm, seccomp-supervisor access(2) EACCES under load, uid-1000
processes with FULL capabilities defeating chmod-denial tests, no X server,
no git identity, Conductor git-shim exit-code laundering) plus the
idempotent script that treats all of it and seeds the run recipe.

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

# Conflicts:
#	bin/gstack-config
#	browse/test/path-validation.test.ts
#	package.json
#	test/helpers/carve-guards.ts
…tract the function standalone

The merge kept a branch-local _sha256_hex helper; main's v1.72 landed the
same portability fix inline WITH tests that extract sha8_of()'s text and run
it under a shim-only PATH — a helper call can't satisfy that shape. Adopt
the landed implementation at both hash sites.

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

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

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aky-retry list

The empty-shard early return omitted the (required) failingFiles field —
tsc TS2741 — feeding undefined into the flaky-retry flatMap. Also drop the
dead 'else if (worst !== 0)' guard (the enclosing if already pins it).

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

agents-digest/gstack-AGENTS.md embeds VERSION in its first line and is
byte-freshness-gated (test/agents-digest.test.ts + Skill Docs Freshness CI),
but nothing in the release path regenerated it — every version-bumping ship
of this repo would land red. write now spawns the repo's own generator when
present (agentsDigest true/false/null in the output JSON), and ship's
evidence gate allow-lists the digest alongside VERSION/package.json.

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

$(pwd) printed a nonexistent path when setup ran from any other directory;
both arms now share one print_instruction_tier() using SOURCE_GSTACK_DIR.

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

The print-path-only guard now catches tee/install/rsync/dd/truncate, >>
appends, and laundered variable-destination writes. New test ties the
digest's hand-rendered reuse-ladder text to the preamble resolver so an
edit to either fails CI instead of shipping drift.

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

The Step 11.5 grep matched documentation mentions (dec-<id>, dec-*) in
checklists, resolver sources, and convention tests, reporting phantom debt
rows on gstack itself. A trailing filter kills placeholder forms; prose
tells the agent to discard convention-quoting hits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Without this, simplification (all-advisory by construction) would log
findings:0 every run and auto-gate itself into permanent silence after 10
dispatches. The advisory carve-out governs score and header only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Harvest diffs against the recorded seed SHA (origin/main is movable by an
  agent that commits AND pushes; a recorded SHA is not).
- Fixtures get a node_modules .gitignore and the git wrapper a 64MB
  maxBuffer, so a vendored-dependency arm is scored instead of killing the
  cell.
- The judge diff cap is a named constant with loud truncation (log +
  judge_reasoning suffix).
- Judge prompt block markers carry a per-call random sentinel, so a diff
  containing a faked closing marker cannot escape the data block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
garrytan and others added 14 commits August 29, 2026 05:57
…ts decision id resolves in the ledger

Cross-model catch (Claude adversarial + Codex agreed): any diff author could
fabricate a marker and silence Completeness review of that gap. Reviewers
now resolve the dec-id via gstack-decision-search; an orphan marker is
reported as a forged suppression, not honored as debt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…amend the plan and re-run Eng

The final gate offered B2 (respond to User Challenges) but the option
handler table omitted it, leaving accepted challenges with no amendment or
Eng re-review path. B2 now walks challenges one at a time; an accepted one
amends the plan and re-runs Eng (the gate always reviews the final plan),
sharing D's 3-cycle cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rrectness before LOC

The plan's metric order is diff-quality FIRST, but cells never ran the
fixtures' own run-tests.js, so a refusal, a broken implementation, and
working code were indistinguishable in aggregates (Codex adversarial catch).
Tasks with an oracle declare checkCmd; every cell records checks=pass|fail|none
in the report line and eval store. Selftest pins the oracle declarations and
that the planted bug fails its own check pre-fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gest trust envelope honestly

A failed digest regen warned and moved on — ship now instructs re-running
the generator and staging the digest with the bump (the freshness check
stays red otherwise). The 'no-op everywhere else' phrasing oversold safety:
the step now names what executes and why that is inside the envelope Step 5
already opened (the repo's own test suite).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ncation defeated the loud-failure contract

'2abc' silently became 2 and '3.7' became 3 despite the error text claiming
a positive-integer requirement. Strict /^\d+$/ pre-check; both shapes pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…f gate, non-interactive sudo

- The /conductor/bin/git patch writes tmp-then-rename with a .orig backup —
  a concurrently spawned git can never exec a truncated shim.
- Xvfb running-check looks for the :99 socket, not any-display pgrep.
- Xvfb install is dnf-gated so non-dnf distros degrade to a warning instead
  of aborting the remaining fixes under set -eu.
- The bashrc /dev/fd restore uses sudo -n || true — no password prompt at
  every shell start on non-passwordless machines.
- BASH_COMPAT=50 keeps heredoc bodies off the bash pipe window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… of deferring the red to CI

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…idens the local allowlist

TEMP_DIRS honors os.tmpdir() at daemon start; a daemon launched with
TMPDIR=/ would have trusted the whole filesystem for local path validation
for its lifetime. Subprocess pins cover /, $HOME, cwd-ancestor rejection and
that a benign distinct TMPDIR (the sandbox recipe's $HOME/tmp) stays honored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…laration made load-bearing; ratchet re-capture

- The ls-remote zero-heads warning no longer accuses an empty remote of
  running a laundering shim.
- instructionTier.rulesFile now must equal the generator's DIGEST_RELPATH
  (and setup must print it) — the declaration fails with the real path
  instead of lying silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
skillify HOME-override gate red (pre-existing, proven on main), the
auq-verbose-vs-carved-ab branch-local ref, eval-store harvest union,
evidence digest allow-path scoping, and the WS6-2 dead-frontmatter live-host
verification deferral.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… v1.73 release-step prose

The --regen-digest trust-envelope paragraph (Step 12) and the evidence-gate
digest note (Step 16) grew the ship skeleton past the previous 75,420
ceiling. Re-measured per the deliberate-change protocol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- README.md, docs/skills.md, AGENTS.md: /autoplan phase order corrected to
  CEO → design → DX → eng (eng always last); /review rows note the advisory
  simplification lens
- docs/PROJECT_STRUCTURE.md: add agents-digest/, gen-agents-digest.ts,
  sandbox-doctor.sh, test-free-shards.ts to the annotated tree
- CONTRIBUTING.md: document GSTACK_FREE_JOBS, GSTACK_FREE_RETRY_FLAKY, and
  the sandbox-doctor one-command fixer in the Tier 1 test section

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- README.md: host table gains the OpenClaw explainer arm row (setup has the
  arm; the table claimed to match setup)
- docs/skills.md: /review completeness-gaps section documents the
  gstack-shortcut(dec-<id>) acknowledged-debt suppression and orphan-marker
  flagging; /autoplan deep-dive states the recommended-option default with
  the 6 principles as tie-breakers
- CONTRIBUTING.md: host count 8 -> 10 (Hermes, GBrain), supported-hosts list
  completed
- docs/TESTING_INTERNALS.md: sandbox recipe says to source ~/.bashrc after
  the doctor seeds it; GSTACK_FREE_JOBS wording fixed from "caps" to
  "overrides in either direction" (matches the un-clamped runner)

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

trunk-io Bot commented Aug 29, 2026 •

Copy link
Copy Markdown

😎 This pull request was merged.

@github-actions

github-actions Bot commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

E2E Evals: ✅ PASS

95/95 tests passed | $20.94 total cost

Suite Result Status Cost
e2e-browse 7/7 ✅ $0.42
e2e-coverage-audit 2/2 ✅ $0.18
e2e-deploy 6/6 ✅ $1.15
e2e-design 7/7 ✅ $2.89
e2e-plan 12/12 ✅ $7.28
e2e-qa-bugs 3/3 ✅ $2.18
e2e-qa-workflow 4/4 ✅ $1.92
e2e-retro 2/2 ✅ $1.04
e2e-review 3/3 ✅ $0.52
e2e-review-attribution 3/3 ✅ $0.54
e2e-routing 10/10 ✅ $0.94
e2e-ship-docsync 1/1 ✅ $0.25
e2e-third-party-actions 5/5 ✅ $0.32
e2e-triage 1/1 ✅ $0.15
e2e-workflow 4/4 ✅ $0.66
llm-judge 25/25 ✅ $0.5

ubicloud-standard-8 runners (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite

garrytan pushed a commit that referenced this pull request Aug 29, 2026
The version gate caught a live queue collision (its whole job); same
MINOR bump level, next free slot per bin/gstack-next-version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
garrytan and others added 4 commits August 29, 2026 14:52
… are POSIX-only

CI exposed two wrong assumptions in the new temp-dirs tests, neither a
product bug:

- The remote-serving asymmetry test assumed a distinct os.tmpdir() lies
  OUTSIDE TEMP_DIR, but the free-shard runner nests each child's TMPDIR
  inside /tmp on CI — a file there is under TEMP_DIR, so serving it
  remotely is legitimate. The test now pins the actual exfil boundary on
  every topology (a cwd project file is locally readable, never remotely
  servable) and branches the os.tmpdir() case on nested-vs-outside.
  Reproduced locally with TMPDIR=/tmp/nested-tmp before fixing.

- The untrustable-TMPDIR subprocess probes set TMPDIR, which Windows
  os.tmpdir() ignores (reads TEMP/TMP) — and on Windows TEMP_DIR is
  DEFINED as os.tmpdir(), so the fixed+movable two-dir topology the guard
  filters does not exist there. Probes now skip on Windows with that
  rationale; the benign-TMPDIR assertion compares realpaths.

Verified under all three POSIX topologies: TMPDIR=$HOME/tmp (outside),
TMPDIR=/tmp/nested-tmp (CI shard shape), TMPDIR unset (identical).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
path.join built it with backslashes on Windows, so the wiring test's
string comparisons against setup and hosts/*.ts (which carry the
forward-slash literal) could never match there — windows-free-tests red.
path.join(root, DIGEST_RELPATH) at the write site normalizes fine.

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

The 4G remount does not survive restarts; a reverted 64M shm made the
multi-tab browse handoff test fail consistently under suite concurrency
(observed live: two consecutive full-run failures, green in isolation,
green again after remounting). Same guarded arithmetic as the doctor body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two-part fix for the gen-skill-docs-out-dir isolation-pin failure:

- cookie-import-browser built its scratch cookie DBs inside the TRACKED
  browse/test/fixtures/ dir (created in beforeAll, deleted in afterAll), so
  they flash as untracked files mid-run — a concurrent shard's porcelain
  snapshot caught the window on Windows. The DBs now live in a per-run
  tmpdir; zero source-tree writes.

- gen-skill-docs-out-dir is the free suite's only LIVE porcelain-snapshot
  test, so it joins TREE_MUTATING (the serial quiet window): any concurrent
  transient tree-write can race it, and its own spawned render rewrites
  llms.txt/agents-digest in place (idempotent on a fresh tree).

The race is pre-existing; this branch's +5 test files reshuffled shard
composition and exposed it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
garrytan added a commit that referenced this pull request Aug 29, 2026
…r speed (#2721)

* fix(ci): free-tests lane actually runs the make-pdf e2e gates

The 9 make-pdf/test/e2e gate tests probe make-pdf/dist/pdf,
browse/dist/browse, and the diagram-render bundle, then self-skip when
absent. The required free-tests lane never built any of them, so the
gates silently skipped on Linux for their entire life (verified: 9 of
14 skip, exit 0). make-pdf-gate.yml's justification for deleting its
Linux leg claimed the free lane covered this — it didn't.

- new build:gates script: exactly the three artifacts the gates probe
  (full bun run build compiles five binaries; ~60-90s tax on the only
  required check is not warranted)
- free-tests.yml: build:gates step + poppler-utils +
  fonts-noto-color-emoji (fonts must precede the first browse daemon
  launch — Chromium snapshots fontconfig at startup; verified live:
  a warm daemon renders tofu, a fresh one embeds NotoColorEmoji)
- make-pdf/test/e2e/ci-prereqs.test.ts: GSTACK_EXPECT_BINARIES=1 (set
  by the workflow) inverts the skip polarity in CI — dropping the
  build step or poppler fails the lane instead of re-opening the
  silent-skip hole

Pre-flight: all 9 gates green on Linux locally.

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

* fix(ci): kill the three zero-test eval jobs (hollow green)

- delete the vestigial e2e-codex / e2e-gemini matrix rows: both files
  are whole-file periodic-tier, so with no row tier: they ran ZERO
  tests and reported green on every PR (~2 min of runner each, pure
  false confidence; the periodic lane owns those suites)
- e2e-pty-plan-smoke gains tier: gate — its two files are whole-file
  describeE2ETier('gate'), so the job burned ~7 min of container setup
  then skipped every describe
- KNOWN_TIER_UNSET burned down to empty; the ratchet stays armed so a
  future row/file tier mismatch fails the suite instead of shipping
  hollow green

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

* fix(ci): least-privilege permissions + fork-safe concurrency keys

- evals.yml / evals-periodic.yml evals jobs: explicit contents:read +
  packages:read (container-image pull) and persist-credentials:false —
  the jobs that execute PR-authored code with three provider API keys
  ran on the repo-default token grant with the token written into
  .git/config
- permissions blocks for the 4 workflows that had none (skill-docs,
  make-pdf-gate, windows-free-tests, windows-setup-e2e)
- fork-safe concurrency keys: actionlint, skill-docs, make-pdf-gate,
  windows-setup-e2e switch from head_ref to PR-number keying — a bare
  branch name carries no fork prefix, so same-name branches from two
  forks shared one group and cancelled each other's runs

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

* fix(ci): one bun version everywhere + drift tripwire

Lanes disagreed four ways: 1.3.13 (free-tests, windows, Dockerfile.ci),
latest (quality-gate, make-pdf-gate), unpinned (skill-docs,
version-gate — setup-bun installs latest), 1.3.10 (.gitlab-ci.yml).
Different Bun versions change the runner output shapes the strict
classifiers regex-match, spawn semantics, and shell parsing — a lane on
a different Bun tests a different product; Dockerfile.ci's own comment
records this class biting once already (silent 1.3.13/1.3.14 drift).

All surfaces pinned to 1.3.13; test/bun-version-drift.test.ts scans
every workflow setup-bun stanza + Dockerfile.ci + .gitlab-ci.yml and
fails on any mismatch or unpinned stanza. skill-docs also gains
--frozen-lockfile (was bare bun install).

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

* test(ci): bind the three-way image-tag hashFiles() expressions

evals.yml, evals-periodic.yml, and ci-image.yml each compute the CI
image tag from hashFiles('.github/docker/Dockerfile.ci', 'bun.lock',
'patches/**') — synced by comment only (TODOS.md 'CI three-way
image-tag drift'). If one input list drifts, that workflow computes a
different tag for the same content: eval lanes silently rebuild the
image every run, or ci-image prebuilds a tag nobody looks up. The test
extracts each tag-computation site and fails on any mismatch.

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

* fix(ci): ci-image stops rebuilding the identical image every ship

- package.json out of the trigger paths: the tag hash deliberately
  excludes it (version bumps every ship), so every merge rebuilt and
  re-pushed the IDENTICAL tag (~2m26s for zero content change);
  patches/** added (it IS a tag input)
- manifest existence check (mirrors evals.yml): tag already exists →
  skip the build
- concurrency group: two rapid main pushes raced pushing the same
  :latest/:buildcache tags
- cron staggered 06:00→04:00 Monday: it shared the exact minute with
  evals-periodic, which could race a half-pushed tag or duplicate the
  build
- timeout-minutes: 30 (was unbounded → 360-min default for a hung
  docker build)

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

* fix(ci): quality-gate drops the 74s full-history checkout

fetch-depth:0 cost 74 of the job's 92 seconds; the three gates it feeds
take ~12s combined. Shallow checkout + exact-SHA fetches for the diff's
base/head (an exact-SHA fetch, not a guessed depth — long-lived
branches and merge queues still resolve), with a --deepen fallback for
push events whose 'before' is unusable. timeout right-sized 20→10 min.

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

* fix(ci): small-lane batch — timeouts, right-sizing, windows cache warm-start

- timeout-minutes on the 6 remaining unbounded jobs (actionlint 5,
  skill-docs 10, version-gate 10, make-pdf-gate 15, pr-title-sync 5,
  evals build-image 15) — a hung step sat on GitHub's 360-min default
- right-size measured-over-long timeouts: dependency-review 10→5,
  windows-setup-e2e 15→10
- dependency-review: 2-core runner (28s API call on an 8-core box) and
  drop .github/workflows/** from its trigger paths (workflow edits have
  no dependencies to review)
- windows caches gain restore-keys: a lockfile bump paid the 26s/43s
  restore for a guaranteed cold miss

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

* fix(test): scope GSTACK_HOME to each file's execution window

Five files assigned process.env.GSTACK_HOME at module scope. Shard
processes evaluate sibling modules before running their tests, so the
assignment leaked into every other file in the shard — the damage was
already visible in defensive workarounds (relink.test.ts:28 'fresh
install test saw a neighbor's skill_prefix'; cdp-e2e's own comment
documents a sibling's temp dir baked into artifacts).

Pattern: save original, assign in beforeAll, restore in afterAll
(cdp-e2e already restored but still assigned at load — its window now
matches the others). GSTACK_TELEMETRY_OFF and GSTACK_PROJECT_SLUG get
the same treatment where they rode along. Victim files' defenses stay
in place (cheap insurance).

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

* test: tripwire against module-scope GSTACK_HOME assignments

Column-0 assignment of GSTACK_HOME / GSTACK_STATE_ROOT in any tracked
*.test.ts fails with the file:line and the fix (beforeAll + afterAll
restore). Kills the cross-file env-leak class the previous commit
swept.

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

* fix(test): e2e-harness-audit derives its skill census from disk

The hand-maintained 39-name SKILL_GLOBS list had drifted to 39 of 54
SKILL.md.tmpl on disk. No live gap today (none of the 15 unlisted
skills is interactive), but the next interactive skill would have
landed unguarded with zero signal. The audit now walks top-level dirs
for SKILL.md.tmpl (statSync so symlinked dirs like connect-chrome
count), so new skills are in scope the commit they appear.

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

* fix(evals): judges honor the eval-model resolution chain + real 429 backoff

callJudge inlined GSTACK_EVAL_MODEL_JUDGE || sonnet, silently ignoring
the global GSTACK_EVAL_MODEL override every other eval call site honors
via lib/eval-model.ts. New 'judge' kind in DEFAULTS (sonnet — the D1a
pin-on-regressors calibration stands; model CHOICE unchanged) and
callJudge resolves through it: explicit arg > GSTACK_EVAL_MODEL_JUDGE >
GSTACK_EVAL_MODEL > default.

429 handling upgraded from one fixed 1s retry (reliably lost races at
CI concurrency) to three jittered exponential retries (~1s/4s/16s),
honoring the server's retry-after when present.

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

* fix(test): the two expect(true) paid stubs become test.todo

skill-e2e-spec-execute (600s budget) and skill-llm-eval-spec (300s)
reported PASS on every periodic run while asserting nothing. Deleting
them would remove the periodic-tier selector surface they exist to
register (diff-based selection for spec/ changes), so they become
test.todo — reported as todo/skip, never pass — with the v1.1
implementation specs kept in-file.

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

* fix(test): reactivate 5 quarantined browse tests (2 security)

extension-sender-auth's two privileged-message denial tests (content
script + missing sender.url — the extension's security boundary) and
snapshot's three skips were quarantined 'pre-existing' failures. Root
cause: machine-local state on the quarantining dev machines — the test
and gate code are byte-identical between the quarantining commit
(410b492) and HEAD, and all five pass deterministically on a clean
checkout (68/68 across both files, multiple runs). No assertions
weakened, no product changes.

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

* fix(evals): activate the 4 paid test files that could never run anywhere

carve-section-loading, codex-e2e-plan-format,
codex-e2e-recommendation-substance, and llm-judge-recommendation gated
on EVALS/tier (free suite loads them as describe.skip) but their names
fell outside PAID_TEST_GLOBS, so no paid lane ever selected them — net
execution zero, forever. The existing matrix tripwire filtered on
isPaidTestFile() first, so it was blind to exactly this class (the same
bug that hid the pre-split monolith's gate tests for ~8 releases).

- PAID_TEST_GLOBS: codex-e2e* + skill-llm-eval* wildcards (replacing
  exact names) + llm-judge-recommendation + carve-section-loading;
  package.json's six test-script glob lists mirrored
- codex-e2e-plan-format gains the explicit periodic tier gate its
  siblings carry (external-service rule) — without it the sharded
  runner's no-guard default would spawn Codex in the gate tier per PR
- eval:bg:periodic --timeout 32400→37800: the census growth pushed the
  periodic worst case to 35910s; the old value had 270s of headroom
  BEFORE this change and would now kill healthy runs mid-flight
- new test/paid-orphan-tripwire.test.ts: any EVALS/tier-gated test file
  outside the globs fails the free suite (reasoned SCANNER_EXEMPT for
  the gate helpers + meta-tests) — the class-killer
- paid-shards pins updated: the four orphans now assert INSIDE the
  census

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

* fix(browse): restrictDirectoryPermissions warns and skips symlinked dirs

Closes the Windows Free Tests red: recent lane failures showed a
platform-unguarded POSIX mode-bit assertion ('Expected: 493' — a
symlink-skip test) from PR-branch variants; the KNOWN_WINDOWS_SAFE
force-include reason ('mode-bitmask hits are POSIX-branch only') did
not hold for that shape, and main had neither the guard nor the
behavior.

- product: lstat first; a symlinked dir gets a warning and a skip on
  both platforms — chmod AND icacls dereference the link, so
  restricting through a symlink hardens an unvetted target (and
  /inheritance:r could lock out its real owner). All callers already
  treat hardening as best-effort (try/catch).
- test: the symlink regression test, platform-aware — symlinkSync in
  the house try/catch skip pattern (Windows runners without Developer
  Mode can't create symlinks), mode-bit assertion guarded off win32,
  behavior assertions (no throw, warning text, target readable)
  everywhere; POSIX still proves the skip (0o755 unchanged, not 0o700)
- KNOWN_WINDOWS_SAFE reason updated to the now-true premise

20/20 pass on Linux.

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

* fix(test): unique tmp dirs for plan artifacts + audited live-repo cwd sites

Six paid PTY tests wrote their expected plan artifact to a FIXED shared
/tmp path ('/tmp/gstack-test-plan-<mode>.md') and rmSync'd it in
finally — under --retry 1, EVALS_JOBS>1, or two concurrent worktrees, a
sibling's cleanup deletes this run's artifact and the D19 'agent did
not produce expected plan file' assertion fires spuriously. Each test
now mkdtemps its own dir, interpolates the unique path into the agent
prompt (fixture-sourced prompts get a replaceAll + drift guard that
throws if the fixture's literal ever moves), and cleans up its own dir.

The 18 cwd:-into-the-live-repo sites were audited: all deliberate
(skill registry + hermetic pre-trusted dir, in-repo gen renders, git
history reads, slug resolution) — each now carries a
'// LIVE-REPO CWD: <reason>' comment so the next audit can tell
deliberate from accidental.

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

* fix(test): trim the seven over-wall 1700s timeouts to the 1500s physical ceiling

1,700,000ms (28.3 min) exceeded every wall these tests run inside: the
25-min CI job timeout and the 1800s sharded-runner wall (which also
leaves --retry 1 zero room for a second attempt). Budget above the wall
is fiction, not headroom — a test that actually used it produced a
job-level kill (no bun summary, no artifact) instead of a clean
per-test timeout. No recorded p95 exists for this family (they are
being retiered to periodic in the re-platform wave); the trim stops at
the physical ceiling rather than guessing lower. Final policy lands in
the Wave-2 eval-budgets constants module.

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

* refactor(gen): main() guard — importing gen-skill-docs no longer regenerates the tree

The generator's whole body executed at module load, so any import of it
(test/gen-skill-docs.test.ts pulls assertSinglePreamble via require();
test/catalog-trim.test.ts imports helpers) regenerated all 71 SKILL.md
in place — the root cause of half the TREE_MUTATING serial-shard
entries (hazard class #2532). The body now lives in an exported
main(): number behind if (import.meta.main).

Semantics preserved exactly: failure exits are immediate (matching the
old top-level process.exit), success leaves the event loop to drain so
the llms.txt fire-and-forget IIFE finishes its write, and the module
stays synchronous/require()-able. Proofs: byte-identical --host all
output (git status clean), --dry-run stale-tree still exits 1 (the
skill-docs freshness lane depends on it), and the new
test/gen-skill-docs-import-purity.test.ts pins load-time purity via a
subprocess probe (mtime-based, so a dirty worktree can't false-fail).

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

* feat(gen): --out-dir renders every host, outputs-only

--out-dir was Claude-host-only (gen-skill-docs.ts:842), which forced
the codex/factory-regenerating tests (gen-skill-docs, skill-validation,
host-config) to mutate the live tree — the reason they sit in the
TREE_MUTATING serial shard. The flag now mirrors ALL outputs into the
out-dir: external-host trees (.agents/.factory/... via
processExternalHost), external section files, openclaw docs, and
gstack/llms.txt (a catalog-mode render must never rewrite the tracked
index). OUTPUTS ONLY — inputs (templates, sections/, host configs) are
always read from ROOT, so an empty out-dir can never feed the render.
rewriteSectionBase stays Claude-only (external hosts have their own
path grammar).

Proofs: in-place --host all is byte-identical (tree clean);
--host all --out-dir <mkdtemp> renders the full multi-host tree with
ROOT untouched; gen-skill-docs-out-dir tests + 415/415
gen-skill-docs.test.ts green (bin/dev-setup's claude rendering
byte-compat).

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

* fix(evals): every E2E key's dep list names its own declaring test file

129-of-177 keys omitted their own test file, so editing only a test's
prompt or assertions selected NOTHING — the changed test never ran on
the change that changed it. 135 keys self-registered (110 E2E + 25
LLM-judge), resolved by strict declaration evidence (testName:/
testIfSelected/judge call sites), with skill-name false positives
excluded.

e2e-tier-alignment's warn-only branch for unregistered files is now a
hard failure with a 4-entry KNOWN_UNREGISTERED ratchet (template-
literal testNames, fail-open-safe) + a burn-down test so the set only
shrinks. Selection sanity: a one-file diff on skill-e2e-qa-workflow now
selects its 4 tests (was 0); skill-llm-eval 0 → 25.

Known follow-ups (filed): 15 E2E + 2 judge PHANTOM keys select tests
that exist nowhere; codex-e2e-plan-format's testIfSelected names have
no map keys (run-all only).

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

* test(evals): ratchet the 8 newly-visible gate-matrix gaps

The self-registration sweep made these eight files' gate-tier keys
visible to the census for the first time — their gate tests run in NO
CI lane today (pre-existing hole, newly measurable). Ratcheted into
KNOWN_MATRIX_GAPS with the burn-down note: the paid-lane re-platform
runs every gate file by construction and retires this ratchet class.

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

* feat(test): duration-aware LPT shard packing for the free suite

Hash sharding balances file COUNTS (1.15x spread) but not cost — the
Playwright-launching files landed 4/3/4/1/2/1 across 6 shards, giving a
measured 28s–97s shard spread and ~40s of idle tail on every run.
Full-suite mode now packs by recorded per-file durations
(longest-processing-time-first) when the committed seed
scripts/free-test-durations.json exists.

- ONE store, no overlay: the seed is refreshed occasionally via the new
  --record-durations mode (each file timed in its own child — exact,
  and immune to bun's stream buffering, where silent passers print no
  header to timestamp); GSTACK_FREE_TEST_DURATIONS overrides the path
  for experiments; CI never records
- seed is a hint: missing → silent hash-shard fallback; corrupt (bad
  merge) → one warning + fallback; unknown files → 75th-percentile
  pessimism so a surprise long-runner can't recreate the tail
- packed shards get duration-aware walls (max(base, predicted x 3)) —
  LPT decouples count from cost BY DESIGN, so the 5s/file heuristic
  would undersize a shard holding few expensive files
- one log line per shard (files + predicted seconds) so packing
  regressions are diagnosable from any run log
- the --shard CI-matrix path is untouched: stable hash indices are its
  contract
- successor note in-code: bun >=1.3.14 ships native --timings/--shard
  LPT — swap this packer when the repo unpins 1.3.13

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

* fix(test): decouple slop:diff from bun run test; quality-gate runs it per PR

'bun run test' silently appended up to two 120s npx slop-scan runs plus
a git worktree add/remove after the suite (2>/dev/null || true) —
invisible in the documented '~90-100s' timing and pure friction in the
pre-commit loop. Decoupling is not coverage removal: quality-gate.yml
now runs slop:diff on every PR (advisory, matching its in-repo 'never
blocking' contract), and /review already invokes it explicitly.

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

* feat(test): eval-budgets timeout tiers + fit/ceiling policy test

Five named tiers (JUDGE 120s / CAPTURE 300s / CAPTURE_LONG 600s /
PTY 900s / PTY_LONG 1200s) replace hand-ratcheted sprawl (46x300s,
46x120s, 44x360s, 44x180s, 27x240s, 19x150s, 13x420s, 12x600s...),
much of it inflated to paper over the old 40-way in-shard concurrency
that the sharded runner's 1-file-per-shard model kills. Policy test
pins: every tier fits the shard wall minus 120s overhead (the
structural fix for budgets-above-the-wall fiction), tiers stay ordered,
and no paid literal exceeds PTY_LONG x1.25 — oversized tests get split,
not budgeted past the wall.

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

* feat(test): shared runBin helper for bin-script unit tests

~36 free test files each carry a near-identical local run() (spawnSync
+ utf-8 + {status, stdout, stderr}) differing only in env composition,
cwd, and timeout. runBin absorbs the invariant core; options carry the
variance (gstackHome sets BOTH GSTACK_HOME and GSTACK_STATE_DIR — the
config-precedence trap several locals rediscovered independently; home
for $HOME-anchored bins; input/trim/timeout/maxBuffer). Free-test-only
by design so it never becomes a de facto global touchfile. Migration of
the 36 call sites lands separately (mechanical batches).

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

* fix(test): runBin trim assertion — trim shapes stream ends, not interior

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

* refactor(test): mechanical sweep — 298 paid-test timeouts onto eval-budget tiers

69 files, both shapes (trailing bun-test budgets and runner
timeout/timeoutMs options), ROUND-UP ONLY so nothing that passed can
start failing: 75 → JUDGE_MS, 137 → CAPTURE_MS, 74 → CAPTURE_LONG_MS,
9 → PTY_MS, 3 → PTY_LONG_MS. Raw >=60s literal count in the paid scope:
395 → 97, of which 51 are non-timeout noise (fixture dates, run IDs)
and 46 are enumerated justified holds (comment-carrying calibrated
budgets, poll-loop constants, utility spawn waits, and the seven
physical-ceiling 1_500_000 sites). The eval-budgets policy ratchet
keeps the residue from regrowing.

Known collapse: where an inner runner budget and its enclosing test
budget now share a tier, the old stagger is gone — an overrun surfaces
as a bun test timeout instead of a graceful runner timeout
(diagnosability trade, not a correctness one).

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

* test: coverage fill — 95 tests for six zero-coverage surfaces

- eval CLI family (eval-list/compare/summary + eval-select smoke): the
  primary interface to eval results had no tests; isolation via a fake
  gstack-slug under a mkdtemp HOME (the scripts' real resolution path —
  they do NOT honor GSTACK_EVAL_DIR; only EvalCollector does). Pinned
  current behavior: eval-list does NOT exclude _partial runs (documented
  improvement candidate)
- slop-diff (runs on every /review + quality-gate): fixture git repo +
  first-on-PATH npx stub (never downloads real slop-scan); no-diff
  early exit, missing-scanner fallback, fingerprint line-insensitivity,
  merge-base worktree scan
- bin/gstack-code-intelligence CLI arg surface (lib was covered, the
  284-line CLI wasn't): select/consent/suggest/index/search gating;
  pinned: --help routes to usage failure exit 1 (no handler)
- browse media-extract: the page.evaluate callback exercised in-process
  against a mock DOM (no exports added) — lazy-src fallback chain,
  HLS/DASH detection, bg-image url() parsing, 500-element cap
- browse session-cookie-store: factory contract (cookieName/ttlMs/
  maxSessions eviction, cross-store isolation, mint→validate
  round-trip); store is in-memory — no fs cases exist
- lib/version-source direct unit tests (gstack-version-bump.test.ts
  spawns the bin, never imports the lib): parse/format/cmp/bump
  coercion, npm 4→3 translation, #2501 mangled-JSON regression class

All hermetic (mkdtemp homes, runBin child isolation); windows curation
correctly partitions the six.

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

* refactor(test): first runBin migration batch (3 of ~36 run() duplicates)

explain-level-config, benchmark-cli, evidence move onto the shared
helper; each file's remaining special-case spawnSync sites (raw-buffer
probes, env-scrub probes) stay put deliberately. 55/55 green.

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

* refactor(evals): paid shards spool to disk + shared runShardChild lifecycle

- runPaidShard no longer buffers whole 30-min stream-json streams in
  RAM (x concurrent jobs): every byte tees to a per-shard log file
  (slug-named, path printed at START for mid-run inspection and on the
  FAILED terminal line); failures print a 64KiB tail read back from
  disk; passing shards stay quiet (the file is the record) — the free
  runner's proven contract. Classification unchanged: the strict
  classifier still sees every byte first.
- the ~35 duplicated spawn/group-kill/wall-timer/finally-reap lines
  move into runShardChild in test-strict-output.ts (detached-per-
  platform spawn, signal forwarding, SIGKILL group kill at the wall,
  drain-before-verdict); designed so the free runner can migrate later
- expectedFiles drift fixed toward ENFORCEMENT: the injected-command
  exemption is gone — a fake command exiting 0 without bun's terminal
  summary now reads FAILED (pinned: silent-pass → failed)

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

* feat(evals): parent-computed selection propagates to shard children

The sharded runner computed diff selection once, then each of its 48-73
children recomputed it at module load — including, on touchfiles-diff
branches, a per-child bun subprocess evaluating the old data file (20s
timeout each). The parent now serializes {version, selected, reason} as
EVALS_SELECTION_JSON into the shard env; e2e-helpers adopts it at load.
Fail-open preserved: any parse/shape violation → ONE stderr warning +
local recompute; absent env → silent local compute (non-sharded
entrypoints unchanged). Drift test pins parent→child round-trip to
identical selection decisions plus the malformed/absent cases.

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

* fix(test): kill the four worst fixed sleeps (300s/30s/30s/20s)

- watchdog.test: the 20s blind wait for one production parent-watchdog
  tick becomes BROWSE_PARENT_WATCHDOG_INTERVAL_MS=250 (new env knob in
  server.ts, NaN-safe, production default unchanged) + polls for the
  boot line and the tick's stay-alive log — strictly stronger (the old
  form never proved a tick observed the parent death). 24s → 3.6s.
- stop-dead-daemon / terminal-agent-owner-watchdog: the 300s/30s
  stand-in child lifetimes become stdin-EOF-bound — the child can never
  self-exit mid-test on a slow runner (spurious-failure class) and
  self-reaps instantly if the test dies (no 300s orphans). Node-compat
  stdin APIs (owner-watchdog runs on the Windows lane).
- browser-skill-commands: the sleeper fixture's 30s self-time becomes
  8s (no stdin pipe exists in runToFiles) — far above the 1s product
  timeout it must outlive, below the test ceiling, so a timeout-kill
  regression fails on clean assertions instead of an opaque bun
  timeout; added: stdout must NOT contain 'done'.

45/45 green across the four files + server tripwires.

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

* fix(test): gen-skill-docs + catalog-trim leave the serial mutator shard

gen-skill-docs.test.ts's 15 in-place generator spawns now render into
mkdtemp out-dirs (gitignored-artifact reads repointed; the handshake
scan's silent console.warn degrade became a hard assertion); its
tracked-tree reads (freshness dry-run, SKILL.md content pins) stay
reads. catalog-trim needed no change beyond the earlier main() guard —
its import is now side-effect-free (pinned by the import-purity test).
Both TREE_MUTATING entries deleted in this commit, per the transition
rule: an entry leaves in the same commit as the file's last in-place
write.

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

* fix(test): skill-validation renders codex host into an out-dir

Its 3 in-place --host codex regeneration sites collapse into one
module-level --out-dir render; assertions untouched. TREE_MUTATING
entry deleted.

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

* fix(test): host-config self-provisions goldens (ordering dependency severed)

Its goldens were 'produced by gen-skill-docs.test.ts' with a
when-missing beforeAll fallback that wrote the live tree — an
inter-test ordering dependency the serial shard hid. It now renders
codex+factory UNCONDITIONALLY into its own out-dir and reads goldens
only from there (the Claude golden deliberately keeps reading tracked
ship/SKILL.md — a read; out-dir claude renders repoint section-base
paths by design). TREE_MUTATING entry deleted.

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

* fix(test): gbrain-detection-override drops mutate-then-git-restore

regenAndSnapshot renders --host claude --out-dir <mkdtemp> (+
--respect-detection) and snapshots probes from the out-dir. The
git-restore machinery is deleted outright — it restored only
PROBE_FILES of the 71 files each call wrote, so a stale tree kept the
other 68 dirty (the partial-restore bug), and its 'no output-path arg'
comment had been false since --out-dir landed. TREE_MUTATING entry
deleted.

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

* fix(test): catalog-mode-full renders to out-dir; restore machinery deleted

The full-catalog smoke no longer rewrites all 71 SKILL.md then
regenerates to restore (with its 'CRITICAL: failed to restore' prayer
path) — it renders into a mkdtemp and additionally asserts tracked
ship/SKILL.md is byte-unchanged. TREE_MUTATING entry deleted.

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

* fix(test): idempotency proof strengthens to two-out-dir recursive diff

Two renders into two separate out-dirs, EVERY file diffed byte-for-byte
(claude-only and --host all; normalization only for each dir's own
sanctioned section-base repoint; presence-sanity lists guard against a
vacuous empty-dir pass) — strictly stronger than the old in-place
double-regen that sampled 5 files. TREE_MUTATING entry deleted.

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

* fix(test): spec-template-sync compares an out-dir render, not an in-place one

TREE_MUTATING entry deleted.

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

* feat(test): the serial tree-mutating shard dissolves — TREE_MUTATING is empty

Zero mutators remain (all eight render into out-dirs now), so the four
ratchet READERS (parity caps, size budgets, carve parity/ordering) get
a quiet tree by construction in any shard and rejoin the parallel
phase. The ~35-40s serial tail on every full-suite run is gone. The
mechanism stays: a future test that genuinely must write shared
artifacts in place earns an entry with a reason and is serialized
again; the census pin still fails on renamed keys.

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

* test(gen): out-dir byte-identity + tree-clean pins for external hosts

codex render: porcelain unchanged AND out-dir gstack-ship/SKILL.md
byte-identical to a fresh in-place render (+openai.yaml presence);
--host all render: exit 0, porcelain unchanged, claude + .agents +
.factory + llms.txt + openclaw docs all present in the out-dir.

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

* feat(test): commit the initial free-test durations seed (496 files)

Recorded via --record-durations on a quiescent tree: 479s serial
total, p50 92ms / p90 1.8s / max 31.4s — the top-heavy cost shape LPT
packing exists for. A hint, not a contract: refresh opportunistically
with bun run test:free --record-durations.

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

* feat(evals): planner/executor/report modes — the CI re-platform surface

One PLANNER computes diff selection + the slice plan ONCE and writes a
manifest (--emit-plan <path> --slices K); K executors consume it
(--plan <path> --slice i), never self-selecting, and write slice-result
artifacts; a REPORT reconciles results against the manifest (--report
<dir>) fail-closed: a slice whose artifact never landed is a FAILURE,
a planned shard nobody reported fails, wrong-slice/duplicate/cross-tier
results fail. Kills per-slice selector divergence and hollow-lane
aggregation at the root.

- hollow-shard guard: under EVALS_ALL, exit 0 with ZERO executed tests
  (bun's 'Ran N tests' now captured by the classifier — additive) is
  'passed-empty' and fails the run; selective runs keep it 'passed'
  with one warning (in-file diff/tier self-skips are legitimate there);
  unknown counts are never guessed hollow
- retry parity: --retry 1 default + RETRY_OVERRIDES literals for the
  three files whose old matrix rows earned retries: 2 (stale entries
  pinned against disk)
- live smoke: gate plan = 48 shards across 6 slices; report mode exits
  1 on a fabricated missing slice, 0 when complete

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

* feat(ci): sliced paid lane (planner -> 6 executors -> fail-closed report)

The parity-phase re-platform: evals.yml gains a second, sliced lane
driven by scripts/test-paid-shards.ts — the SAME engine local
eval:bg:gate uses, so CI and local share one selection engine.

- plan-slices: ONE planner (fetch-depth 0 — the only job needing
  history) emits the manifest; selection fails open to run-all, never
  per-slice (the divergence class is structurally dead)
- eval-slices: 6-way matrix consuming the manifest; PTY seed +
  skill-registration steps run unconditionally (idempotent — a sliced
  lane cannot key them on suite names); aggregate spawn budget
  6 x EVALS_JOBS=2 x EVALS_CONCURRENCY=2 = 24 lane-wide (the matrix's
  40-way per row queued session startup behind 39 siblings — the
  timeout-flake family root); slice results + spooled shard logs
  uploaded as artifacts
- slices-report: reconciles slice artifacts against the manifest
  FAIL-CLOSED via --report — a slice whose artifact never landed, or a
  planned shard nobody reported, is a failure, not an absence
- sequenced needs: evals so provider concurrency never doubles while
  both lanes coexist; the matrix + its ratchets are deleted after
  demonstrated parity (intersection + expected-additions comparison)
- workflow_dispatch gains evals_all (default true) for parity runs and
  post-merge smokes — a dispatch can never silently select zero

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

* feat(ci): weekly periodic lane runs EVERY periodic test + gate census backstop

evals-periodic.yml re-platforms onto the sharded runner: planner
manifest → 6 executor slices → FAIL-CLOSED report. This IS the coverage
contract: all ~70 periodic-tier files weekly (EVALS_ALL=1), killing the
silent-rot class where a hard-coded 9-file matrix left ~57 files
running NOWHERE (the autoplan E2E rotted invisibly for months).

- test/helpers/periodic-exclude-data.ts: reasoned exclusions in their
  OWN literals file (deliberately not touchfiles-data — map-diff
  evaluates old versions of that file standalone). Every entry carries
  reason + tracking with a re-entry condition; the runner surfaces each
  exclusion per run; policy test pins real-file + non-empty fields.
  Initial: ship-idempotency + brain-privacy-gate (documented-red,
  never green) and skill-e2e-ios (manual hardware). The TODOS 'sidebar
  E2E trio' turned out already deleted — only tombstone tests remain.
- gate-census job: weekly EVALS_ALL gate-tier run — PR lanes are
  diff-billed, so without this the full gate census might never execute
  anywhere; with the hollow-shard guard it is a census-health check
  (exit 0 + zero executed tests fails), not just a test run.
- failure notification is a concrete gh issue UPSERT (one tracking
  issue, commented per red week — never issue-per-week spam), with
  issues:write scoped to the report job.

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

* docs: TESTING_INTERNALS covers the 2026-08 runner overhaul

LPT-packed free suite + --record-durations, the emptied TREE_MUTATING
mechanism, the sharded paid runner as the single selection engine,
CI planner/executor/report with the fail-closed report and hollow-shard
guard, the weekly coverage contract + exclusions policy, and the
eval-budgets timeout tiers.

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

* docs(CLAUDE.md): testing prose matches the overhauled runners

- bun run test: duration-packed shards + --record-durations; the
  trailing serial tree-mutating shard no longer exists
- two-tier system: the sliced CI lanes (one engine local+CI), the
  weekly all-periodic coverage contract + exclusions, the gate census
- periodic detach timeout 32400 → 37800

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

* docs(TODOS): close the absorbed test-infra items, file the overhaul follow-ups

Closed with receipts: the periodic coverage contract (implemented as
full weekly coverage + exclusions), the eval-harness observability P1
(verified already landed: heartbeat, incremental _partial persistence,
live stderr + eval-watch), and the sidebar trio (already deleted —
tombstones remain). Filed: matrix deletion after parity, the
required-check maintainer decision, browse /tmp-namespace hardening,
PTY boot-readiness waits, the single typed test registry, bun-native
LPT swap, runBin/free-runner migrations, eval-list partial exclusion,
phantom key cleanup, duration-weighted slicing.

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

* v1.73.0.0: test/CI overhaul — green means green, suites restructured for speed

Version + release notes for the audit-and-overhaul branch: every
silently-skipping or never-running test class fixed and tripwired, the
free suite duration-packed with the serial mutator shard dissolved, the
paid lane re-platformed onto the sharded runner (planner/slices/
fail-closed report, parity phase), the weekly all-periodic coverage
contract, eval-budget timeout tiers, and 95 new coverage tests.

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

* fix(ci): first-live-run fixes — executor history + two environment-blind assertions

The sliced lane's first run (PR #2721) did its job: the planner and
report worked, the manifest governed, and every failure had a name.
Three were fixable on the spot:

- executor + gate-census checkouts get fetch-depth: 0 — files with
  SELF-derived selection (the LLM-judge map, routing) walk git at
  module load, and selection is deliberately fail-closed on git errors,
  so the shallow checkout crashed those shards ('ambiguous argument
  main...HEAD'). The manifest still governs WHICH shards run.
- landscape --toc gate: the exact toBe(3) landscape-page count was
  font-metric-dependent (3 on Amazon Linux, 2 on ubuntu CI — the same
  disease the file's own page-index comment warns about). Now a
  comparative invariant: --toc must not CHANGE the landscape count vs
  a baseline render.
- paid-run-manifest parse test builds its manifest under EVALS_ALL so
  it never walks git (proven with GIT_DIR=/nonexistent).

Remaining first-run failures are newly-exposed rot in gate files that
had never executed in CI (skillify D1 refusal, session-intelligence
context-restore, one tpa-apple-ban retry flake) — being probed
separately; they are the lane WORKING, not the lane failing.

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

* docs(TODOS): file the three first-execution findings from the sliced lane's live run

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

* v1.74.0.0: queue-advance — #2722 claims the v1.73.0.0 slot

The version gate caught a live queue collision (its whole job); same
MINOR bump level, next free slot per bin/gstack-next-version.

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

* fix(test): per-shard CHROMIUM_PROFILE — the collision class duration packing exposed

Nine test files launch in-process persistent contexts or daemons that
default to the SHARED ~/.gstack/chromium-profile. Two concurrent shard
processes on one profile dir kill each other's browser — observed live
on CI once duration packing recomposed shards: handoff's
launchPersistentContext died 'Target page, context or browser has been
closed' (--user-data-dir=~/.gstack/chromium-profile in the call log)
while a sibling shard's daemon logged 'Chromium process crashed'. Hash
sharding had masked the collision by chance placement; handoff passes
standalone everywhere.

Fix at the runner, not per file: each shard child gets
CHROMIUM_PROFILE=<shard-state>/chromium-profile (the documented env
knob, same isolation idea as the existing per-shard TMPDIR). Files
within a shard run serially, so sharing the per-shard profile is safe;
config.test's resolution-order tests save/restore the env around their
assertions.

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

* fix(test): landscape --toc gate asserts promotion PRESENCE, not counts

Two rounds of CI receipts: the exact toBe(3) was font-metric-coupled
(3 on Amazon Linux, 2 on ubuntu), and the baseline-comparison repair
then failed 2-vs-3 across renders SECONDS apart in one CI job while the
sibling no-toc test saw 3 — per-render image-promotion timing makes any
count assertion here a coin flip. The sibling test owns exact promotion
counts; this test's actual invariant is that --toc does not break the
promotion machinery: >=1 landscape page + the TOC rendered. Also drops
the second render (halves the test's runtime).

Flaky per-render image promotion itself is worth its own look — noted
in TODOS with these receipts.

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

* docs(TODOS): file the per-render image-promotion nondeterminism (receipts from PR #2721)

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

* fix(test): per-FILE Chromium profiles for the nine in-process launcher files

Completes the profile-isolation work: the per-shard CHROMIUM_PROFILE
stopped cross-shard kills; these nine files launch in-process
persistent contexts and could still collide with a lingering daemon a
sibling file spawned on the SAME shard profile. Each now scopes a
mkdtemp profile via beforeAll/afterAll (the module-scope-tripwire-safe
pattern), cleaned up per file. All nine green solo and in combined
runs, except the pre-existing commands+snapshot pairing — proven
identical WITH and WITHOUT these edits (baseline receipts) — which is
the daemon-lifecycle follow-up now extended in TODOS with this
session's receipts.

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

* fix(browse): Chromium-crash exit is daemon-only — embedded launches never kill their host

handleChromiumDisconnect unconditionally process.exit()ed. Correct for
the standalone daemon (its supervisor/user must notice); suicidal when
a TEST launches BrowserManager in-process: a mid-suite Chromium death
exited the whole bun shard with no terminal summary — the exact
truncation class the strict runner flags (observed live: CI shard 1 on
eb23329 died at cache-concurrent-refresh right after a daemon-spawning
gate test; with this fix the same pairing runs to completion and
REPORTS instead of dying).

The standalone entrypoint opts in via markDaemonProcess() under
server.ts's import.meta.main gate — the same embedder contract its
signal handlers already use (gbrowser phoenix keeps its own handlers).
Embedded contexts now get the disconnect log line and continue.

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

* fix(test): context-restore assertion is evidence-based, not prose-matching

The test failed twice per run in TWO CI cycles while passing locally
4/4: the prompt said 'present the content' and the check grepped the
FINAL message for exact phrases — local runs quoted the file, CI runs
paraphrased ('the most recent context is from branch-b...') and the
substring check lost the coin flip.

- prompt now demands machine-checkable output: the newest file's
  '## Working on:' heading VERBATIM + a literal 'RESTORED: <filename>'
  marker (the mtime-scramble and cross-branch subject matter untouched)
- assertion ordered strongest-first: RESTORED marker → legacy content
  phrases → tool-call corroboration (Read/Bash input naming the newer
  file, credited ONLY when the older file was never read — a
  both-files run must still present the right one)
- the older-file negative got STRONGER: an explicit RESTORED marker
  naming the older file fails even if wintermute words appear elsewhere
- sibling scan: context-recovery-artifacts got the additive prompt-side
  treatment only (quote the matched literals verbatim); its lenient
  1-of-6 assertion deliberately unchanged

3/3 consecutive local green with all evidence classes firing
(marker=true, content=true, toolNewer=true, toolOlder=false).

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

* fix(test): skillify family — HOME==cwd broke project-skill registration

Root cause (forensically pinned from stream-json init events + a
kill-after-init probe): with HOME set EQUAL to the child's cwd, claude
resolves <cwd>/.claude/skills as the PERSONAL skills directory and the
seeded project-tier skills never register — the Skill tool returned
'Unknown skill'. The provenance-refusal test then improvised a refusal
whose wording missed the regex (the deterministic CI+local red); the
happy-path and approval-reject siblings passed only because their
agents self-recovered by Reading SKILL.md manually — silently not
exercising the Skill-tool path at all.

All three tests now use HOME=<workDir>/home (a fresh subdir keeps the
override's intent: child ~/.gstack writes land in the assertable
sandbox, without the cwd collision). Refusal test additionally: a
'not registered/unknown skill' tripwire (a not-loaded skill can never
pass as a refusal) and the refusal regex now matches assistant text
only — the skill BODY echoed into the transcript contains the exact
refusal message, so the old full-surface match could pass vacuously
once the skill loaded. Sibling disk assertions sweep both $HOME/.gstack
and cwd .gstack roots (positives and negatives).

Verified paid: refusal 2x consecutive green with the skill's EXACT
message rendered ('Launching skill: skillify' in-transcript), then the
full file 5/5 green (~$1.35) with both siblings driving real Skill
calls (25-27 turns each).

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

* docs(TODOS): two of three first-execution findings fixed (skillify family, context-restore)

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

* fix(test): context-restore gets a private home — the REAL root cause was fixture sharing

The evidence-based assertion fix was treating a symptom. The slice
artifact's embedded transcript showed the CI agent restoring
20260829-context-save-skill-test.md — the checkpoint the SIBLING
context-save test wrote into the SHARED gstackHome checkpoints dir,
which by filename-prefix ordering genuinely IS the newest. The agent
behaved CORRECTLY; the test's fixture set was open to concurrent
sibling writes, and bun --concurrent ordering differs between CI (save
finished first) and local (restore listed first) — the entire
local-green/CI-red split explained.

The restore test now uses its own .gstack-restore-home (the whole home
moves, not just the handed path — an agent deriving the dir from
GSTACK_HOME/projects/<slug> must land in the closed set too). Full file
4/4 paid green with all evidence flags firing.

Also: the on-failure shard-log artifact glob uploaded nothing — the
Fix-bun-temp step points TMPDIR at /home/runner/.cache, so the spool
lands there, not /tmp. Both eval workflows now glob both locations
(this gap is why diagnosing THIS failure required digging transcripts
out of the slice-results artifact).

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

* fix(evidence): carry the real index mtime onto gstack-wtree's temp copy

The stat-cache seed (cp of the real index) stamped the temp index "now",
which defeats git's racy-git protection: an entry is only re-hashed when
its cached mtime is not older than the index file itself, so a same-size
rewrite landing in the same second as the last real index write looked
non-racy, kept its stale stat-cache entry, and vanished from the
fingerprint — evidence stayed FRESH after a source change. This is the
CI flake in test/evidence.test.ts "allow-paths carve-out" (sub-second
alignment on fast runners: expected STALE exit 1, got FRESH exit 0).

touch -r restores the original index timestamp, reinstating the exact
racy window git itself uses. Deterministic regression pin in
test/review-log.test.ts reproduces the miss with pinned zero-nsec
timestamps (fails on the old script, passes now); receipts: manual
probe shows the fresh-stamped copy returning the clean tree for a
same-size 'hello'→'howdy' rewrite while the mtime-carried copy detects
it.

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

* fix(test): landscape gate bounds the promotion count instead of pinning 3

The alt-hinted image promotion rides the per-render measurement race
already filed in TODOS (2-vs-3 landscape pages on renders seconds
apart — CI receipts from PR #2721, now reproduced locally). Pin the
two deterministic promotions as the floor and the three promotable
blocks as the ceiling (anything above 3 means the veto leaked); the
veto/portrait assertions remain exact.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Test <test@test.com>
garrytan and others added 3 commits August 29, 2026 16:11
…review

# Conflicts:
#	CHANGELOG.md
#	TODOS.md
#	VERSION
#	docs/TESTING_INTERNALS.md
#	package.json
#	scripts/test-free-shards.ts
#	test/helpers/llm-judge.ts
#	test/helpers/touchfiles-data.ts
…main

The v1.73.0.0 slot this branch claimed was superseded when #2721 merged;
same MINOR level relative to main per the versioning invariant. CHANGELOG
entry renumbered (1.73.0.0 was branch-internal and never landed on main),
digest restamped via --regen-digest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dictions don't transfer across machines

The committed duration seed is recorded on fast CI; a syscall-supervised
sandbox replays the same files 2-4x slower. Observed post-merge: a 253-file
shard predicted ~242s was wall-killed at its predicted-x3 725s wall while
genuinely progressing (the old count heuristic guaranteed 1265s). Packed
walls may be looser than the count floor, never tighter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@garrytan garrytan changed the title v1.73.0.0 feat: ponytail import wave — simplification review lens, arm benchmark, reuse ladder, instruction-tier digest v1.75.0.0 feat: ponytail import wave — simplification review lens, arm benchmark, reuse ladder, instruction-tier digest Aug 29, 2026
@garrytan
garrytan merged commit 07b59e3 into main Aug 29, 2026
37 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant