Repository navigation
fix(test): wait for writers before removing temp homes to end ENOTEMPTY flakes (lr-c3e80a) - #486
Conversation
…-flight writes (lr-c3e80a) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ENOTEMPTY flakes (lr-c3e80a) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
BOBBIE — clean (0 finding(s), 1 dropped) Dropped candidates (1):
|
|
PEACHES — blocking (2 finding(s), 1 dropped) Verdict reasons (1):
Blocking findings (1):
Advisory findings (1):
Dropped candidates (1):
|
… to the temp-home helper (lr-c3e80a) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…temp-home removal (lr-c3e80a) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
BOBBIE — clean (0 finding(s), 1 dropped) Dropped candidates (1):
|
|
PEACHES — clean (3 finding(s), 1 resolved) Advisory findings (3):
Resolved (1):
|
…drive the pre-SIGKILL identity guard (lr-c3e80a) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
BOBBIE — clean (0 finding(s)) |
|
PEACHES — clean (0 finding(s), 2 dropped) Dropped candidates (2):
|
|
Merged via clagentic-loadout v0.2.0
|
|
This issue has been resolved in version 1.11.0-beta.2. To update, run: |
|
This issue has been resolved in version 1.11.0. To update, run: |
TASK: lr-c3e80a (folds lr-efe464)
What changed (final state, test-only)
One shared helper, test/helpers/remove-temp-home.js (removeTempHome), removes every temp home that a spawned process or in-process async writer may still write into. Order is the fix, not a retry: (0) the caller quiesce step stops in-process timer or debounce writers through their own API; (1) bounded wait for every listed child (ChildProcess or bare pid), failing with the pid if one outlives the bound; (2) stop any live process carrying the harness token (an orphaned grandchild included), SIGTERM then SIGKILL, failing with the pid if one survives; each pid is re-verified (alive, same /proc start time, still carrying the token) before every signal, so a recycled pid is never signalled; (3) let in-flight fs.promises requests finish; (4) bounded removal passes with one shared option set (maxRetries 10, retryDelay 100; the retry is only the last step), then confirm the home stayed gone. No sleep or timeout was lengthened. Without a listable /proc (macOS) the helper does not throw: it still waits for listed children, skips the token scan and logs once (util.debuglog remove-temp-home, or the log option).
Routed through it: test/message-sender-record.test.js (claude + codex loop tests; its world quiesce stops tracked scheduled-input services and flushes every session buffer), test/automation-sender.test.js (loop timers cancelled through quiesce), test/fixtures/daemon-harness.js cleanup, test/shutdown-entry-points-lr-8f12ca.test.js reapDaemon, test/access-harness.js stop (replaced its sleep-and-retry loop; it already destroys the relay before removal). Also fixed waitForExit in the shutdown test and the harness: a signal-killed process has exitCode null, so it hung once cleanup awaited the exit.
Root causes (reproduced on main first)
Timer-scheduled writers (fold round 1)
The drain sees fs requests that exist; a timer that has not fired has none. Sweep of in-process writers into a test home: lib/store.js is a promise chain only (covered by the drain); lib/config.js:326 and :398 are connection timeouts, not writers; lib/project-loop.js saveLoopState is called from retry, watchdog and stop timers; lib/sessions.js:478 is a 50 ms append debounce with the existing flushSessionBuffer API; lib/scheduled-input/service.js:80 re-arms a timer, with the existing stop(). The limit (a timer-scheduled write must be quiesced by the caller) is in the helper JSDoc and in test/remove-temp-home.test.js with a regression test.
Round 2 (review of db4d353)
Race demonstrated, then fixed
test/remove-temp-home.test.js shows the old shape failing deterministically (a late-writing child recreates the removed home; an in-flight store write recreates it; a live orphan writer beats a bare rmSync), then the helper passing each case, plus the bounded-wait failure naming the pid.
Sweep
git grep -nE "rmSync|fs.rm(|rmdir" -- test : 100+ hit lines across 95 files. Only sites where a process or async writer can outlive the test were routed. The rest remove dirs no live writer touches; the boot-smoke style sites already report a removal failure as a diagnostic and were left alone. Class sweep: git grep removeTempHome in test, 7 files, all reviewed.
Tests
The repo profile has no test block (clagentic-loadout test exits 72), so the repo admitted commands were used. Isolated runs, each file alone via node --test, 5 consecutive: test/remove-temp-home.test.js 5/5, test/message-sender-record.test.js 5/5, test/automation-sender.test.js 5/5, test/shutdown-entry-points-lr-8f12ca.test.js 5/5, test/access-gate-integration.test.js 5/5, test/daemon-harness-cleanup-lr-e85fec.test.js 5/5. Full npm test on the head: 3603 tests, 3603 pass, 0 fail, 0 skipped; failing-test-ID set: empty.
Out of scope, observed
No CREW_SOP section 6 record needed: no hook, gate, merge step or test runner changed; tests only, over the repo daemon harness.
🤖 Generated with Claude Code