Context
Observed 2026-08-03 on PR #16437's CI (an unrelated diff: fleet-cockpit view + Electron harness files; nothing in ai/daemons/**), which makes the failure environmental rather than change-driven:
✘ test/playwright/integration/ai/daemons/workspaceSafety.spec.mjs:388:5
› Orchestrator workspace-safety integration (#11948 / Sub-5 AC5 of #11837)
› AC4 — swarm-heartbeat target resolver degrades-with-log when selfIdentity is missing (2.8s)
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/neo-workspace-safety-XCsynB'
Job: https://github.com/neomjs/neo/actions/runs/30816553986/job/91695664038 — 49/50 passed; only this teardown failed. The same suite was green on the branch's previous head 40 minutes earlier, same base. Live latest-open sweep: checked latest 10 open issues at 2026-08-03T13:15Z; no equivalent found. A2A claim scan: no overlapping lane.
The Problem
The AC4 case exercises the degrade-with-log path — the resolver keeps running and WRITES A LOG when selfIdentity is missing — and the failure is ENOTEMPTY on removing the spec's temp workspace. That is the classic teardown race genre: the recursive removal walks the directory while the still-settling subject (or its logger) creates one more entry, so rmdir on the parent fires against a directory that just became non-empty again. The assertion half of the test passed; only cleanup lost the race. Every occurrence taxes an entire CI round (~13 min unit + the integration wall-clock) on an unrelated PR.
The Architectural Reality
- The spec provisions a per-test workspace under
/tmp/neo-workspace-safety-* and removes it in teardown.
- The AC4 subject is precisely the one path guaranteed to still be producing filesystem writes at assertion time (its contract IS "degrades with log"), which makes this test the racy one out of the 50 — consistent with 49/50 green.
- Node's own answer to this genre exists:
fs.rm(dir, {recursive: true, force: true, maxRetries, retryDelay}) retries ENOTEMPTY/EBUSY internally — or the teardown awaits the subject's settle (close/flush) before removal.
The Fix
Owner verifies which half is cheapest and correct:
- Settle-then-remove (preferred if the seam exists): await the daemon/logger handle the AC4 case opened (close/flush) before the workspace removal — teardown ordering, not tolerance.
- Tolerant removal: switch the teardown to
fs.rm with maxRetries/retryDelay (or an equivalent bounded retry) so a last-instant write cannot fail the case that already passed its assertions.
Acceptance Criteria
Out of Scope
- The workspace-safety behavior under test (all 50 assertions pass); this ticket is teardown-only.
- CI retry machinery — the fix belongs in the spec's cleanup, not in workflow-level retries.
Related
- Observed on: PR #16437 (unrelated diff, evidence anchor only)
- Suite provenance:
#11948 / #11837 (workspace-safety integration)
Origin Session ID: 65e414e1-48a4-49a0-a430-70a81911f9fc
Retrieval Hint: "workspaceSafety ENOTEMPTY teardown race"
Context
Observed 2026-08-03 on PR
#16437's CI (an unrelated diff: fleet-cockpit view + Electron harness files; nothing inai/daemons/**), which makes the failure environmental rather than change-driven:Job: https://github.com/neomjs/neo/actions/runs/30816553986/job/91695664038 — 49/50 passed; only this teardown failed. The same suite was green on the branch's previous head 40 minutes earlier, same base. Live latest-open sweep: checked latest 10 open issues at 2026-08-03T13:15Z; no equivalent found. A2A claim scan: no overlapping lane.
The Problem
The AC4 case exercises the degrade-with-log path — the resolver keeps running and WRITES A LOG when
selfIdentityis missing — and the failure isENOTEMPTYon removing the spec's temp workspace. That is the classic teardown race genre: the recursive removal walks the directory while the still-settling subject (or its logger) creates one more entry, sormdiron the parent fires against a directory that just became non-empty again. The assertion half of the test passed; only cleanup lost the race. Every occurrence taxes an entire CI round (~13 min unit + the integration wall-clock) on an unrelated PR.The Architectural Reality
/tmp/neo-workspace-safety-*and removes it in teardown.fs.rm(dir, {recursive: true, force: true, maxRetries, retryDelay})retriesENOTEMPTY/EBUSYinternally — or the teardown awaits the subject's settle (close/flush) before removal.The Fix
Owner verifies which half is cheapest and correct:
fs.rmwithmaxRetries/retryDelay(or an equivalent bounded retry) so a last-instant write cannot fail the case that already passed its assertions.Acceptance Criteria
ENOTEMPTYwhile the degrade-with-log subject settles (whichever mechanism, asserted by the repair's own reasoning or a targeted stress loop).Out of Scope
Related
#11948/#11837(workspace-safety integration)Origin Session ID: 65e414e1-48a4-49a0-a430-70a81911f9fc Retrieval Hint: "workspaceSafety ENOTEMPTY teardown race"