---
name: v0121-realharness-reopen
description: "v0.12.0 lifecycle fixes shipped but DON'T work in the real claude-spt harness — reopened as v0.12.1; mock-gating escape; root causes found"
metadata: 
  node_type: memory
  type: project
  originSessionId: 4a8de2fb-1ebb-4dce-b8fb-c6e4ed060c10
---

**v0.12.0 SHIPPED (counter 25) but its lifecycle fixes DON'T deliver in the REAL harness** (operator dogfood, claude-spt, Windows 2026-06-18). Reopened as **v0.12.1** (branch `v0.12.1-lifecycle` off main@5f9ea23; JIT plan `V0.12.1-LIFECYCLE-JIT.md`; 5 REQs minted @a4a76c6). todlando executes, doyle gates per-wave, deployah → v0.12.1 PATCH.

**META-LESSON (binding):** v0.12.0 was GATE-PASSED on **mock adapters + in-proc `reconcile_once`** + tests that explicitly killed+reaped sessions → green tests, broken reality. The escape class we keep hitting ([[shared-seam-change-run-all-seam-tests]], [[ci-clippy-preflight-workspace]] siblings). **v0.12.1 discipline: every lifecycle gate runs against a REAL dummy-harness fixture (operator's idea: a `kind=harness` adapter whose command is a trivial bind-perch+stdout+stay-alive program, driven through the REAL `spt endpoint run`→broker-PTY→rc path) + a REAL detached daemon — NO mocks, NO in-proc reconcile_once.** I'm on LEGACY spt on HFENDULEAM, so spt-core can be freely built + its daemon restarted here (real-harness validation uses the operator's actual claude-spt setup).

**ROOT CAUSES (two layers, both verified-at-code via investigation agents):**
1. **REQ-HAZARD-VIEWER-CLOSE-DETACH (PRIMARY).** Closing the tab/window KILLS the spt-hosted harness (should only detach the view). ROOT: daemon never breaks away from the launching terminal's Windows **Job Object** (`KILL_ON_JOB_CLOSE`); `CREATE_BREAKAWAY_FROM_JOB` used NOWHERE. Both daemon spawn paths (daemon.rs:707, deelevate.rs:519) drop the CONSOLE but not job membership → the daemon's broker-spawned ConPTY harness subtree is reaped on tab-close. ConPTY isolation itself is CORRECT (portable-pty builds the pseudoconsole in the daemon). FIX: add CREATE_BREAKAWAY_FROM_JOB to both paths + pin each broker-spawned harness into a daemon-owned Job Object backstop. pty.rs unchanged. Unix already OK (daemon session-detach). New CONTEXT.md invariant "**A view is independent from the endpoint**" (operator-mandated, tagline by operator).
2. **REQ-HAZARD-ATTACH-WEDGE (robustness).** A dead PTY child + undrained operator pump WEDGES the broker GLOBALLY (new endpoints stall at `PUMP_IPC_READER: spawned`). ROOT: loopback attach output = blocking `write_all` into a bounded 64KB tokio duplex (nethost.rs:1040,1090); a dead operator stops draining → write_all blocks forever ("loopback never hangs" at nethost.rs:1103 is FALSE) → parks workers in the **2-worker** net runtime (nethost.rs:640) → both saturate → all new attach/endpoint-run stall; daemon stop can't join. DISTINCT from the removed B1 path-(c) mutex deadlock (data-plane backpressure, not lock-ordering). FIX: loopback sends fail-fast on a dead/full subscriber (BrokenPipe → end serve_attach), never hold a worker indefinitely.

**BUILD COMPLETE 2026-06-18 (all waves doyle GATE-PASS), branch `v0.12.1-lifecycle` HEAD @9b79a54.** Commits on top of a42e80e (L1.5-plan): bbcf6a6 **L1.5** job-neutral daemon launch (WMI `Win32_Process.Create` PRIMARY via abs-powershell `-EncodedCommand`; breakaway DEMOTED to fallback rung; LaunchRung ladder WMI→schtasks→breakaway→in-job wired into spawn_detached for BOTH cold-start + `daemon start`; `wrapped_daemon_command` forwards `SPT_*` via `cmd /c set & start /b` because WMI/scheduler children DON'T inherit transient env — empirically verified, else wrong-universe collision; int `job_escape_e2e.rs` now CI-testable — WMI escapes any job; ELEVATED deelevate keeps L1 breakaway = follow-up). aa398a1 **P2** picker online→Attach not "Start now" (wrong OPTION SET, not stale read). 68844de **P1** picker fresh endpoint shows under origin project (doyle ruling Option A, operator looped in: union `project_id_for_dir(info.cwd)` into membership, LOCAL-only, pure `merge_origin_project`). 9b79a54 **E1** `endpoint list` always merges local + `--local` REMOVED + cmd_list_local→render_local_section + whoami 2-arg. NOTHING left for todlando to build. NEXT: doyle owns help-md merge LAST (re-dry-run vs @9b79a54, E1 touched cli.rs near help-md run()) → deployah rolls **v0.12.1 PATCH**. doyle follow-ups logged: elevated-case WMI-reparent, direct-COM WMI, Unix setsid guard.

**ALSO real-harness-confirmed:** UNHOST-PSYCHE-REAP (v0.12.0) ALSO fails for real — a dead endpoint left its `{id}-psyche` process alive holding perch files → `endpoint purge` hit "os error 32" until scope-killed. Control plane stayed responsive while only the data plane wedged (matches ATTACH-WEDGE). `ensure_running` respawns a fresh daemon on any CLI find-none (account for in daemon-stop gates). Wave 3 picker bugs (no project-history for fresh endpoints; "Start now" shown for online endpoints) are SEPARATE — investigate root before fixing. Wave 4 = `--local` removal (operator decision 2026-06-17, was dropped, now minted REQ-ENDPOINT-LIST-MERGE-LOCAL). Picker-adapter-description stays deferred. Supersedes the "v0.12.0 COMPLETE/shipped" status in [[v012-lifecycle-milestone]].
