---
name: breadcrumb-reaper-auth-lane
description: Issue-#32 lane — authenticated breadcrumb reapers in crates/spt/tests; the tests/common sharing rules, the CARGO_TARGET_DIR path-spelling trap that made 0443613's guard refuse its own daemon, and the mock-adapter prebuild contract_e2e needs.
metadata:
  type: project
---

Test-side fix lane (branch `fix/authenticated-test-reapers`, 2026-07-30) for
BigscreenVR/spt-bs-releases#32: `spt` E2Es `taskkill /F /T`-ing a pid read from
`daemon.pid` / `brain.ready` without checking the pid still belongs to the writer.

**Why:** Windows re-mints a freed pid in ~2.2 s (p50 11.7 s under Phase-A churn),
and on the happy path `daemon stop` runs BEFORE those kills — so the breadcrumb
almost always names a freed pid. Victim dies with a bare exit 1, no panic.

**How to apply:**
- Shared guard = `crates/spt/tests/common/reap.rs`. `mod common;` is THE cross-test
  sharing mechanism for the `spt` crate (~40 bins include it) — any new `pub fn`
  there needs a file-level `#![allow(dead_code)]` or every non-caller bin reds
  under `-D warnings`. Cross-CRATE sharing is `spt-test-support` (spt-daemon uses it).
- **Path-spelling trap (cost a false red + a leaked daemon):** comparing
  `exe_path(pid)` to `CARGO_BIN_EXE_spt` as raw strings fails when
  `CARGO_TARGET_DIR` was spelled with forward slashes — the OS returns backslashes.
  Canonicalize BOTH sides. This is what made 0443613's guard panic instead of reap.
- `is_process_alive(pid)` on a breadcrumb pid is an unsound liveness oracle for the
  same reason (a re-minted number reads ALIVE). Use identity: `process_identity`
  (Present(started_at)/Absent/Unproven) + `exe_path`. In job_escape_e2e that bare
  read could FALSE-PASS the survival claim, not just false-red the hygiene gate.
- **`contract_e2e` prerequisite:** 2 of its 4 tests panic "mock-session must be
  built" unless you first run `cargo build -p mock-adapter --bins` (mock-session +
  mock-shell live in `adapters/mock`, not a `crates/` package).
- Census instrument: `.github/ci/reaper-breadcrumb-census.ps1 -Ref <sha>` tiers the
  kill population per breadcrumb and labels TIER-1 members AUTH vs RAW. **Run the
  script, never a hand-rolled grep** — these files discuss `taskkill` in comments
  constantly, so a naive `grep -l taskkill` reports already-ported files (and the
  guard's own regression gate) as exposed. The script skips comment lines and
  requires a read->kill flow within 14 lines; it reads COMMITTED state
  (`git show <ref>:<path>`), so commit before asserting a post-fix census.
- **daemon.pid breadcrumb is CLOSED** (lane `fix/reaper-port-12sites` @c1f52ba,
  stacked on `fix/authenticated-test-reapers`): TIER-1 18, AUTH 18, RAW 0. The 12
  ports were uniform — one cleanup-only reap block per file, replaced by
  `reap::reap_breadcrumb_daemon("<file>", home.path(), &spt_bin, None)`.
- **Convention: `observed = None` is correct for cleanup-only breadcrumb reads.**
  Pass a `reap::observe` pin ONLY when the test held the process while it was
  provably its own (contract_e2e pins right after the daemon writes the breadcrumb).
  A test that first learns the pid in teardown has nothing to pin; image + ancestry
  is the whole guard there.
- **Residual, next lane:** the `brain.ready` breadcrumb is still RAW in
  `crates/spt/tests/brain_split.rs` (kills L131,L150,L194,L196) and
  `twohost_cli.rs` (L218,L220) — same defect class, different breadcrumb.

Related: [[spt-test-conventions]], [[teardown-w1-oracle-soundness]].
