{
  "summary": "Root cause ranking\n\n1. CONFIRMED, deterministic, dominant normal-exit leak: 34 test files store `TempDir` in `static OnceLock<TempDir>` and one more stores it in `static Mutex<Option<TempDir>>`. Rust does not run destructors for statics at process exit, so these homes persist after a passing test. Under nextest, each test is a separate process, so the static leaks once per test process that initializes it—not once per integration-test binary. Census: 35 static-owner files, 134 source activation callsites (114 integration/static-owner callsites plus 20 `spt-msg::test_home::isolate` callsites). `sync.rs` has 3 tests and `digest_cross_node.rs` has 7, hence exactly 10 normal-exit static leaks from those test invocations alone. The parent agent’s Windows reproduction establishes the shape directly: one exact `sync` test leaves one `.tmp…` directory on normal exit; killing it at 1s leaves two—the static home plus the scoped local test directory whose destructor was made unreachable.\n\n2. CONFIRMED hard-kill amplifier: `.config/nextest.toml:14-19` terminates tests after 4 × 60s. On Windows nextest uses a Job Object and immediately terminates the test plus in-job descendants; there is no unwind, so no local `TempDir` destructor. GitHub job cancellation/50-minute timeout has the same destructor-reachability problem. The job-end reap scripts kill processes but never retry/remove tempfile directories.\n\n3. Windows panic/child-retention path: ordinary Rust test panic unwinds scoped locals, but never drops statics. It also skips all manual cleanup below the panic. `std::process::Child` does not kill on drop, and most spawn-heavy tests use end-of-test `kill`/`wait` functions rather than `Drop` guards. If a surviving child holds a cwd or non-delete-sharing handle below the temp home, Windows can reject `remove_dir_all`; tempfile’s `Drop` silently discards that error, and killing the child later does not retry deletion. This is a valid secondary leak mechanism, but the normal `sync` reproduction proves it is not needed to explain the baseline leak.\n\n4. Detached-daemon escape path: `spawn_detached` deliberately launches a job-neutral Windows daemon through WMI → schtasks → breakaway → in-job. WMI/schtasks daemons are outside nextest’s per-test Job Object and can outlive a panic/timeout until the CI path-scoped reap. Graceful `daemon stop` does correctly raise the brain supervisor stop flag and reap the brain subtree, but tests that panic before stop do not reach it. Again, this amplifies/locks temp homes but is not required for the static leak.\n\n5. Ruled out as a repository-wide cause: there are no `TempDir::keep`, `into_path`, `disable_cleanup`, tempfile `persist`, or `mem::forget(TempDir)` callsites. `transport_death_eof.rs:640-644` intentionally forgets a frozen `Broker` runtime to avoid teardown hanging, not its `TempDir`. Linux-only zombie-oracle tests intentionally forget `Child` values, also unrelated to Windows `.tmp` directories.\n\n14,319-directory attribution\n\nThe basename alone cannot identify one helper or test family: Cargo.lock pins tempfile 3.27.0, whose default builder prefix is `.tmp`, so every unprefixed `tempdir()`/`TempDir::new()` call in every crate produces the same `.tmpXXXXXX` shape. Therefore 14,319 cannot be exactly apportioned from names alone. It does strongly map to the static-home family as a bulk source: 35 static owners and at least 134 activation sites exist, and sampled live artifacts map directly to those owners—`spawn_truth` homes contain `freshconflict-*`, `mixedrace-*`, `freshn1-*`, `zombierun-*`, and `handlecorpse-*`; `transport_death_eof` homes contain `tdeof-clean-*`/`tdeof-torn-*`. [INFERENCE] The total is accumulated across many nextest invocations plus abnormal-kill local directories, not 14,319 instances from one helper.\n\nSmallest source fix\n\nReplace all static `TempDir` ownership with a scoped, panic-safe per-test `HomeGuard`, modeled on `crates/spt/src/testutil.rs:15-49`. `init_home` should return the guard and each test should bind it first (`let _home = init_home();`), making the home the last local dropped. The guard owns the prior environment and `TempDir`; spawn-heavy tests must additionally own a process-tree/broker guard declared after the home, whose `Drop` performs stop/kill-tree plus wait/join. This removes the guaranteed normal-exit leak and lets panic unwind run cleanup. Do not merely swap `OnceLock<TempDir>` for another static wrapper; static destructors remain unreachable. Do not rely on `Child` drop.\n\nA complete hard-kill guarantee cannot live inside the killed test process. Give both nextest phases a run-scoped `TEMP`/`TMP` root and, after the existing job-end process reap, remove that root from the still-live CI parent. This is the correct outer cleanup seam for timeout/cancellation. Setting nextest `leak-timeout = { …, result = \"fail\" }` is useful classification hardening but not cleanup: current config omits it, so the documented default is 100ms and detected inherited-stdio leaks pass as `LEAK`; null-stdio/job-neutral daemons are not detected.\n\nDeterministic regression proof\n\nUse an out-of-process fixture controlled by a parent test/support seam. The fixture creates a home under a parent-supplied run temp root, writes its exact path to a marker, and supports three modes: normal return, panic, and block after spawning a descendant that holds cwd/open state below the home. Parent assertions: (a) normal and (b) panic both leave zero directory delta after scoped `HomeGuard` plus process guard teardown; (c) force-kill the blocked fixture with the same Windows Job/process-tree mechanism, wait for every process handle, first prove the directory survives because the child destructor was unreachable, then invoke the exact post-reap run-root sweeper and assert the directory is gone. The kill leg must test the parent sweeper—not `TempDir::Drop`, which by definition cannot run. Retain the proven `sync` exact-test zero-delta check as the smallest regression for removing static ownership.",
  "files": [
    {
      "path": ".config/nextest.toml",
      "description": "Lines 14-19 set `slow-timeout = { period = \"60s\", terminate-after = 4 }`; lines 43-44 serialize the heavy group; lines 245-306 define Windows Phase-A concurrency but no `leak-timeout`, no temp-root isolation, and no cleanup hook."
    },
    {
      "path": ".github/workflows/ci.yml",
      "description": "Lines 108-145 define job timeout and pre-test process reap; lines 180-212 run nextest Phase A/B; lines 348-381 run the always-on job-end process reap. No step removes/retries test temp-directory cleanup."
    },
    {
      "path": ".github/ci/reap-census.ps1",
      "description": "Windows parent-side process census/reap. It settles, path-verifies, force-stops named test process families, waits 1.5s, and reports survivors; it never sweeps `.tmp` directories. This is the correct parent seam after which a run-scoped TEMP root can safely be removed."
    },
    {
      "path": "Cargo.lock",
      "description": "Lines 4273-4276 pin `tempfile` 3.27.0. Its unconfigured builder names both files and directories with the generic `.tmp` prefix, so directory names do not encode a test family."
    },
    {
      "path": "crates/spt/src/testutil.rs",
      "description": "Lines 15-49 contain the existing scoped `HomeGuard`: process-global lock, local `TempDir`, prior-SPT_HOME restoration in `Drop`. This is the repository pattern the static-home tests should reuse/emulate."
    },
    {
      "path": "crates/spt-daemon/tests/sync.rs",
      "description": "Lines 48-57 hold `TempDir` in static `OnceLock`; lines 226-229, 353-356, and 520-523 initialize it in all three tests and separately allocate scoped broker dirs. Windows reproduction: normal exact run leaks static home only; forced kill leaks static plus scoped dir."
    },
    {
      "path": "crates/spt-daemon/tests/digest_cross_node.rs",
      "description": "Static `OnceLock<TempDir>` home at lines 49-56; seven test invocations activate it, yielding seven deterministic normal-exit leaks under process-per-test nextest."
    },
    {
      "path": "crates/spt-daemon/tests/notif_quiet_delivery.rs",
      "description": "Lines 29-40 are the 35th equivalent static owner, using `Mutex<Option<TempDir>>` instead of OnceLock. It is still static and its inner TempDir is not dropped at process exit."
    },
    {
      "path": "crates/spt-msg/src/lib.rs",
      "description": "Lines 29-51 define `test_home::isolate` around static `OnceLock<TempDir>`; 20 unit-test activation callsites become separate leaked homes under nextest’s process-per-test execution."
    },
    {
      "path": "crates/spt-daemon/tests/spawn_truth.rs",
      "description": "Lines 32-40 define static HOME. Markers at lines 137, 210, 253, 331, and 419 match sampled persistent `%TEMP%\\.tmp*` directory contents exactly."
    },
    {
      "path": "crates/spt-daemon/tests/transport_death_eof.rs",
      "description": "Lines 185-192 define static HOME; line 442 allocates a scoped broker dir; lines 640-644 intentionally `mem::forget(broker_b)` for a frozen runtime. Sampled leaked HOME contents (`tdeof-clean-*`, `tdeof-torn-*`) identify this family; the forgotten object is a Broker, not a TempDir."
    },
    {
      "path": "crates/spt-daemon/src/daemon.rs",
      "description": "Lines 745-867 define detached daemon semantics and Windows breakaway; lines 1593-1680 implement WMI→schtasks→breakaway→in-job job-neutral ladder. These children can evade nextest’s job and survive until explicit CI reap."
    },
    {
      "path": "crates/spt-daemon/src/reap.rs",
      "description": "Lines 1-25 describe daemon brain-subtree ownership; lines 49-104 implement enroll/reap; Windows lines 107-205 use KILL_ON_JOB_CLOSE and TerminateJobObject. This is production graceful/crash subtree cleanup, distinct from test TempDir cleanup."
    },
    {
      "path": "crates/spt-daemon/src/brainproc.rs",
      "description": "Lines 1114-1128 create the Unix brain process group; lines 1186-1190 enroll each Windows/Unix brain in BrainReaper. Supervisor kill paths call kill+wait, but none make killed test-process destructors reachable."
    },
    {
      "path": "crates/spt-daemon/src/daemon.rs:362-407",
      "description": "Daemon run creates BrainReaper, and graceful stop first stops supervisor respawn then calls `reaper.reap()`. A test panic before the stop request skips this path."
    },
    {
      "path": "crates/spt-daemon/tests/redispatch.rs",
      "description": "Lines 136-138 show a representative manual consuming `Generation::kill(self)` rather than a Drop guard; panic before the call leaks the child until the outer runner/job cleanup."
    },
    {
      "path": "crates/spt-daemon/tests/resume_custody_aba.rs",
      "description": "Lines 55-59 show the uncommon correct child RAII pattern (`Drop` calls kill+wait), suitable as a local model for spawn-heavy test guards."
    }
  ],
  "architecture": "Static-owner census (34 `OnceLock<TempDir>` + 1 equivalent static Mutex owner):\n\n`crates/spt-daemon/tests/{access,attach,attach_idempotent_replay,attach_resize_capture,attach_resize_repaint,brain_decouple,brain_resume_conn_deadlock,conn_blackhole_lifecycle,control_stamp_lifetime,controller_lease,digest_cross_node,dispatch,driven_by_selfheal,endpoint_lifecycle,false_promote,inject_control_wedge,input_ack_deadlock,mesh,mesh_recovery,notif_quiet_delivery,pump,redispatch,redispatch_stall,registry_lifecycle,render_lifecycle,resize_geometry_epoch,resize_presentation_barrier,restart_replay_lifetime,resume,spawn_truth,sync,transport_death_eof,wanmsg}.rs`, plus `crates/spt-msg/src/lib.rs` and `crates/spt-msg/tests/killer_quickstart.rs`.\n\nLifecycle model:\n\n1. nextest launches one libtest process per exact test.\n2. Test initializes static HOME and often one or more scoped TempDirs.\n3. Passing test: scoped locals drop; static HOME never drops → one guaranteed directory remains per activated static owner/test process.\n4. Panicking test: scoped locals unwind, but manual child cleanup after the panic is skipped and static HOME still never drops. Windows handle/cwd retention can make scoped TempDir Drop fail silently.\n5. Leaky classification only observes inherited stdout/stderr remaining open. Current config does not fail `LEAK`; detached/null-stdio descendants are invisible.\n6. Timeout on Windows: nextest terminates the Job Object immediately, so neither scoped nor static destructors run. An intentionally job-neutral daemon may be outside that Job.\n7. CI’s `always()` reaper later kills path-scoped process families, releasing handles, but performs no filesystem retry; already-failed TempDir deletion remains failed forever.\n\nWindows tempfile semantics are therefore two distinct causes, not one: (a) destructor unreachable (static or forced termination), which is platform-independent and proven here; (b) destructor reached but `remove_dir_all` rejected by live Windows cwd/handle state, whose error tempfile Drop suppresses and which requires child-first teardown plus an explicit/outer retry. Official nextest references used to verify runner behavior: https://nexte.st/docs/features/slow-tests/ and https://nexte.st/docs/features/leaky-tests/."
}