Symptom
python3 scripts/run_tests.py intermittently fails its final host-package drift guard:
ERROR: committed host packages are stale relative to source.
1 path(s) differ ... hosts/claude/.claude-plugin/plugin.json
But standalone python3 scripts/build_host_packages.py --check always reports OK: committed host packages are in sync with source, git diff is clean, and build_all is byte-for-byte deterministic across repeated builds. So a test earlier in the suite contaminates process state so that check_drift's in-process scratch rebuild diverges from the committed tree — only when run inside the full suite, not standalone.
Failing test
scripts/test_build_host_packages.py::DriftCheckTests::test_check_passes_when_in_sync
(its assertEqual(code, 0, out.getvalue()) prints the drift text on failure; the
jig-drift-… tempdir prefix in the error confirms it's a DriftCheckTests method).
Inside that test the seed build and the check rebuild both build from REPO_ROOT,
yet _diff_packages reports claude/.claude-plugin/plugin.json differing between them.
Nature of the flakiness
- Per-process random, not test-order (discovery order is sorted/deterministic).
- Separate suite processes give different outcomes (repro'd once, passed several times), consistent with
PYTHONHASHSEED / cold-cache sensitivity.
- Fixed-
PYTHONHASHSEED sweeps (0–7, 12345, 99991) all passed; warm random runs mostly passed. The first run on a clean checkout (cold __pycache__) failed — a leading, still-untested hypothesis is that cold bytecode caches are the trigger (matches "reproduces on a clean checkout").
Ruled out
build_claude_plugin.build copies source .claude-plugin/plugin.json byte-for-byte; output is deterministic given disk state.
- No test rebinds the build /
install_contract / scaffold module attributes (only test_scaffold_mode.py patches scaffold_mod.atomic_write_text, restored in finally).
- No test writes or deletes the real source
.claude-plugin/plugin.json (the .claude-plugin unlink/rmtree calls in tests operate on tempdirs / _seed_valid_repo).
- Passing runs show 0 write/delete/rename ops to source
.claude-plugin (verified with an fs-tracer).
CLAUDE_PLUGIN_ROOT is leaked (set to REPO_ROOT, never restored, by skills/memory-sync/test_memory.py:946 and :1035) — but that's deterministic, so it can't by itself explain a flaky failure. Worth ruling in/out as a contributor and fixing anyway (missing tearDown).
The paradox to crack
A pure byte-copy of an unchanged file should make the seed and check builds identical. So the divergence is most likely one of:
- a presence difference (one tree missing
plugin.json), or
- a source mutation performed by a child process (an fs-tracer scoped to the parent process wouldn't see it), or
- module/global state that changes the build's file enumeration between the two calls.
Next diagnostic step: capture a failing run's actual scratch-vs-committed bytes — e.g. a temporary dump inside check_drift under if drifted: guarded by an env var — to distinguish content-vs-<absent>, plus a child-process-aware fs-trace.
Repro harness gotcha
Running multiple run_tests.py concurrently deadlocks on git worktree/reservation locks and hangs the suite on a review.py record-review subprocess. Sweep for the flake strictly sequentially. (macOS has no timeout; use a Python Popen + watchdog.)
Not yet root-caused. Filed to keep a trace; help/second opinion welcome.
Symptom
python3 scripts/run_tests.pyintermittently fails its final host-package drift guard:But standalone
python3 scripts/build_host_packages.py --checkalways reportsOK: committed host packages are in sync with source,git diffis clean, andbuild_allis byte-for-byte deterministic across repeated builds. So a test earlier in the suite contaminates process state so thatcheck_drift's in-process scratch rebuild diverges from the committed tree — only when run inside the full suite, not standalone.Failing test
scripts/test_build_host_packages.py::DriftCheckTests::test_check_passes_when_in_sync(its
assertEqual(code, 0, out.getvalue())prints the drift text on failure; thejig-drift-…tempdir prefix in the error confirms it's aDriftCheckTestsmethod).Inside that test the seed build and the check rebuild both build from
REPO_ROOT,yet
_diff_packagesreportsclaude/.claude-plugin/plugin.jsondiffering between them.Nature of the flakiness
PYTHONHASHSEED/ cold-cache sensitivity.PYTHONHASHSEEDsweeps (0–7, 12345, 99991) all passed; warm random runs mostly passed. The first run on a clean checkout (cold__pycache__) failed — a leading, still-untested hypothesis is that cold bytecode caches are the trigger (matches "reproduces on a clean checkout").Ruled out
build_claude_plugin.buildcopies source.claude-plugin/plugin.jsonbyte-for-byte; output is deterministic given disk state.install_contract/scaffoldmodule attributes (onlytest_scaffold_mode.pypatchesscaffold_mod.atomic_write_text, restored infinally)..claude-plugin/plugin.json(the.claude-pluginunlink/rmtree calls in tests operate on tempdirs /_seed_valid_repo)..claude-plugin(verified with an fs-tracer).CLAUDE_PLUGIN_ROOTis leaked (set toREPO_ROOT, never restored, byskills/memory-sync/test_memory.py:946and:1035) — but that's deterministic, so it can't by itself explain a flaky failure. Worth ruling in/out as a contributor and fixing anyway (missing tearDown).The paradox to crack
A pure byte-copy of an unchanged file should make the seed and check builds identical. So the divergence is most likely one of:
plugin.json), orNext diagnostic step: capture a failing run's actual scratch-vs-committed bytes — e.g. a temporary dump inside
check_driftunderif drifted:guarded by an env var — to distinguish content-vs-<absent>, plus a child-process-aware fs-trace.Repro harness gotcha
Running multiple
run_tests.pyconcurrently deadlocks on git worktree/reservation locks and hangs the suite on areview.py record-reviewsubprocess. Sweep for the flake strictly sequentially. (macOS has notimeout; use a PythonPopen+ watchdog.)Not yet root-caused. Filed to keep a trace; help/second opinion welcome.