test: cut the golden fixture's duplication and fold three VM tests into one (#312) - #318
Merged
Conversation
The web golden config is the vm config plus the web overlay, so 12 of the 16 files vm rendered were byte-for-byte copies of web's — including the three biggest payloads (supervisor, session CLI, codex remote control) — and AGENTS.robot.md was a third copy of the guide. A two-line edit to one generated script therefore landed twice in the fixture, which is why the tests/golden diff on a small change read as a multiple of the change. Deduplicate at render time: the first path in sorted order owns the bytes and every other path that renders them is one line in DUPLICATES. Measured against real history this drops 20-50% of the golden churn on any commit that touches a shared payload or the seeded guide, and 0% on web-only changes, which is correct — nothing was duplicated there. No coverage is given up. The check still diffs the whole rendered snapshot against the fixture, so a copy that stops matching drops out of DUPLICATES and reappears as its own file in the same diff. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMbiL3ux2XbUTrGQqP15U1
…jail (#312) tests/web-fail2ban.nix, tests/self-serve-domain.nix and tests/download-files.nix were three tests with the same node definition — same services.agent-box block, the same 12-line password-hash activation script, the same curl-only client node — differing only in the Caddyfile they lib.mkForce-swapped in and the assertions they then ran. That is one capability per file rather than one test per capability, and it cost three guest boots to check three routing shapes of the same vhost. Fold them into tests/web-surface.nix: one guest, one client, three named subtests, every assertion and every hard-won comment carried over. CI goes from 7 VM checks to 5, which at --max-jobs 3 is one wave less. Ordering is deliberate. The fail2ban subtest bans the client at the firewall, so it runs last; running it after the self-serve subtest's `systemctl reload caddy.service` also means its "correct password still works" check now proves the {$WEB_PASSWORD_HASH_AGENT} placeholder survives a reload — the exact sequence a real agent puts a box through, and something neither split test could see, because fail2ban never reloaded and the self-serve Caddyfile carried no placeholder. The merged Caddyfile keeps all three shapes the module emits (per-user snippet import, authenticated /<user>/downloads/ handle, authenticated catch-all); `caddy adapt` accepts it both with and without a snippet present. The credential-less 401 the downloads subtest takes still cannot score against the jail: the failregex requires "Authorization":["REDACTED"], so only a supplied wrong credential counts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMbiL3ux2XbUTrGQqP15U1
This was referenced Aug 21, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent reductions for #312, one per commit.
1.
tests/goldenstores identical rendered bytes once.I measured what actually drives the fixture diff rather than guessing. The
webgolden config is thevmconfig plus the web overlay, so 12 of the 16files
vmrendered were byte-for-byte copies ofweb's — including the threebiggest payloads (supervisor 46 KB, session CLI 21 KB, codex remote control
12 KB) — and
AGENTS.robot.mdwas a third copy of the seeded guide. So atwo-line edit to one generated script landed twice in
tests/golden. That isthe multiplier behind "the changes in tests/golden is often way more than the
code being changed".
bin/golden-snapshot.pynow deduplicates at render time: the first path insorted order owns the bytes, and every other path that renders them is one line
in
tests/golden/DUPLICATES. Fixture goes from 67 files to 55.Replayed over every commit since 2026-07-20 that touched the fixture, that is
28% less golden churn overall — 20-50% on anything touching a shared payload
or the guide, and 0% on web-only changes, which is correct because nothing was
duplicated there:
fix(sessions): move the supervisor's observations out of sessions.jsonfix(sessions): lock sessions.json across every read-modify-writedocs(agents): type slash commands into your own panefix(sessions): follow /clear's session-id rotation on respawnfix(webhook): launch claude with --channelsfeat(web): download a session's transcript from its row(web-only)No coverage is given up: the check still diffs the whole rendered snapshot, so a
copy that stops matching drops out of
DUPLICATESand reappears as its own filein the same diff. The header in
DUPLICATESsays exactly that, so adisappearing line does not read as a cleanup.
2. Three VM tests that were the same test become one.
web-fail2ban.nix,self-serve-domain.nixanddownload-files.nixhad thesame node definition — same
services.agent-boxblock, same 12-linepassword-hash activation script, same curl-only client node — and differed only
in the Caddyfile they
lib.mkForce-swapped in. That is one capability per filerather than one test per capability, and it spent three guest boots on three
routing shapes of one vhost.
They are now
tests/web-surface.nix: one guest, one client, three namedsubtest()blocks, every assertion and every hard-won comment carried over.366 lines become 261, and CI goes from 7 VM checks to 5 — one
--max-jobs 3wave less.
Merging also adds coverage. The fail2ban subtest runs last (it bans the
client), which puts it after the self-serve subtest's
systemctl reload caddy.service— so its "correct password still works" check now proves the{$WEB_PASSWORD_HASH_AGENT}placeholder survives a reload. Neither split testcould see that: fail2ban never reloaded, and the self-serve Caddyfile carried no
placeholder.
What I deliberately did not touch
tests/webhook.nix(1949 lines) andtests/sessions.nix(1682) are the fileswith the one-stanza-per-issue growth pattern, and an earlier comment on #312
proposed regrouping them into
subtest()blocks. I decided against it here:an assertion exists, and the rest is ~380 dense assertions each. Coverage is
what makes them long, and I could not find assertions worth deleting.
thing this issue is about.
underneath would be a merge conflict for no behavior gain.
Regrouping them into
subtest()blocks is worth doing once #295 lands, as apure-move commit with no assertion changes, so the diff is reviewable as a move.
A further lever, if you want it (not in this PR)
The remaining golden churn is inherent: a payload's bytes are in the fixture, so
an intentional change to
modules/src/*shows up once in the source and once inthe fixture. It can be cut to near zero by recording payloads as
sha256 + the normalized store references they containinstead of full bytes. Ahash is an equally strong byte-stability lock (a no-op refactor still shows zero
diff), and the reference list still catches wiring changes — but you lose the
readable "here is how the rendered config changed" diff that #154 Phase 0 was
built for, and #295 is the current consumer of exactly that. Your call; I did not
want to remove a review capability you designed in while a refactor is mid-flight.
Closes #312
Test plan
nix build .#checks.aarch64-linux.golden-snapshot— passes with thededuplicated fixture (and still passed before, so the fixture is
cross-arch stable either way)
assemble-module-escaping,download-route,golden-snapshot,module-generated-up-to-date,module-single-file,multi-user,webhook-routenix eval .#checks.x86_64-linux.web-surface.drvPathevaluates; the threeremoved check names no longer resolve;
settings-page,memory-protection,sessions,webhookstill evaluatecheck-testscript.sh tests/web-surface.nix— the driver's owntyandruff --select Fgates on the testScript passcaddy adaptaccepts the merged Caddyfile both with and without a~/sitessnippet present (empty import glob is a warning, as before)web-surfacegets its first realboot here
🤖 Generated with Claude Code
https://claude.ai/code/session_01AMbiL3ux2XbUTrGQqP15U1