---
name: spt-api-autostarts-daemon
description: Any `spt api ...` invocation autostarts the per-machine daemon, so an e2e that only reads local stores through `spt api` is still HEAVY and still leaks an exe-holding daemon without teardown.
metadata:
  type: project
---

`api::run` (crates/spt/src/api/mod.rs) calls `ensure_daemon()` as its FIRST act
(REQ-DAEMON-3), so **every** `spt api <verb>` spawns a real `spt daemon` tree —
even `psyche-download`, which is otherwise a pure local store read.

**Why:** nextest's <HEAVY> sweep criterion asks what a binary SPAWNS, and it is
natural to answer by reading the verb under test. That answer is wrong one layer
out. Verified 2026-08-01 by counting surviving `spt.exe` processes after a run,
not by reading.

**How to apply:**
- Any `spt` e2e that shells `spt api` is HEAVY AT BIRTH → all three <HEAVY>
  strings (`.config/nextest.toml` override + the two `-E` filters in
  `.github/workflows/golden.yml`); verify with `cargo nextest show-config
  test-groups` and by listing both phases.
- It also owes a `daemon stop --force` + `reap::reap_breadcrumb_daemon` teardown.
  On Windows a leaked daemon HOLDS `target/debug/spt.exe`, so the next
  `cargo build`/`xtask check` in that tree dies with **os error 5** — a red test
  that also breaks the following build.
- The cheap oracle is a process count, not the test's own output:
  `@(Get-CimInstance Win32_Process -Filter "Name='spt.exe'" | Where-Object { $_.ExecutablePath -like '*<worktree>*' }).Count`
  after each run. `spt knock ...` and `spt endpoint list ...` add zero; `spt api`
  adds two (daemon + brain).

**Known unfixed instance (reported, not changed):**
`crates/spt/tests/psyche_download_e2e.rs` has this exact shape — it is in Phase A
and leaks 2 processes per run. It is another wave's evidence file, so moving it
between golden phases was left as a referral rather than a unilateral edit.

See [[fixture-perch-pid-refuses-daemon-stop]] and [[teardown-before-assert]].
