Skip to content

fix(dogfood): close the confine-tamper pins race at its root (exit-wait)#119

Merged
m1ngshum merged 1 commit into
mainfrom
fix/dogfood-confine-pins-race
Jul 3, 2026
Merged

fix(dogfood): close the confine-tamper pins race at its root (exit-wait)#119
m1ngshum merged 1 commit into
mainfrom
fix/dogfood-confine-pins-race

Conversation

@m1ngshum

@m1ngshum m1ngshum commented Jul 3, 2026

Copy link
Copy Markdown
Member

Symptom

The confine dogfood (macOS) CI leg intermittently fails:

FAIL: tamper: failed closed on pins, not confine — gate not isolated

It re-appeared on a docs-only PR (#118), which proves it's nondeterministic — identical code to the green #117. The merge still landed because this leg is advisory (not a required check), but a flaky gate erodes trust and keeps pinging.

Root cause

driveHappy() resolved the instant the 3rd JSON-RPC response arrived and called child.kill() without waiting for the child to exit. The relay child does an off-thread TOFU pins write (pins.json + .integrity) triggered by the tools/list; that write was still in flight when control returned to the tamper step's rmSync, so it landed after the delete — recreating a partial pins state. The tampered run's readPins then raised PINS-READ-ERROR and failed closed for an unrelated reason, masking the CONFINE hash-mismatch gate under test.

#114 deleted the whole pins sidecar set to fix this, but a point-in-time delete can't beat an async writer in another process.

Fix

driveHappy() now resolves only on the child's exit event (2s SIGKILL escalation if SIGTERM stalls). Once the process is gone, no write from it can land after the rmSync, so the delete is final and the tamper run sees a clean pins state — exercising the CONFINE gate as intended.

Test-harness only — no product code. The confine enforcement itself was always correct (secret read denied EPERM passes every run). Verified 3× locally; the real proof is this PR's own macOS leg.

The macOS confine dogfood intermittently failed the tamper assertion with
'failed closed on pins, not confine — gate not isolated' (seen again on a
docs-only PR #118, proving it's nondeterministic). #114 narrowed this by deleting
the whole pins sidecar set before tampering, but couldn't beat the real cause: an
async writer in another process.

Root cause: driveHappy() resolved the instant the 3rd JSON-RPC response arrived
and did child.kill() WITHOUT waiting for exit. The relay child's off-thread pins
TOFU write (pins.json + .integrity, triggered by tools/list) was still in flight
when control returned, so it landed AFTER the tamper step's rmSync — recreating a
partial pins state, so the tampered run's readPins raised PINS-READ-ERROR and
failed closed for an UNRELATED reason, masking the CONFINE gate under test.

Fix: driveHappy() now resolves only on the child's 'exit' event (with a 2s
SIGKILL escalation if SIGTERM stalls). Once the process is gone, no write from it
can land after the delete, so the rmSync is final. Test-harness only — no product
code; the confine enforcement path itself was always correct (secret read denied
EPERM). Verified 3x locally.
@m1ngshum m1ngshum merged commit 9dc27af into main Jul 3, 2026
8 checks passed
@m1ngshum m1ngshum deleted the fix/dogfood-confine-pins-race branch July 3, 2026 03:34
m1ngshum added a commit that referenced this pull request Jul 3, 2026
Developer-reach release: Claude Code (~/.claude.json) becomes a first-class
client (D1). First release to attach the CycloneDX SBOM correctly (v0.17.0's
attach step tripped over GitHub immutable releases; fixed in #116). Also folds in
the confine macOS dogfood race fix (#119). Version is derived from the git tag.
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