---
name: msg-identity-w1-f036
description: MSG-IDENTITY W1 (F-036 seat-theft) unit tests — identity-env scrub + psyche-custody squat guard, where they live and how to run them
metadata:
  type: project
---

MSG-IDENTITY W1 / F-036 (seat-theft class) unit tests, branch build/msg-identity-w1-f036.

**Two REQs, three test sites:**
- `crates/spt-runtime/src/runtime.rs` [unit->REQ-HAZARD-DAEMON-IDENTITY-ENV-SANITIZE] — 3 tests: `identity_env_scrubbed_even_when_role_declares_no_env_remove` (empty env_remove still scrubs all 3 IDENTITY_ENV_VARS via `command_for`), `identity_scrub_overrides_a_spawn_env_stamp` (scrub is LAST — beats a `with_spawn_env` stamp of SPT_ENDPOINT_ID), `scrub_identity_env_clears_all_identity_vars_from_process` (daemon-startup half). Added `env_removed(&Command)` helper next to existing `env_set` (get_envs → (key,None) = env_remove'd).
- `crates/spt-store/src/psyche_custody.rs` [unit->REQ-BIND-PSYCHE-CUSTODY-SQUAT-GUARD] — 2 tests on `custody_squatter(owlery, sid)`: finds holding psyche by nested dir name / misses others; tolerates missing-nested + corrupt custody file.
- `crates/spt/src/api/startup.rs` [unit->REQ-BIND-PSYCHE-CUSTODY-SQUAT-GUARD] — 2 tests calling private `establish_perch` directly (returns Result<String,BindError>, cleaner than cmd_bind which only returns exit code): squat sid → `BindError::PsycheCustodySquat{id,psyche}` + attacker perch NOT created (guard runs before create_dir_all); unrelated sid binds fine.

**Fixture facts:**
- IDENTITY_ENV_VARS = ["SPT_ENDPOINT_ID","OWL_SESSION_ID","SPT_AGENT_ID"]. No other spt-runtime test mutates these process-env vars (only expand_tilde reads HOME/USERPROFILE) → the set_var→scrub test is single-fn, no serialization needed.
- Psyche custody layout: `owlery/<parent>/nested/<child>/psyche-custody.json`. `write_psyche_sid(&resolve_perch_path("victim-psyche", ParentHint::Infer), sid)` seeds it under isolated_home; `custody_squatter` returns the child dir name ("victim-psyche").
- `establish_perch(id, session_id, parent_pid, adapter, subnet, endpoint_type, controllable, cwd)` — private but tests use `super::*`; needs NO seed daemon (only bind_from_seed takes seeds). Success token.len()==32. Binds fine with zero subnets created.

**Run (host caveats apply — no full spt-daemon --lib):**
- `cargo test -p spt-runtime --lib identity` / `scrub_identity`
- `cargo test -p spt-store --lib custody`
- `cargo test -p spt --bins api::startup` (spt is bin-only — `--bins` not `--lib`; --lib errors "no library targets")
- clippy: `-p spt-runtime -p spt-store` fast; `-p spt` ~12s. All green 2026-07-10.
