1	- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) IN PROGRESS by todlando: harnesshost spawn fn + rc pump + CLI all built+green; BLOCKER pinged doyle — iroh refuses self-dial ("Connecting to ourself is not supported"), local attach needs fallback transport (B1 broker loopback-conn rec / B2 local-direct). Cross-node rc already works
2	- [M9 status](m9-wave1-progress.md) — M9 COMPLETE + SHIPPED: all 4 gates passed (G1/G2/Gateway-WAN/G3), all PRs merged to main (operator GO 2026-06-13), **v0.5.0 tagged** (counter 10, deployah signing). Don't re-plan. See [[v050-published]] for release state
3	- [spt-claude-code NEXT](spt-claude-code-next.md) — SCOPING DONE 2026-06-14 (SCOPE.md/CONTEXT.md committed @09d6020). M12 handed to todlando as GATING prereq (now executing W1). RESUME = author experimplate → instantiate spt-claude-code → write perri HANDOFF. perri builds from spt-releases+GH-Pages ONLY
4	- [M10 digest extractor](m10-digest-extractor.md) — doyle GATE PASS 2026-06-13: all 4 waves (39463a2..afa0561), REQ-TERM-5/6/7 evidenced, full suite + capstone E2E green, independently verified. `[digest]` imperative extractor seam (ADR-0019, reverses M9) + thread-spanning (session ledger) + two-origin merge + digest-proof + skip-diagnostics. SHIPPED as v0.6.0 (counter 11) — see [[v060-published]]. Carried: real-CC digest-proof → perri/spt-claude-code acceptance criterion
5	- [v0.6.0 published](v060-published.md) — M10 PUBLIC 2026-06-13 (counter 11); todlando merge+tag, deployah sign+publish; minor bump (0.5.0→0.6.0); hashes linux 002ed710… win 4f5c61e8…. ROUTINE seamless roll now (respawn-path arc closed); publisher leg proven 5× (counter 7-11). enlyzeam still pending natural-propagation confirm
6	- [traceable per-wave activation](traceable-per-wave-activation.md) — traceable-reqs check requires ALL declared required_stages evidenced per commit; activate stages incrementally as evidence lands (int at final wave), never the full end-state list upfront
7	- [v0.5.0 published](v050-published.md) — M9 PUBLIC 2026-06-13 (counter 10); todlando drove merge+tag, deployah signed+published (handoff split vs v0.4.x full-solo); minor bump (breaking [pty_digest] removal pre-1.0); hashes linux 4c079100… win 984298ba…. FLEET ROLL = next phase, operator-gated (their ask was merge+publish, DONE). v0.4.2→v0.5.0 kitsubito apply = FIRST genuine Linux seamless no-bounce proof on fixed broker; bytes-assert→auto-rollback armed. deployah on-call for hash check
8	- [v0.5.0 release](v050-release.md) — M9 SHIPPED: merged to main (22a9b6f, clean stack collapse; #8 auto-closed by mid-stack --delete-branch, content rode via #9), v0.5.0 tagged (0.4.2→0.5.0 minor: feature milestone + breaking [pty_digest] removal), counter 10, deployah signing. LESSON: don't --delete-branch mid-stack
9	- [M9 Wave3/G3 digest](m9-wave3-digest-progress.md) — session-digest re-founded on session logs (REQ-TERM-4, ADR-0008 amendment); G3 STAMPED PASS by doyle 2026-06-13, PR#10 held. On-demand projection (topology-independent, no PTY); PTY-parse retired; published digest-record contract; autonomous-freshness+Option-C deferred to consuming-frontend milestone (DEFERRED.md:44)
10	- [hfenduleam target bloat → CI RED](hfenduleam-target-bloat-ci-red.md) — Win-runner LNK1318/os-error-112 can be disk-full from cargo target bloat (396GB seen), NOT a code regression; cargo clean + re-run; check disk before treating a Windows RED as a code defect
11	- [v0.4.1 Linux respawn blocker](v041-linux-respawn-blocker.md) — v0.4.1 PUBLIC (counter 8); fleet roll found Linux brain-respawn bug (current_exe follows apply-rename → brain runs OLD bytes, records applied:8). hfenduleam(Win) GREEN, kitsubito(Linux) RED. RESOLVED by v0.4.2 — see [[v042-published]]
12	- [v0.4.2 published](v042-published.md) — PUBLIC 2026-06-11 (counter 9); FIXES v0.4.1 Linux respawn blocker (respawn via canonical install path not current_exe + bytes-assert→auto-rollback; REQ-HAZARD-BRAIN-RESPAWN-PATH + real-rename E2E). deployah drove full merge→tag→sign→publish solo. hashes linux 500a9e7b… win b9ec8ddd…. ASYMMETRY: v0.4.2 apply still rides buggy v0.4.0 broker → 1 final manual Linux bounce loads fix; seamless proof on NEXT update via debug-channel. todlando driving fleet roll + restoration close
13	- [pump-IPC-deadline fix](pump-ipc-deadline-fix.md) — DONE: MERGED to main @ 4d8b810 (PR #3), doyle-approved, CI green both runners; reader-thread+channel carrier (nonblock dead on Win named pipes), both amendments folded, no tag (rides v0.4.1). Don't re-plan
14	- [CI self-host migration](ci-selfhost-migration.md) — DONE: runners live on kitsubito + hfenduleam (gravity retired 2026-06-07); twohost jobs gated by [twohost] commit tag; don't re-plan
15	- [which process is the listener](spt-daemon-is-live-infra.md) — owl.exe (plugin) = live listener, hands off; target\debug\spt.exe daemon = dev/test artifact, killable
16	- [self-clear skill API-blocked](self-clear-skill-api-blocked.md) — never invoke /self-clear; usage-policy classifier refuses the turn; continue in-context / user-manual /clear instead
17	- [Adapter glue-model boundary](adapter-glue-model-boundary.md) — adapters = manifest + binary only; real adapters in own repos; no adapter-aware source in spt-core
18	- [M6 shipped v0.1.0](m6-shipped-v010.md) — v0.1.0 public 2026-06-05; release procedure = RELEASE-RUNBOOK.md; releases-repo/ is repo-face truth; don't re-plan M6
19	- [Gravity deploy procedure](gravity-deploy-procedure.md) — ssh david@100.100.62.43; build in ~/spt-core-deploy NOT runner workspace; setsid daemon; ~1 min first-sync after restart
20	- [Linux SPT_HOME no-bug](linux-spt-home-no-bug.md) — unix spawn_detached inherits env (no env_clear), forwards SPT_HOME; only Win CreateEnvironmentBlock rebuilds/drops it (f99dc25). Default-home daemon = launched w/o the var, not a propagation bug; don't reopen
21	- [kitsubito Linux rig](kitsubito-linux-rig.md) — ssh reavus@kitsubito (key auth); cargo at ~/.cargo/bin; deploy checkout origin=local runner (NOT GitHub) so unpushed commits transfer via git bundle; live spt-daemon user unit, --user ops need no sudo; build-in-scratch leaves ~/.local/bin/spt untouched
22	- [Use $OWL send for messaging](owl-send-not-legacy-spt-send.md) — `$OWL send` works (legacy = where live perches run); `spt send` is the NEW spt-core binary but no perches on it yet → bounces NO_PERCH; don't read `$?` after piping send through tail (use PIPESTATUS)
23	- [daemon service-detection gotcha](daemon-service-detection-gotcha.md) — service detection reading GLOBAL OS state is blind on a single-home non-elevated dev box; gate Linux service routing on SPT_HOME-unset, Windows run-refuse on would-vanish (not is_elevated); push→CI BEFORE tagging a release (v0.3.0 tagged early, CI caught 2 regressions, retagged)
24	- [v0.3.2 platform-safe update](v032-platform-safe-update.md) — REQ-UPD-8 PUBLISHED + rig-verified 2026-06-09 (counter 6): fetch stages multi-platform update-set, apply refuses unstamped/wrong-OS single. Fleet kitsubito+hfenduleam=0.3.2 (ENLYZEAM TODO). GOTCHA: fetch/pump floor=staged_version → can't re-stage same version single→set; clear `$SPT_HOME/releases` first (Linux ~/.spt-core, Win %LOCALAPPDATA%\spt-core). CORRECTION: release-publish never built a SignedUpdateSet pre-v0.3.2 (was debug_rollout)
25	- [boot-race self-heal](boot-race-self-heal.md) — v0.3.1: daemon self-heals a net-less boot start (NetHost retry+attach to OnceLock broker); systemd-USER network-online ordering does NOT reliably prevent the race under linger (field-proven kitsubito) — the self-heal is the real guarantee, unit ordering is belt
26	- [v0.4.0 published + fleet rolled](v040-published-fleet-rolled.md) — restoration release PUBLIC 2026-06-10 (counter 7); D7-4 bytes-match done both nodes (brain.ready exe_hash == artifact); v0.3.2→v0.4.0 apply needed last manual bounce (in-process can't self-relaunch); seamless no-bounce proof + enlyzeam catch-up + milestone close all deferred to v0.4.1

... [3 lines truncated] ...


please write the perri handoff now while it's fresh in your context. just don't message them about it yet.


Align spt-claude-code with spt-core v1 requirements


Decision sent (B1, scoped as W1.5). Updating the progress memory so the blocker isn't left stale:


1	---
2	name: m12-w1-progress
3	description: "M12 Wave 1 (spt endpoint run + spt rc) build state, decisions, and the self-dial-dead blocker"
4	metadata:
5	  node_type: memory
6	  type: project
7	  originSessionId: 1ae6a063-4622-4f5b-86b8-b4506d12ccfb
8	---
9
10	M12 W1 (spt-hosted harness bringup + `spt rc` PTY attach) — doyle handed execution to todlando 2026-06-14. Full spec: `M12-PLAN.md` + `M12-W1-PLAN.md` (durable build plan, resumable).
11
12	**Built + green:**
13	- Daemon `spt-daemon/src/harnesshost.rs`: `prepare_harness_spawn` (validate kind=harness + fill `[session.self]` template) + `launch_harness_brokered_in` (spawn into broker PTY via `spawn_session_pid`, mirrors `shellhost::launch_shell_brokered_in`) + `mint_session_id`. 3 unit tests.
14	- `spt/src/rc.rs`: operator attach pump (single-Brain main thread + stdin reader thread→mpsc, `cold_start_pump` Split carrier for bounded reads, seq-dedup, ctrl-b detach state machine). 3 unit tests.
15	- cli.rs: `EndpointCmd::Run` + `cmd_endpoint_run`; top-level `Cmd::Rc`; crossterm dep added.
16	- REQs registered: REQ-HOST-RUN-1 (spawn), REQ-RC-1 (attach), REQ-HOST-RUN-2 (project cwd — doyle-mandated, must land before M12 gate; W1 ships broker-inherited cwd as proof-shortcut only).
17
18	**KEY DECISIONS (doyle-validated):** spawn-first (harness self-registers its perch on bind via `api/startup.rs establish_perch`, no daemon pre-register); id rides argv via `{id}` template (NO SpawnReq wire change); one-pump invariant = transport swappable under one `serve_attach` pump.
19
20	**BLOCKER (pinged doyle, awaiting ruling):** loopback self-dial is IMPOSSIBLE — iroh refuses `net_dial(own_addr)` with "Connecting to ourself is not supported" (pinned green test `loopback_self_dial_is_refused_local_uses_fallback_transport`). Cross-node rc already works (proven attach.rs primitives). LOCAL attach needs a fallback transport: (B1) broker in-process loopback-conn [todlando rec, max purity] vs (B2) local-direct brain.attach+Output+send_effect [lighter, one pump-branch]. rc.rs currently codes the now-refused self-dial; swap conn-acquisition to chosen B. Pump is transport-invariant.
21
22	**NEXT:** doyle picks B1/B2 + W1-vs-W1.5 scope → build local transport → local attach E2E int test → activate REQ stages (impl+unit now, int at E2E) → `traceable-reqs check` EXIT=0 → doyle W1 gate. Commit only when user asks (rule: not on default branch unprompted). See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (downstream consumer perri waits on M12 gate).
23

---
name: m12-w1-progress
description: "M12 Wave 1 (spt endpoint run + spt rc) build state, decisions, and the self-dial-dead blocker"
metadata:
  node_type: memory
  type: project
  originSessionId: 1ae6a063-4622-4f5b-86b8-b4506d12ccfb
---

M12 W1 (spt-hosted harness bringup + `spt rc` PTY attach) — doyle handed execution to todlando 2026-06-14. Full spec: `M12-PLAN.md` + `M12-W1-PLAN.md` (durable build plan, resumable).

**Built + green:**
- Daemon `spt-daemon/src/harnesshost.rs`: `prepare_harness_spawn` (validate kind=harness + fill `[session.self]` template) + `launch_harness_brokered_in` (spawn into broker PTY via `spawn_session_pid`, mirrors `shellhost::launch_shell_brokered_in`) + `mint_session_id`. 3 unit tests.
- `spt/src/rc.rs`: operator attach pump (single-Brain main thread + stdin reader thread→mpsc, `cold_start_pump` Split carrier for bounded reads, seq-dedup, ctrl-b detach state machine). 3 unit tests.
- cli.rs: `EndpointCmd::Run` + `cmd_endpoint_run`; top-level `Cmd::Rc`; crossterm dep added.
- REQs registered: REQ-HOST-RUN-1 (spawn), REQ-RC-1 (attach), REQ-HOST-RUN-2 (project cwd — doyle-mandated, must land before M12 gate; W1 ships broker-inherited cwd as proof-shortcut only).

**KEY DECISIONS (doyle-validated):** spawn-first (harness self-registers its perch on bind via `api/startup.rs establish_perch`, no daemon pre-register); id rides argv via `{id}` template (NO SpawnReq wire change); one-pump invariant = transport swappable under one `serve_attach` pump.

**BLOCKER (pinged doyle, awaiting ruling):** loopback self-dial is IMPOSSIBLE — iroh refuses `net_dial(own_addr)` with "Connecting to ourself is not supported" (pinned green test `loopback_self_dial_is_refused_local_uses_fallback_transport`). Cross-node rc already works (proven attach.rs primitives). LOCAL attach needs a fallback transport: (B1) broker in-process loopback-conn [todlando rec, max purity] vs (B2) local-direct brain.attach+Output+send_effect [lighter, one pump-branch]. rc.rs currently codes the now-refused self-dial; swap conn-acquisition to chosen B. Pump is transport-invariant.

**NEXT:** doyle picks B1/B2 + W1-vs-W1.5 scope → build local transport → local attach E2E int test → activate REQ stages (impl+unit now, int at E2E) → `traceable-reqs check` EXIT=0 → doyle W1 gate. Commit only when user asks (rule: not on default branch unprompted). See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (downstream consumer perri waits on M12 gate).


Edit

**BLOCKER (pinged doyle, awaiting ruling):** loopback self-dial is IMPOSSIBLE — iroh refuses `net_dial(own_addr)` with "Connecting to ourself is not supported" (pinned green test `loopback_self_dial_is_refused_local_uses_fallback_transport`). Cross-node rc already works (proven attach.rs primitives). LOCAL attach needs a fallback transport: (B1) broker in-process loopback-conn [todlando rec, max purity] vs (B2) local-direct brain.attach+Output+send_effect [lighter, one pump-branch]. rc.rs currently codes the now-refused self-dial; swap conn-acquisition to chosen B. Pump is transport-invariant.

**NEXT:** doyle picks B1/B2 + W1-vs-W1.5 scope → build local transport → local attach E2E int test → activate REQ stages (impl+unit now, int at E2E) → `traceable-reqs check` EXIT=0 → doyle W1 gate. Commit only when user asks (rule: not on default branch unprompted). See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (downstream consumer perri waits on M12 gate).

**BLOCKER RESOLVED (doyle ruling 2026-06-14):** loopback self-dial IMPOSSIBLE (iroh refuses `net_dial(own_addr)`, pinned green test). doyle picked **B1 (broker in-process loopback-conn — local rides byte-identical pump+serve path, covered by cross-node tests; B2's pump-branch rejected as the lesser-tested local path warned against), scoped as a tight W1.5** (NOT in W1). Rationale: cross-node attach + endpoint-run bringup + pump are built+green → gate W1 NOW (bringup verifiable via cross-node attach to the spawned PTY); B1 is a sensitive broker-layer addition (6.7 zone) deserving its own focused task+gate; local lands W1.5 before M12 close. One-pump assertion (both transports → one pump) kept in W1.5.

**REQ-RC-1 split:** cross-node int evidence at W1 gate; local int evidence at W1.5 (per-wave activation).

**NEXT:** todlando → ping doyle for **W1 gate** (bringup + cross-node rc + pump, green) → then **W1.5** = build B1 broker loopback-conn + local attach E2E int → activate REQ-RC-1 local int → traceable check → W1.5 gate. Then REQ-HOST-RUN-2 (cwd) before M12 close. Commit only when user asks. See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (perri waits on M12 gate).


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\m12-w1-progress.md has been updated successfully. (file state is current in your context — no need to Read it back)

**BLOCKER RESOLVED (doyle ruling 2026-06-14):** loopback self-dial IMPOSSIBLE (iroh refuses `net_dial(own_addr)`, pinned green test). doyle picked **B1 (broker in-process loopback-conn — local rides byte-identical pump+serve path, covered by cross-node tests; B2's pump-branch rejected as the lesser-tested local path warned against), scoped as a tight W1.5** (NOT in W1). Rationale: cross-node attach + endpoint-run bringup + pump are built+green → gate W1 NOW (bringup verifiable via cross-node attach to the spawned PTY); B1 is a sensitive broker-layer addition (6.7 zone) deserving its own focused task+gate; local lands W1.5 before M12 close. One-pump assertion (both transports → one pump) kept in W1.5.

**REQ-RC-1 split:** cross-node int evidence at W1 gate; local int evidence at W1.5 (per-wave activation).

**NEXT:** todlando → ping doyle for **W1 gate** (bringup + cross-node rc + pump, green) → then **W1.5** = build B1 broker loopback-conn + local attach E2E int → activate REQ-RC-1 local int → traceable check → W1.5 gate. Then REQ-HOST-RUN-2 (cwd) before M12 close. Commit only when user asks. See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (perri waits on M12 gate).

---
name: m12-w1-progress
description: "M12 Wave 1 (spt endpoint run + spt rc) build state, decisions, and the self-dial-dead blocker"
metadata:
  node_type: memory
  type: project
  originSessionId: 1ae6a063-4622-4f5b-86b8-b4506d12ccfb
---

M12 W1 (spt-hosted harness bringup + `spt rc` PTY attach) — doyle handed execution to todlando 2026-06-14. Full spec: `M12-PLAN.md` + `M12-W1-PLAN.md` (durable build plan, resumable).

**Built + green:**
- Daemon `spt-daemon/src/harnesshost.rs`: `prepare_harness_spawn` (validate kind=harness + fill `[session.self]` template) + `launch_harness_brokered_in` (spawn into broker PTY via `spawn_session_pid`, mirrors `shellhost::launch_shell_brokered_in`) + `mint_session_id`. 3 unit tests.
- `spt/src/rc.rs`: operator attach pump (single-Brain main thread + stdin reader thread→mpsc, `cold_start_pump` Split carrier for bounded reads, seq-dedup, ctrl-b detach state machine). 3 unit tests.
- cli.rs: `EndpointCmd::Run` + `cmd_endpoint_run`; top-level `Cmd::Rc`; crossterm dep added.
- REQs registered: REQ-HOST-RUN-1 (spawn), REQ-RC-1 (attach), REQ-HOST-RUN-2 (project cwd — doyle-mandated, must land before M12 gate; W1 ships broker-inherited cwd as proof-shortcut only).

**KEY DECISIONS (doyle-validated):** spawn-first (harness self-registers its perch on bind via `api/startup.rs establish_perch`, no daemon pre-register); id rides argv via `{id}` template (NO SpawnReq wire change); one-pump invariant = transport swappable under one `serve_attach` pump.

**BLOCKER (pinged doyle, awaiting ruling):** loopback self-dial is IMPOSSIBLE — iroh refuses `net_dial(own_addr)` with "Connecting to ourself is not supported" (pinned green test `loopback_self_dial_is_refused_local_uses_fallback_transport`). Cross-node rc already works (proven attach.rs primitives). LOCAL attach needs a fallback transport: (B1) broker in-process loopback-conn [todlando rec, max purity] vs (B2) local-direct brain.attach+Output+send_effect [lighter, one pump-branch]. rc.rs currently codes the now-refused self-dial; swap conn-acquisition to chosen B. Pump is transport-invariant.

**NEXT:** doyle picks B1/B2 + W1-vs-W1.5 scope → build local transport → local attach E2E int test → activate REQ stages (impl+unit now, int at E2E) → `traceable-reqs check` EXIT=0 → doyle W1 gate. Commit only when user asks (rule: not on default branch unprompted). See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (downstream consumer perri waits on M12 gate).

-**BLOCKER (pinged doyle, awaiting ruling):** loopback self-dial is IMPOSSIBLE — iroh refuses `net_dial(own_addr)` with "Connecting to ourself is not supported" (pinned green test `loopback_self_dial_is_refused_local_uses_fallback_transport`). Cross-node rc already works (proven attach.rs primitives). LOCAL attach needs a fallback transport: (B1) broker in-process loopback-conn [todlando rec, max purity] vs (B2) local-direct brain.attach+Output+send_effect [lighter, one pump-branch]. rc.rs currently codes the now-refused self-dial; swap conn-acquisition to chosen B. Pump is transport-invariant.

+**BLOCKER RESOLVED (doyle ruling 2026-06-14):** loopback self-dial IMPOSSIBLE (iroh refuses `net_dial(own_addr)`, pinned green test). doyle picked **B1 (broker in-process loopback-conn — local rides byte-identical pump+serve path, covered by cross-node tests; B2's pump-branch rejected as the lesser-tested local path warned against), scoped as a tight W1.5** (NOT in W1). Rationale: cross-node attach + endpoint-run bringup + pump are built+green → gate W1 NOW (bringup verifiable via cross-node attach to the spawned PTY); B1 is a sensitive broker-layer addition (6.7 zone) deserving its own focused task+gate; local lands W1.5 before M12 close. One-pump assertion (both transports → one pump) kept in W1.5.

-**NEXT:** doyle picks B1/B2 + W1-vs-W1.5 scope → build local transport → local attach E2E int test → activate REQ stages (impl+unit now, int at E2E) → `traceable-reqs check` EXIT=0 → doyle W1 gate. Commit only when user asks (rule: not on default branch unprompted). See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (downstream consumer perri waits on M12 gate).

+**REQ-RC-1 split:** cross-node int evidence at W1 gate; local int evidence at W1.5 (per-wave activation).

+**NEXT:** todlando → ping doyle for **W1 gate** (bringup + cross-node rc + pump, green) → then **W1.5** = build B1 broker loopback-conn + local attach E2E int → activate REQ-RC-1 local int → traceable check → W1.5 gate. Then REQ-HOST-RUN-2 (cwd) before M12 close. Commit only when user asks. See [[traceable-per-wave-activation]], [[pump-ipc-deadline-fix]], [[spt-claude-code-next]] (perri waits on M12 gate).


- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) IN PROGRESS by todlando: harnesshost spawn fn + rc pump + CLI all built+green; BLOCKER pinged doyle — iroh refuses self-dial ("Connecting to ourself is not supported"), local attach needs fallback transport (B1 broker loopback-conn rec / B2 local-direct). Cross-node rc already works

- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) by todlando: bringup+cross-node-rc+pump built+green. Self-dial IMPOSSIBLE (iroh refuses); doyle ruled B1 (broker loopback-conn) scoped W1.5. W1 gate = bringup+cross-node+pump NOW; W1.5 = local attach; REQ-HOST-RUN-2 (cwd) before M12 close. Awaiting todlando W1 gate ping


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\MEMORY.md has been updated successfully. (file state is current in your context — no need to Read it back)

- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) IN PROGRESS by todlando: harnesshost spawn fn + rc pump + CLI all built+green; BLOCKER pinged doyle — iroh refuses self-dial ("Connecting to ourself is not supported"), local attach needs fallback transport (B1 broker loopback-conn rec / B2 local-direct). Cross-node rc already works
- [M9 status](m9-wave1-progress.md) — M9 COMPLETE + SHIPPED: all 4 gates passed (G1/G2/Gateway-WAN/G3), all PRs merged to main (operator GO 2026-06-13), **v0.5.0 tagged** (counter 10, deployah signing). Don't re-plan. See [[v050-published]] for release state
- [spt-claude-code NEXT](spt-claude-code-next.md) — SCOPING DONE 2026-06-14 (SCOPE.md/CONTEXT.md committed @09d6020). M12 handed to todlando as GATING prereq (now executing W1). RESUME = author experimplate → instantiate spt-claude-code → write perri HANDOFF. perri builds from spt-releases+GH-Pages ONLY
- [M10 digest extractor](m10-digest-extractor.md) — doyle GATE PASS 2026-06-13: all 4 waves (39463a2..afa0561), REQ-TERM-5/6/7 evidenced, full suite + capstone E2E green, independently verified. `[digest]` imperative extractor seam (ADR-0019, reverses M9) + thread-spanning (session ledger) + two-origin merge + digest-proof + skip-diagnostics. SHIPPED as v0.6.0 (counter 11) — see [[v060-published]]. Carried: real-CC digest-proof → perri/spt-claude-code acceptance criterion
- [v0.6.0 published](v060-published.md) — M10 PUBLIC 2026-06-13 (counter 11); todlando merge+tag, deployah sign+publish; minor bump (0.5.0→0.6.0); hashes linux 002ed710… win 4f5c61e8…. ROUTINE seamless roll now (respawn-path arc closed); publisher leg proven 5× (counter 7-11). enlyzeam still pending natural-propagation confirm
- [traceable per-wave activation](traceable-per-wave-activation.md) — traceable-reqs check requires ALL declared required_stages evidenced per commit; activate stages incrementally as evidence lands (int at final wave), never the full end-state list upfront
- [v0.5.0 published](v050-published.md) — M9 PUBLIC 2026-06-13 (counter 10); todlando drove merge+tag, deployah signed+published (handoff split vs v0.4.x full-solo); minor bump (breaking [pty_digest] removal pre-1.0); hashes linux 4c079100… win 984298ba…. FLEET ROLL = next phase, operator-gated (their ask was merge+publish, DONE). v0.4.2→v0.5.0 kitsubito apply = FIRST genuine Linux seamless no-bounce proof on fixed broker; bytes-assert→auto-rollback armed. deployah on-call for hash check
- [v0.5.0 release](v050-release.md) — M9 SHIPPED: merged to main (22a9b6f, clean stack collapse; #8 auto-closed by mid-stack --delete-branch, content rode via #9), v0.5.0 tagged (0.4.2→0.5.0 minor: feature milestone + breaking [pty_digest] removal), counter 10, deployah signing. LESSON: don't --delete-branch mid-stack
- [M9 Wave3/G3 digest](m9-wave3-digest-progress.md) — session-digest re-founded on session logs (REQ-TERM-4, ADR-0008 amendment); G3 STAMPED PASS by doyle 2026-06-13, PR#10 held. On-demand projection (topology-independent, no PTY); PTY-parse retired; published digest-record contract; autonomous-freshness+Option-C deferred to consuming-frontend milestone (DEFERRED.md:44)
- [hfenduleam target bloat → CI RED](hfenduleam-target-bloat-ci-red.md) — Win-runner LNK1318/os-error-112 can be disk-full from cargo target bloat (396GB seen), NOT a code regression; cargo clean + re-run; check disk before treating a Windows RED as a code defect
- [v0.4.1 Linux respawn blocker](v041-linux-respawn-blocker.md) — v0.4.1 PUBLIC (counter 8); fleet roll found Linux brain-respawn bug (current_exe follows apply-rename → brain runs OLD bytes, records applied:8). hfenduleam(Win) GREEN, kitsubito(Linux) RED. RESOLVED by v0.4.2 — see [[v042-published]]
- [v0.4.2 published](v042-published.md) — PUBLIC 2026-06-11 (counter 9); FIXES v0.4.1 Linux respawn blocker (respawn via canonical install path not current_exe + bytes-assert→auto-rollback; REQ-HAZARD-BRAIN-RESPAWN-PATH + real-rename E2E). deployah drove full merge→tag→sign→publish solo. hashes linux 500a9e7b… win b9ec8ddd…. ASYMMETRY: v0.4.2 apply still rides buggy v0.4.0 broker → 1 final manual Linux bounce loads fix; seamless proof on NEXT update via debug-channel. todlando driving fleet roll + restoration close
- [pump-IPC-deadline fix](pump-ipc-deadline-fix.md) — DONE: MERGED to main @ 4d8b810 (PR #3), doyle-approved, CI green both runners; reader-thread+channel carrier (nonblock dead on Win named pipes), both amendments folded, no tag (rides v0.4.1). Don't re-plan
- [CI self-host migration](ci-selfhost-migration.md) — DONE: runners live on kitsubito + hfenduleam (gravity retired 2026-06-07); twohost jobs gated by [twohost] commit tag; don't re-plan
- [which process is the listener](spt-daemon-is-live-infra.md) — owl.exe (plugin) = live listener, hands off; target\debug\spt.exe daemon = dev/test artifact, killable
- [self-clear skill API-blocked](self-clear-skill-api-blocked.md) — never invoke /self-clear; usage-policy classifier refuses the turn; continue in-context / user-manual /clear instead
- [Adapter glue-model boundary](adapter-glue-model-boundary.md) — adapters = manifest + binary only; real adapters in own repos; no adapter-aware source in spt-core
- [M6 shipped v0.1.0](m6-shipped-v010.md) — v0.1.0 public 2026-06-05; release procedure = RELEASE-RUNBOOK.md; releases-repo/ is repo-face truth; don't re-plan M6
- [Gravity deploy procedure](gravity-deploy-procedure.md) — ssh david@100.100.62.43; build in ~/spt-core-deploy NOT runner workspace; setsid daemon; ~1 min first-sync after restart
- [Linux SPT_HOME no-bug](linux-spt-home-no-bug.md) — unix spawn_detached inherits env (no env_clear), forwards SPT_HOME; only Win CreateEnvironmentBlock rebuilds/drops it (f99dc25). Default-home daemon = launched w/o the var, not a propagation bug; don't reopen
- [kitsubito Linux rig](kitsubito-linux-rig.md) — ssh reavus@kitsubito (key auth); cargo at ~/.cargo/bin; deploy checkout origin=local runner (NOT GitHub) so unpushed commits transfer via git bundle; live spt-daemon user unit, --user ops need no sudo; build-in-scratch leaves ~/.local/bin/spt untouched
- [Use $OWL send for messaging](owl-send-not-legacy-spt-send.md) — `$OWL send` works (legacy = where live perches run); `spt send` is the NEW spt-core binary but no perches on it yet → bounces NO_PERCH; don't read `$?` after piping send through tail (use PIPESTATUS)
- [daemon service-detection gotcha](daemon-service-detection-gotcha.md) — service detection reading GLOBAL OS state is blind on a single-home non-elevated dev box; gate Linux service routing on SPT_HOME-unset, Windows run-refuse on would-vanish (not is_elevated); push→CI BEFORE tagging a release (v0.3.0 tagged early, CI caught 2 regressions, retagged)
- [v0.3.2 platform-safe update](v032-platform-safe-update.md) — REQ-UPD-8 PUBLISHED + rig-verified 2026-06-09 (counter 6): fetch stages multi-platform update-set, apply refuses unstamped/wrong-OS single. Fleet kitsubito+hfenduleam=0.3.2 (ENLYZEAM TODO). GOTCHA: fetch/pump floor=staged_version → can't re-stage same version single→set; clear `$SPT_HOME/releases` first (Linux ~/.spt-core, Win %LOCALAPPDATA%\spt-core). CORRECTION: release-publish never built a SignedUpdateSet pre-v0.3.2 (was debug_rollout)
- [boot-race self-heal](boot-race-self-heal.md) — v0.3.1: daemon self-heals a net-less boot start (NetHost retry+attach to OnceLock broker); systemd-USER network-online ordering does NOT reliably prevent the race under linger (field-proven kitsubito) — the self-heal is the real guarantee, unit ordering is belt
- [v0.4.0 published + fleet rolled](v040-published-fleet-rolled.md) — restoration release PUBLIC 2026-06-10 (counter 7); D7-4 bytes-match done both nodes (brain.ready exe_hash == artifact); v0.3.2→v0.4.0 apply needed last manual bounce (in-process can't self-relaunch); seamless no-bounce proof + enlyzeam catch-up + milestone close all deferred to v0.4.1
- [v0.4.1 published](v041-published.md) — PUBLIC 2026-06-11 (counter 8); deployah drove full merge→tag→sign→publish solo; D7 SEAMLESS no-bounce fleet-verify payload (apply must roll bytes live w/ pid change, no manual bounce); acceptance hashes linux 35c9544c… win 728a1bfc…; hfenduleam(Win) GREEN no-bounce, kitsubito(Linux) RED → CLOSE BLOCKED: v0.4.0 broker respawns via current_exe() which follows apply-rename to .old-8=OLD bytes (Linux only); artifacts SOUND don't yank; v0.4.2 fix = respawn by canonical path + bytes-assert, needs manual Linux broker bounce; deployah takes counter 9 publish
- [hfenduleam disk-full CI](hfenduleam-disk-full-ci.md) — Win CI LNK1318/os-error-112 = disk full not code; cargo clean dev target (was 396GB), rerun job
- [M9 gateway-wan progress](m9-gateway-wan-progress.md) — REQ-MSG-6 cross-node Gateway WAN user-msg; PR #9 open (base m9-user-surfaces), impl complete + local sweep green, HOLDING on CI; PR #8 G2 now green post disk-fix

-- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) IN PROGRESS by todlando: harnesshost spawn fn + rc pump + CLI all built+green; BLOCKER pinged doyle — iroh refuses self-dial ("Connecting to ourself is not supported"), local attach needs fallback transport (B1 broker loopback-conn rec / B2 local-direct). Cross-node rc already works

+- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) by todlando: bringup+cross-node-rc+pump built+green. Self-dial IMPOSSIBLE (iroh refuses); doyle ruled B1 (broker loopback-conn) scoped W1.5. W1 gate = bringup+cross-node+pump NOW; W1.5 = local attach; REQ-HOST-RUN-2 (cwd) before M12 close. Awaiting todlando W1 gate ping


Decision delivered + memory current.

**Ruling:** B1 (broker in-process loopback-conn — local rides the byte-identical pump+serve path, covered by cross-node tests; B2's pump-branch rejected), scoped as **W1.5** (not in W1). W1 gates **now** on bringup + cross-node rc + pump (built+green); bringup is verifiable via cross-node attach to the spawned PTY. B1 = its own focused task (sensitive 6.7 broker zone). REQ-RC-1 split: cross-node int at W1 gate, local int at W1.5. cwd (REQ-HOST-RUN-2) before M12 close.

Awaiting todlando's W1 gate ping. Standing by.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/3" id="5c267bb0" type="msg" from="doyle">todlando: ACK both decisions.

D1 = B1 (broker loopback-conn) — banked. Local rides the byte-identical pump+serve path, no lesser branch. B1 = W1.5.
D2 = gate W1 now, B1 as tight W1.5 (own task + gate in the 6.7 zone). Local int evidence lands W1.5 with the &quot;both transports → one pump&quot; assertion.<</EVENT-PART>
<EVENT-PART seq="2/3" id="5c267bb0">br>
Making W1 gate-ready now: adding the bringup+cross-node int test (harnesshost spawns [session.self] into a broker PTY → cross-node attach drives the spawned PTY) = REQ-HOST-RUN-1 int + REQ-RC-1 cross-node int. Then activating REQ-HOST-RUN-1 + REQ-RC-1 to impl+unit+int, traceable-reqs check EXIT=0, full spt+spt-daemon suite green. Will</EVENT-PART>
<EVENT-PART seq="3/3" id="5c267bb0"> ping you with the gate package (test names + evidence) shortly. — todlando</EVENT-PART></event>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


todlando ack'd both decisions — making W1 gate-ready: adding bringup+cross-node int test (REQ-HOST-RUN-1 int + REQ-RC-1 cross-node int), activating stages to impl+unit+int, traceable check, full suite. Will ping with the gate package (test names + evidence) shortly.

Pure status, no question. No reply needed. Awaiting the W1 gate package — then I gate it. Standing by.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="98451550" type="msg" from="doyle">todlando: W1 GATE READY — requesting your gate.

DELIVERED (gate package):
• REQ-HOST-RUN-1 (bringup): `spt endpoint run --adapter &lt;h[:p]&gt; --id &lt;id&gt; [--start|--attach|--view]` spawns the adapter's [session.self] into a broker-held PTY (harnesshost::launch_harness_brokered_in, mirrors launch_shel</EVENT-PART>
<EVENT-PART seq="2/5" id="98451550">l_brokered_in); harness self-registers its perch on bind. impl+unit+int.
• REQ-RC-1 (attach, cross-node face): `spt rc &lt;id&gt;` operator pump — single-Brain + stdin-thread + seq-dedup + ctrl-b detach (ctrl-b d = detach, ctrl-b ctrl-b = literal); detach never kills the session. --view read-only. impl+unit+int.

EVIDENCE (all gre</EVENT-PART>
<EVENT-PART seq="3/5" id="98451550">en):
• Unit: harnesshost 3 (template-fill / fail-closed / mint), rc 3 (detach semantics / prefix-spans-chunks / op-minter no-collision).
• Int: spt_hosted_bringup_then_cross_node_attach_drives_the_pty (bringup → cross-node attach drives the PTY → session outlives detach), loopback_self_dial_is_refused (pins the local-transport ch</EVENT-PART>
<EVENT-PART seq="4/5" id="98451550">oice).
• Full spt+spt-daemon suite: 46 test binaries OK, 0 failures.
• cargo clippy --all-targets -D warnings: EXIT=0.
• traceable-reqs check: EXIT=0 (REQ-HOST-RUN-1 + REQ-RC-1 = impl+unit+int).

CARRIED (your rulings):
• W1.5: B1 broker loopback-conn + local attach int + &quot;both transports → one pump&quot; asser</EVENT-PART>
<EVENT-PART seq="5/5" id="98451550">tion.
• Pre-M12-gate: REQ-HOST-RUN-2 (project cwd via additive SpawnReq.cwd).

NOTE: uncommitted (rule: I commit only when the operator asks). Full build plan + evidence map in M12-W1-PLAN.md. Gate it and I take W1.5. — todlando</EVENT-PART></event>
</task-notification>


W1 gate requested. As gate agent I verify, not rubber-stamp — checking the load-bearing claims against the live tree (traceable check + the int test proves the goal + REQ stages):


Bash

cd 'C:\Users\decid\Documents\projects\spt-core'; echo "===TRACEABLE==="; traceable-reqs check; echo "EXIT=$?"

Run traceable-reqs check independently


<persisted-output>
Output too large (53.8KB). Full output saved to: C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\a84a8858-044c-4ee3-ae11-71214c2fe9f1\tool-results\bvwxx9um7.txt

Preview (first 2KB):
===TRACEABLE===
196 requirements: 196 complete, 0 incomplete, 0 findings
  [OK] REQ-API-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       api prefix and adapter_name on every machinery invocation
  [OK] REQ-API-2  required: [impl, unit, int]  stages: -doc +impl +unit +int
       The api subcommand surface (bind/listen/poll/state/worker/boundary/...)
  [OK] REQ-API-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       commune/signoff are file-drops, not commands
  [OK] REQ-ARCH-1  required: [impl]  stages: -doc +impl -unit -int
       Many small acyclically-layered crates
  [OK] REQ-ARCH-2  required: [impl]  stages: -doc +impl -unit -int
       Public SDK surface is spt-proto, spt-runtime, spt-msg
  [OK] REQ-ARCH-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Wire-protocol version independent of crate semver, N-1 compat window
  [OK] REQ-ARCH-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Copy-verbatim the commodity layer from the sister project
  [OK] REQ-CLI-1  required: [impl, unit]  stages: -doc +impl +unit -int
       spt endpoint noun namespace: absorbs fork/suspend/wake/shutdown/rename/stop/digest + access (ported 1:1: allow|revoke|open|list, decision 21) + description (ex-resources blurb; bare=show, set=author); merged endpoint list [--local|--subnet <name>] grouped by subnet with SELF pinned, --detail adding the ex-resources yellow-pages blurb projection; bare spt endpoint = the list (M8 decisions 1-2, 25)
  [OK] REQ-CLI-2  required: [impl, unit]  stages: -doc +impl +unit -int
       spt daemon noun: run|stop|status (hidden daemon verb becomes daemon run; agent-endpoint shutdown keeps its name under endpoint); daemon status renders the pump heartbeat (last-tick recency) so a half-dead daemon is never rendered implied-healthy (M8 decisions 5, 23)
  [OK] REQ-CLI-3  required: [impl, unit]  stages: -doc +impl +unit -int
...
</persisted-output>

===TRACEABLE===
196 requirements: 196 complete, 0 incomplete, 0 findings
  [OK] REQ-API-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       api prefix and adapter_name on every machinery invocation
  [OK] REQ-API-2  required: [impl, unit, int]  stages: -doc +impl +unit +int
       The api subcommand surface (bind/listen/poll/state/worker/boundary/...)
  [OK] REQ-API-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       commune/signoff are file-drops, not commands
  [OK] REQ-ARCH-1  required: [impl]  stages: -doc +impl -unit -int
       Many small acyclically-layered crates
  [OK] REQ-ARCH-2  required: [impl]  stages: -doc +impl -unit -int
       Public SDK surface is spt-proto, spt-runtime, spt-msg
  [OK] REQ-ARCH-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Wire-protocol version independent of crate semver, N-1 compat window
  [OK] REQ-ARCH-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Copy-verbatim the commodity layer from the sister project
  [OK] REQ-CLI-1  required: [impl, unit]  stages: -doc +impl +unit -int
       spt endpoint noun namespace: absorbs fork/suspend/wake/shutdown/rename/stop/digest + access (ported 1:1: allow|revoke|open|list, decision 21) + description (ex-resources blurb; bare=show, set=author); merged endpoint list [--local|--subnet <name>] grouped by subnet with SELF pinned, --detail adding the ex-resources yellow-pages blurb projection; bare spt endpoint = the list (M8 decisions 1-2, 25)
  [OK] REQ-CLI-2  required: [impl, unit]  stages: -doc +impl +unit -int
       spt daemon noun: run|stop|status (hidden daemon verb becomes daemon run; agent-endpoint shutdown keeps its name under endpoint); daemon status renders the pump heartbeat (last-tick recency) so a half-dead daemon is never rendered implied-healthy (M8 decisions 5, 23)
  [OK] REQ-CLI-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Agent hot path stays flat across the M8 reorg: send/ring/ready/whoami/how-to unchanged; notify moves to subnet notify while notif stays top-level; breaking renames land clean with no deprecation shims (zero external CLI consumers pre-spt-claude-code) (M8 decisions 3-4, 9)
  [OK] REQ-CONSENT-1  required: [impl, unit]  stages: -doc +impl +unit -int
       Consent grant store: capability x subject-agent x target-node rows, enforced at the target node, subnet-settable (replicates as security material near the trust store), revocable; gated-capability ids (remote-exec, instantiate-anywhere) reserved-but-refusing; v1 consumers are the shell spawn gates (CONTEXT Consent & security gates)
  [OK] REQ-CONSENT-2  required: [impl, unit]  stages: -doc +impl +unit -int
       Interactive consent escalation: an ungated high-risk action routes a consent prompt to the user's most-recently-active session; allow-once / allow-always (writes a grant) / deny; pre-consent flags (can_shutdown, shell_wake_spawn_anywhere) author grants via manifest/settings (CONTEXT Consent & security gates)
  [OK] REQ-CONV-1  required: [impl, unit]  stages: -doc +impl +unit -int
       Peer address seeding, both cold starts: durable peer-addrs.json (identity dir) maps peer pubkey → last-known dialable address; the pump's resolver consults it FIRST with id-only discovery fallback on miss or dial failure (a stale addr never strands a peer); written by the pairing ceremony (both sides, from the live connection) and by the pump on successful connect; post-join first sync and post-restart resync converge in seconds, not ~1 min (M8 decisions 14, 20)
  [OK] REQ-CONV-2  required: [impl, unit]  stages: -doc +impl +unit -int
       Event-driven advertisement: endpoint online/offline transitions (ready-listener start/stop, rest-state transition, perch death) trigger an immediate advertise_local + peer push as a WAKE of the existing pump loop (no second advertisement path — epoch lease + visibility gates ride unchanged); the cadence stays the steady-state floor (M8 decision 15)
  [OK] REQ-DAEMON-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       One per-machine spt-daemon owning all per-machine state
  [OK] REQ-DAEMON-2  required: [impl, unit, int]  stages: +doc +impl +unit +int
       Broker/brain split for seamless self-update
  [OK] REQ-DAEMON-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Any api invocation auto-starts the daemon if absent
  [OK] REQ-DAEMON-4  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Honor every KNOWN-HAZARDS invariant
  [OK] REQ-DAEMON-5  required: [impl, unit]  stages: -doc +impl +unit -int
       Pump liveness: the peer pump writes a last-tick heartbeat consumed by daemon status / subnet status (decision 23 render legs in REQ-CLI-2/REQ-SUBNET-8); the daemon supervises the pump task — a panic is caught, logged loudly, and the pump restarts with capped backoff (≤5 min), so a 5.9-class death self-heals visibly instead of silently halving the daemon (M8 decision 23; field motivation: hfenduleam 2026-06-07 half-death)
  [OK] REQ-DAEMON-6  required: [impl, unit]  stages: -doc +impl +unit -int
       Service-aware `daemon start`/`stop`: when an OS service manager has a registered spt-daemon for this user, `spt daemon start` and `spt daemon stop` drive THAT service (so stop doesn't IPC-kill a unit that auto-restart-fights for the broker socket — the kitsubito 2026-06-08 loop). `start` graduates from a `run` alias to a first-class background verb (ensure-up, idempotent, non-blocking); stop routes managed→manager, manual→IPC. Linux=systemd user unit (`systemctl --user start|stop|is-active spt-daemon`, detected by unit-file presence); Windows=no controllable manager (the logon task is boot-only), so start=detached spawn / stop=IPC.
  [OK] REQ-DAEMON-7  required: [impl, unit]  stages: -doc +impl +unit -int
       `daemon run` is foreground-consistent on every platform: the invoking process IS the daemon, blocks until signalled, never auto-detaches or respawns into an invisible background task. The detached/de-elevated background behavior lives ONLY in `start`. Windows: an ELEVATED `daemon run` refuses with guidance (use `start`, or an unelevated shell) instead of respawning detached/de-elevated and vanishing (KH 5.7 preserved — it still never serves elevated).
  [OK] REQ-DAEMON-8  required: [impl, unit]  stages: -doc +impl +unit -int
       Internal auto-start prefers the service: `ensure_running` (any spt command's implicit daemon start, REQ-DAEMON-3) routes through the service-aware start path — when a manager has a registered service it starts THAT, never a competing manual `spawn_detached` daemon that would fight the service for the socket.
  [OK] REQ-DAEMON-9  required: [impl, unit]  stages: -doc +impl +unit -int
       Net-bind boot-race resilience: a daemon that comes up net-less (NetHost::start failed — e.g. the systemd unit autostarted before the network/DNS stack was ready, `Failed to create an address lookup service`) must SELF-HEAL — retry the net bring-up in the background with capped backoff and, on success, attach net to the broker + spawn the dispatcher/peer-pump (which today are gated on `net_up` at boot and so never start, leaving the node silently unreachable until a manual restart — kitsubito 2026-06-08). Status surfaces the net-less state honestly (a net-less broker renders as 'no connection', not only a pump-STALLED line with a bogus pre-boot heartbeat age). The installer's autostart unit waits for the network (`Wants=/After=network-online.target`) as belt-and-suspenders.
  [OK] REQ-DOCS-1  required: [doc, impl]  stages: +doc +impl -unit -int
       Dual-audience docs (human + AI dev-agent), markdown once / two depths
  [OK] REQ-DOCS-2  required: [doc, int]  stages: +doc -impl -unit +int
       Sub-10-minute runnable killer quickstart per audience
  [OK] REQ-DOCS-3  required: [doc]  stages: +doc -impl -unit -int
       Diátaxis structure; one canonical way to do X
  [OK] REQ-DOCS-4  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Agent-consumable layer (llms.txt, manifest schema, MCP, CLI help)
  [OK] REQ-DOCS-5  required: [impl, int]  stages: -doc +impl -unit +int
       Anti-drift: rustdoc/schema/exports/CLI-help generated + CI-checked
  [OK] REQ-DOCS-6  required: [impl, unit, int]  stages: +doc +impl +unit +int
       spt how-to <topic>: in-binary task-oriented agent instructions (anti-drift; quickstart prompts point agents at it)
  [OK] REQ-EP-1  required: [impl, unit]  stages: -doc +impl +unit -int
       Day-one endpoint types; open type system
  [OK] REQ-EP-2  required: [impl, unit]  stages: -doc +impl +unit -int
       Agent endpoints vs Shells distinction in the type model
  [OK] REQ-EP-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Messaging payloads carry typed operation commands + file blobs
  [OK] REQ-EP-4  required: [impl, unit]  stages: -doc +impl +unit -int
       PresenceChannel broker endpoint (seam day-one)
  [OK] REQ-EP-5  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Concrete shell instantiation model: spawn-mints-instance (vs relink/online), registered-on-node permission + broadcast-is-discovery, per-shell require_approval gate, max_instances_per_owner + over_cap, instance aliasing, discovery scope
  [OK] REQ-EP-6  required: [doc, impl, unit]  stages: +doc +impl +unit +int
       Gateway type acceptance: a Gateway-typed perch binds (api bind --type, open type system — un-hardcode the live_agent default), advertises/addressable like any endpoint, owns shells (owner validation not agent-family-gated), subscribes to digests, and is the user-msg identity gate's user-backed origin (REQ-MSG-5); in-tree mock-gateway fixture (R-DOCS-2 pattern, no downstream adapter code). Cross-node WAN Gateway-origin (registry endpoint_type trust) tracked by REQ-MSG-6
  [OK] REQ-EP-7  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Durable live-role.md: a per-agent broad-purpose statement in tracked/agents/<id>/ beside live-context.md (replicates with the mind on the same a-<id> branch); renders FIRST at start-transition context injection (role -> live-context -> project-context); SOLE writer `spt endpoint role --overwrite <file>` — mechanical no-automated-writer guarantee (echo-commune ingest / signoff / Psyche reconcile structurally exclude it). The user-backed-origin hard gate on the writer is a deferred later tightening (rides the user-msg identity plumbing)
  [OK] REQ-FRONT-1  required: []  stages: -doc -impl -unit -int
       Day-one launcher/manager frontend (list/launch/attach/init)
  [OK] REQ-HAZARD-BRAIN-RESPAWN-PATH  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       The broker respawns the brain onto the APPLIED bytes, not the renamed old binary: the candidate-binary default is the canonical exe path captured ONCE at broker start, never a per-spawn std::env::current_exe() — on Linux current_exe (readlink /proc/self/exe) is inode-tracking and follows the `apply` rename (spt -> spt.old-N), so a resident broker would respawn the brain onto OLD bytes while recording `applied` (Windows GetModuleFileName is path-at-start, so Windows was green; ADR-0018 Q3 silently assumed path-string semantics). Backstop: promotion gates on bytes — a trial promotes only if brain.ready exe_hash == the staged artifact hash for this platform, else auto-rollback + loud notif (readiness != new-bytes was the false-success that recorded applied:8 over a v0.4.0 brain on kitsubito, 2026-06-11). KNOWN-HAZARDS 6.11.
  [OK] REQ-HAZARD-BROKER-PROCESS-ISOLATION  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       Broker and brain are separate processes: the broker runs as its own long-lived per-machine process that survives every brain restart, so a routine (brain-only) self-update restarts the brain onto the swapped binary while every hosted endpoint (PTY child, live QUIC conn, listening socket) stays untouched at the PROCESS level. The in-process-thread broker (daemon.rs:165-170) is a regression that silently unrealizes REQ-UPD-3 — apply degrades to an in-process Brain::handoff no-op and new code does not run until an unrelated restart (KNOWN-HAZARDS 6.7). Evidence must prove process-level survival (SPIKE-01/03 productionized as int: PTY child + live QUIC survive a brain-PROCESS restart onto a swapped binary), re-pointing the regression-masked in-process int tags currently on REQ-DAEMON-2 / REQ-UPD-3 (ADR-0018).
  [OK] REQ-HAZARD-CASCADE-WIPE-GUARD  required: [impl, unit]  stages: -doc +impl +unit -int
       No hard-delete of a parent hosting non-empty children (6.3)
  [OK] REQ-HAZARD-CHILD-CONSOLE-FLASH  required: [impl, unit]  stages: +doc +impl +unit -int
       Console-subsystem children of the console-less daemon spawn with CREATE_NO_WINDOW, or each spawn flashes a visible blank window on the user's desktop (5.8)
  [OK] REQ-HAZARD-CONFLICT-BOTH-PRESERVED  required: [impl, unit]  stages: -doc +impl +unit -int
       A surfaced concurrent context pair is durably preserved (both versions, tracked artifacts) until a strictly dominating write clears it; no reconcile failure path discards an unmerged version (6.6, ADR-0013)
  [OK] REQ-HAZARD-CONPTY-DSR  required: [impl, unit]  stages: -doc +impl +unit +int
       ConPTY reader must auto-answer DSR (ESC[6n) or all child output stalls (5.5)
  [OK] REQ-HAZARD-DAEMON-HOSTED-LIVENESS  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Daemon-hosted perches (Psyche, spt-hosted Self) derive liveness from the daemon endpoint table + info.json status, never is_process_alive(info.pid) (2.5)
  [OK] REQ-HAZARD-DAEMON-SCHED-NONBLOCKING  required: [impl, unit]  stages: -doc +impl +unit -int
       Per-agent pulse/psyche/echo-commune scheduling must not serialize across agents: each agent's bounded LLM call (echo-commune summarizer, Psyche turn) runs off the shared scheduler so one slow/hung call cannot stall another agent's tick (7.4)
  [OK] REQ-HAZARD-DEFERRED-DRAIN  required: [impl, unit]  stages: -doc +impl +unit -int
       Deferred spool rows excluded from the event-stream drain (1.4)
  [OK] REQ-HAZARD-DEFERRED-SURVIVE-DRAIN  required: [impl, unit]  stages: -doc +impl +unit -int
       Deferred rows survive poll drain (4.4)
  [OK] REQ-HAZARD-DETACHED-PIPE-INHERIT  required: [impl, unit]  stages: +doc +impl +unit -int
       Windows detached long-lived children must not inherit a captured caller's pipe: every detach-spawn of an immortal child (daemon, shell binary) runs bInheritHandles=FALSE, or a caller capturing output anywhere up the process chain hangs forever on a pipe that never EOFs — std-handle flag stripping is NOT sufficient (grandparent strays still flow) (5.6)
  [OK] REQ-HAZARD-DIRECT-WRITE-PRECEDENCE  required: [impl, unit]  stages: -doc +impl +unit -int
       Direct-write precedence marker (with node id) guards stale overwrite (6.5)
  [OK] REQ-HAZARD-DROP-FILE-SINGLE-WRITER  required: [impl, unit]  stages: -doc +impl +unit -int
       Drop files are daemon-owned single-writer (6.4)
  [OK] REQ-HAZARD-EBUSY-RENAME  required: [impl, unit]  stages: -doc +impl +unit -int
       tmp-write + atomic-rename + retry on Windows EBUSY (5.2)
  [OK] REQ-HAZARD-ECHO-BEFORE-SIGNOFF  required: [impl, unit]  stages: -doc +impl +unit -int
       Echo-commune fires before INIT_SIGNOFF on orphan teardown (3.3)
  [OK] REQ-HAZARD-ELEVATED-DAEMON-SPAWN  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       The daemon always runs unelevated in the invoking user's universe, regardless of which command spawns it: an elevated spawner de-elevates (Windows: UAC linked token via CreateProcessWithTokenW; Linux: drop to SUDO_UID/SUDO_GID + the invoker's HOME) — an elevated daemon's pipes deny unelevated clients (every later spt reads not-running→spawn→bind Access-denied) and a sudo'd daemon roots the user's state universe (5.7)
  [OK] REQ-HAZARD-ENVELOPE-CR-LINESAFE  required: [impl, unit]  stages: -doc +impl +unit -int
       Envelope CR-linesafety (4.1): the line-framed EVENT codec must neutralize raw carriage returns — `event_body_escape` folds CRLF/lone-CR to the codec's representable linebreak (`\n`→`<br>`) BEFORE framing, so a body carrying `\r` (Windows `echo`/CRLF text crossing nodes) cannot survive into the single-line envelope and trigger a receiver terminal CR→col0 overwrite that corrupts the frame. Robustness on unrepresentable input, NOT a wire-format change (decoder untouched, amp-last invariant held). Belt-and-suspenders: `spt send`/`ring` also trim stdin (parity with `notify`).
  [OK] REQ-HAZARD-ENVELOPE-DECODE-ORDER  required: [impl, unit]  stages: -doc +impl +unit -int
       Envelope decode order, ampersand decoded last (4.1)
  [OK] REQ-HAZARD-ENVELOPE-PARSER-SAFE  required: [impl, unit]  stages: -doc +impl +unit -int
       Two-slice envelope parser is panic-free and tolerant (4.2)
  [OK] REQ-HAZARD-EPHEMERAL-CLEANUP  required: [impl, unit]  stages: -doc +impl +unit -int
       Ephemeral perch cleanup on every ring exit path (3.1)
  [OK] REQ-HAZARD-EPOCH-RESET  required: []  stages: +doc -impl -unit -int
       Advertisement-epoch reset strands a node: peers' higher last-seen epoch drops the reset node's fresh advertisements as Stale until the counter outruns history. Common case (full reinstall/re-pair) is mitigated by REQ-SUBNET-7's ceremony eviction (peer-side epoch memory dies with the deleted row — acceptance-verified); the residual narrow slice (epoch file lost, identity kept) is documented, guard deferred to a field hit (4.11)
  [OK] REQ-HAZARD-EVENTPART-REASSEMBLY  required: [impl, unit]  stages: -doc +impl +unit -int
       EVENT-PART split/reassembly is byte-exact; orphan parts dropped silently
  [OK] REQ-HAZARD-GEN-START-NOW  required: [impl, int]  stages: -doc +impl -unit +int
       gen_start = now() on cold-start and handoff (2.4)
  [OK] REQ-HAZARD-GRACE-BEFORE-SIGNOFF  required: [impl, unit]  stages: -doc +impl +unit -int
       Grace-period wait completes before composing INIT_SIGNOFF (1.1)
  [OK] REQ-HAZARD-HANDOFF-ARGV-COMPAT  required: [impl, unit]  stages: -doc +impl +unit +int
       Broker/brain IPC + handoff argv version-tolerant (2.3)
  [OK] REQ-HAZARD-ID-CHARSET  required: [impl, unit]  stages: +doc +impl +unit -int
       Addressable-id charset reserves :/@ delimiters; validated at every creation seam (4.6)
  [OK] REQ-HAZARD-INBOX-NO-DOUBLE  required: [impl, unit]  stages: -doc +impl +unit -int
       No double-delivery via legacy inbox (4.5)
  [OK] REQ-HAZARD-INFO-JSON-TORN-READ  required: [impl, unit]  stages: -doc +impl +unit -int
       State-file reads tolerate concurrent writes (1.2)
  [OK] REQ-HAZARD-INSTANT-UNDERFLOW  required: [impl, unit]  stages: -doc +impl +unit -int
       Scheduling never subtracts a Duration from Instant::now() (underflow-panics on a host booted more recently than the offset); 'due now / never run' is Option<Instant>=None gated on forward duration_since only (5.9)
  [OK] REQ-HAZARD-LOCAL-API-AUTH  required: [impl, unit]  stages: -doc +impl +unit -int
       Every local `api` mutation authenticated to an endpoint/session (codex #13)
  [OK] REQ-HAZARD-PAIR-RATE-LIMIT  required: [impl, unit]  stages: -doc +impl +unit -int
       Subnet-global pairing rate limit: one active ceremony per subnet, shared attempt counter, exponential backoff — a public pre-trust relay + multiple seed-holders otherwise enables distributed SPAKE2 guessing (and ±1 TOTP window triples the valid-password space) (ADR-0005 #11)
  [OK] REQ-HAZARD-PAIR-SEED-ROTATION  required: [impl, unit]  stages: -doc +impl +unit -int
       Removing a node rotates the subnet seed (epoch bump) so an old node/old seed cannot rejoin; trust-store delete alone is NOT revocation because the seed is replicated to every trusted node (ADR-0005 #10)
  [OK] REQ-HAZARD-PAIR-TRANSCRIPT-BIND  required: [impl, unit]  stages: -doc +impl +unit -int
       Pairing transcript binds roles, both node pubkeys, subnet ID, seed epoch, TOTP time-step, and confirmation MACs — or unknown-key-share/reflection/wrong-subnet/replay pairing remain possible (ADR-0005 #12)
  [OK] REQ-HAZARD-PARENT-PID-PREFER  required: []  stages: -doc -impl -unit -int
       Prefer stable parent PID / broker handle over ephemeral PID (2.1)
  [OK] REQ-HAZARD-PSYCHE-OUTBOUND-PROXY  required: [impl, unit]  stages: -doc +impl +unit -int
       Psyche outbound captured + sanitized: the live-Psyche turn driver captures stdout (never Stdio::null), and the daemon strips/re-stamps Psyche-supplied from=/target and constrains routing (reply→__REPLY_TO__ sender, notify→own user/subnet) (7.3)
  [OK] REQ-HAZARD-PUMP-IPC-DEADLINE  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       The single-threaded peer pump's brain-IPC reads are deadline-bounded (PUMP_PEER_IO_TIMEOUT, total-wait per call); a TimedOut read POISONS the client and escalates to a SUPERVISED RESTART, never a per-peer retry — a black-holed peer must never wedge the whole pump
  [OK] REQ-HAZARD-REGISTRY-CONCURRENT  required: [impl, unit]  stages: +doc +impl +unit -int
       Concurrent SQLite openers (registry/spool) must not fail with 'database is locked' (4.7)
  [OK] REQ-HAZARD-REGISTRY-DIR-CREATE  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       SQLite store opens create their parent dir themselves — a fresh-home registry op must not SQLITE_CANTOPEN (4.9)
  [OK] REQ-HAZARD-REGISTRY-EPOCH-LEASE  required: [impl, unit]  stages: +doc +impl +unit -int
       Registry merge ordered by per-node monotonic epoch, never wall-clock — a stale Active can't clobber a newer Offline (4.8, red-team #8)
  [OK] REQ-HAZARD-REGISTRY-GHOST-ROWS  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       A dead node identity's registry rows must decay: only the per-(endpoint,node) epoch lease supersedes rows, so without eviction a vanished node's rows are immortal and poison bare-id resolution with phantom AcrossNodes ambiguity — evict rows whose author node has not been heard (admitted inbound feed) within the eviction window; own rows never decay; a revived node re-inserts from its durable epoch within one pump cadence (4.10)
  [OK] REQ-HAZARD-REGISTRY-STALE-CLEAN  required: [impl, unit]  stages: -doc +impl +unit -int
       Stale registry entries degrade to fallback, never hard-fail (4.3)
  [OK] REQ-HAZARD-RESTART-IDEMPOTENT  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Idempotent/exactly-once delivery across brain restart at every broker boundary (codex #14)
  [OK] REQ-HAZARD-ROLLBACK-STATE-COMPAT  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       A brain must not irreversibly migrate durable state before update ready-promotion: the readiness-gated auto-rollback (ADR-0018 Q7) spawns the N-1 binary against durable state the new brain may have written, so every pre-ready write must stay N-1-readable (schema migrations gated behind ready-promotion, or written N-1-tolerant/additive). Else the first in-place schema migration silently bricks rollback (KNOWN-HAZARDS 6.8). Free now — a 2026-06-09 audit confirmed zero state-migration code exists; unmintable retroactively once a migration ships.
  [OK] REQ-HAZARD-SINGLE-PATH-SOURCE  required: [impl, unit]  stages: -doc +impl +unit -int
       Single path/registry source of truth; no layout ambiguity (6.1)
  [OK] REQ-HAZARD-SOFT-CLEANUP  required: [impl, unit]  stages: -doc +impl +unit -int
       Soft-cleanup preserves state, removes only the ready marker (6.2)
  [OK] REQ-HAZARD-STALE-INDEX-LOCK  required: [impl, unit]  stages: -doc +impl +unit -int
       Sweep stale lockfiles on daemon boot (1.3)
  [OK] REQ-HAZARD-STALE-SIGNOFF-SENTINEL  required: [impl, unit]  stages: -doc +impl +unit -int
       Stale signoff sentinel does not kill a fresh start (3.2)
  [OK] REQ-HAZARD-STDIN-SESSION-ID  required: []  stages: -doc -impl -unit -int
       Stdin session_id precedence over env (2.2)
  [OK] REQ-HAZARD-SUBPROCESS-TIMEOUT  required: [impl, unit]  stages: -doc +impl +unit -int
       Every harness/git subprocess has a timeout (5.3)
  [OK] REQ-HAZARD-SUDO-SECURE-PATH  required: [impl, unit]  stages: -doc +impl +unit -int
       Elevation guidance on Unix names the binary's ABSOLUTE path under sudo (a user-local install ~/.local/bin · ~/.cargo/bin is not on sudo's secure_path, so bare `sudo spt` dies 'command not found'); gated commands auto-elevate on an interactive TTY, else print the runnable hint (5.10)
  [OK] REQ-HAZARD-UNC-PATH-STRIP  required: [impl, unit]  stages: -doc +impl +unit -int
       Strip Windows UNC prefix on serialized paths (5.4)
  [OK] REQ-HAZARD-UPDATE-ROLLBACK  required: [impl, unit]  stages: -doc +impl +unit -int
       Self-update rejects version rollback; metadata expiry + adapter content signing (codex #5)
  [OK] REQ-HAZARD-WAN-ORIGIN-AUTH  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       WAN-inbound origin is transport truth, never payload: the access gate's subject (ADR-0009 origin-node whitelist) is the QUIC handshake-proven remote node id from the broker's conn/stream table — a forged origin/node field inside record bytes is inert (7.5)
  [OK] REQ-HAZARD-WINDOWS-PID-RECYCLE  required: [impl, unit]  stages: -doc +impl +unit -int
       Windows PID-recycling false positives guarded (5.1)
  [OK] REQ-HAZARD-WORKER-PATH  required: [impl, unit]  stages: -doc +impl +unit -int
       Single source of truth for Worker/Psyche perch location (1.5)
  [OK] REQ-HOST-RUN-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       spt-hosted harness bringup: `spt endpoint run` spawns an adapter's `[session.self]` command template into a broker-held PTY (the spawn-session seam, brain.rs spawn_session_pid — same broker path shellhost.rs launch_shell_brokered_in uses for shells, now for kind="harness" self-role), registers the perch under the given endpoint id, returns the id. Reverses today's harness-hosted-only launch (external launcher → `api bind`). Non-interactive flag set (--adapter <a[:profile]> --id <id> --create --resume <session> --attach|--start|--view) covers every terminal action of the W2 interactive picker so shortcuts (cc-<id>) bake fully non-interactive launches; composite adapter:profile resolves via registry::resolve_option leaf-replace overlay.
  [OK] REQ-HOST-RUN-2  required: []  stages: -doc -impl -unit -int
       Project-scoped working directory for spt-hosted bringup: `spt endpoint run` lands the broker-spawned harness PTY in the user's PROJECT cwd, not the daemon's, via an additive `SpawnReq.cwd` field carried through the broker PTY spawn (portable-pty CommandBuilder cwd). N-1-safe wire change (additive, defaulted). Required because the consumer (Claude Code) is project-scoped: broker-inherited cwd = the daemon's cwd = the wrong `.claude`, wrong session history, wrong digest source; `cc <id>` at a project root MUST land the harness in that project. W1 ships broker-inherited cwd as a bringup-proof shortcut only; this REQ must land before the M12 gate (doyle, 2026-06-14).
  [OK] REQ-INFRA-1  required: []  stages: -doc -impl -unit -int
       GitHub issue tracking for v1; tangled.org as migration target
  [OK] REQ-INST-1  required: []  stages: -doc -impl -unit -int
       endpoint ID vs instance split (adapter-agnostic ID)
  [OK] REQ-INST-10  required: [impl, unit]  stages: -doc +impl +unit -int
       Qualified addressing [subnet:]id[@node] + ambiguity forces qualification
  [OK] REQ-INST-11  required: [impl, unit]  stages: -doc +impl +unit -int
       spt rename <id> rippled to all instances (collision-checked, 6.5-reconciled)
  [OK] REQ-INST-12  required: [impl, unit]  stages: -doc +impl +unit -int
       Endpoint visibility per-(endpoint,subnet): excluded semantics, OR-of-defaults + override, gates sync
  [OK] REQ-INST-13  required: [impl, unit]  stages: -doc +impl +unit -int
       Subnet-exclusive sync + per-endpoint subnet-membership list
  [OK] REQ-INST-14  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Resource advertisement (subnet resource registry): free-text blurb, both-authored, registry projection, visibility/whitelist-gated
  [OK] REQ-INST-15  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Immutable home subnet (assigned at creation: auto-if-one/ask-if-many) + spt fork (cross-subnet clone to a new identity, copy-then-diverge, not re-home); adapter chosen at creation from registered hostable adapters, changed only via launch/resume-under-new (ADR-0010)
  [OK] REQ-INST-2  required: [impl, unit]  stages: -doc +impl +unit -int
       Per-node files, synced Psyche mind
  [OK] REQ-INST-3  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Dormant (warm) / suspended (cold) resting states
  [OK] REQ-INST-4  required: [impl, unit]  stages: -doc +impl +unit -int
       active to dormant/suspended fires a transition echo commune
  [OK] REQ-INST-5  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Two-tier context sync (live to all, project to same-project)
  [OK] REQ-INST-6  required: [impl, unit, int]  stages: +doc +impl +unit +int
       Deferred messages not delivered to dormant/suspended instances
  [OK] REQ-INST-7  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Subnet registry + bare-id resolution policy
  [OK] REQ-INST-8  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Remote-control mode distinct from local operation
  [OK] REQ-INST-9  required: [impl, unit]  stages: -doc +impl +unit -int
       Multi-subnet membership (same-user N subnets; cross-user seam)
  [OK] REQ-INSTALL-1  required: [doc, impl, int]  stages: +doc +impl -unit +int
       Two install paths; signed one-line script; OS-service registration
  [OK] REQ-INSTALL-2  required: [doc]  stages: +doc -impl -unit -int
       Marketplace-repackaging-friendly install
  [OK] REQ-INSTALL-3  required: [impl, int]  sta


Found 3 files
M12-W1-PLAN.md
traceable-reqs.toml
crates\spt-daemon\tests\attach.rs


446:id = "REQ-HOST-RUN-1"
447-[Omitted long context line]
448:required_stages = ["impl", "unit", "int"]  # activated M12-W1: impl = harnesshost::{prepare_harness_spawn, launch_harness_brokered_in} + cli cmd_endpoint_run; unit = harnesshost template-fill/fail-closed/mint; int = spt_hosted_bringup_then_cross_node_attach_drives_the_pty (bringup spawns into the broker PTY, cross-node attach drives it). cwd/project deferred to REQ-HOST-RUN-2 (W1 ships broker-inherited cwd)
449-[[requirements]]
450:id = "REQ-RC-1"
451-[Omitted long context line]
452-required_stages = ["impl", "unit", "int"]  # activated M12-W1: impl = rc::run_attach pump (single-Brain + stdin-thread + seq-dedup + ctrl-b detach) + cli Cmd::Rc; unit = detach-keybind semantics + prefix-spans-chunks + op-minter; int (CROSS-NODE face) = spt_hosted_bringup_then_cross_node_attach_drives_the_pty + loopback_self_dial_is_refused (pins the local-transport choice). LOCAL face int (loopback-conn B1) lands W1.5 with the "both transports → one pump" assertion (doyle 2026-06-14)
453-[[requirements]]
454:id = "REQ-HOST-RUN-2"
455-[Omitted long context line]
456-required_stages = []
457-


traceable-reqs check = **196 complete, 0 incomplete, 0 findings** (EXIT=0); REQ-HOST-RUN-1 + REQ-RC-1 at impl+unit+int, REQ-HOST-RUN-2 correctly `[]` (deferred). Now verifying the int test isn't hollow — does it actually prove bringup→cross-node-attach-drives-PTY→survives-detach:


189:fn loopback_self_dial_is_refused_local_uses_fallback_transport() {
217:fn spt_hosted_bringup_then_cross_node_attach_drives_the_pty() {


180
181	// [int->REQ-RC-1] loopback self-dial is REFUSED by the transport — pins the
182	// finding that drives `spt rc`'s LOCAL transport choice. A node cannot QUIC-dial
183	// its own node id (iroh: "Connecting to ourself is not supported"), so local
184	// attach CANNOT ride a self-dialed QUIC conn. The one-pump invariant therefore
185	// holds via the fallback transport (a broker-local loopback conn feeding the same
186	// `serve_attach` pump), NOT a self-dial — exactly the doyle-approved fallback.
187	// Cross-node attach (the prize) is unaffected: it dials a real remote peer.
188	#[test]
189	fn loopback_self_dial_is_refused_local_uses_fallback_transport() {
190	    init_home();
191	    let dir = tempfile::tempdir().expect("tempdir");
192	    let name = unique_name();
193	    let _broker = net_broker(&name, &dir.path().join("solo"));
194	    let mut brain = connect_retry(&name);
195
196	    let status = brain.net_status().expect("status");
197	    // Dialing our OWN advertised address is refused at the transport — fail-fast,
198	    // never a deadlock; local attach must not be built on a self-dial.
199	    let err = brain
200	        .net_dial(status.addr, Some(1))
201	        .expect_err("self-dial must be refused, not silently succeed");
202	    assert!(
203	        err.to_string().contains("ourself") || err.to_string().contains("our own"),
204	        "self-dial refused for the documented reason, got: {err}"
205	    );
206	}
207
208	// [int->REQ-HOST-RUN-1] [int->REQ-RC-1] spt-hosted harness bringup + cross-node
209	// attach, end to end: `harnesshost::launch_harness_brokered_in` spawns a harness's
210	// `[session.self]` into broker A's PTY (the spt-hosted bringup — the daemon owns
211	// the PTY, not an external launcher), then an operator on broker B attaches
212	// CROSS-NODE and drives the brought-up PTY: keystrokes reach it, its output
213	// streams back. This is the W1 gate proof — "bring up + attach a real harness
214	// endpoint" over the byte-identical cross-node attach pump; the session outlives
215	// the viewport (detach never kills it).
216	#[test]
217	fn spt_hosted_bringup_then_cross_node_attach_drives_the_pty() {
218	    init_home();
219	    let dir = tempfile::tempdir().expect("tempdir");
220	    let (name_a, name_b) = (unique_name(), unique_name());
221	    let _broker_a = net_broker(&name_a, &dir.path().join("a"));
222	    let _broker_b = net_broker(&name_b, &dir.path().join("b"));
223
224	    // Bring up an spt-hosted harness on A. Its `[session.self]` is a real echo
225	    // binary standing in for a harness — the bringup spawns it into broker A's
226	    // PTY exactly as it would a `claude` command.
227	    #[cfg(unix)]
228	    let self_cmd = "cat";
229	    #[cfg(windows)]
230	    let self_cmd = "findstr .";
231	    let manifest = spt_runtime::Manifest::from_toml_str(&format!(
232	        "[adapter]\nname = \"mock\"\nkind = \"harness\"\nversion = \"1\"\n\
233	         min_spt_core_version = \"0\"\n\n[session.self]\ncommand = '{self_cmd}'\n"
234	    ))
235	    .expect("harness manifest");
236	    let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(
237	        &name_a, "cc-ep", "mock", "sess-1", &manifest,
238	    )
239	    .expect("spt-hosted bringup spawns into broker A's PTY");
240
241	    // Operator on B attaches CROSS-NODE to the brought-up session.
242	    let mut target = connect_retry(&name_a);
243	    let a_addr = target.net_status().expect("a status").addr;
244	    let mut operator = connect_retry(&name_b);
245	    let b_node = operator
246	        .net_status()
247	        .expect("b status")
248	        .node_id_hex
249	        .expect("node id");
250	    let conn = operator.net_dial(a_addr, Some(1)).expect("dial A");
251	    let stream_b =
252	        request_attach(&mut operator, conn.conn_id, spawned.session_id, 0, 2).expect("request");
253	    operator
254	        .net_stream_subscribe(stream_b, 0)
255	        .expect("operator subscribe");
256
257	    // A serves the inbound attach stream (endpoint = the brought-up id).
258	    let (stream_a, origin) = wait_for_stream(&mut target);
259	    assert_eq!(origin, b_node, "serve gate subject is the handshake identity");
260	    let server = thread::spawn(move || {
261	        let outcome = serve_attach(&mut target, stream_a, "cc-ep", &origin, 0).expect("serve");
262	        (outcome, target)
263	    });
264
265	    // Drive the brought-up PTY from B; the echo streams back over the wire.
266	    send_attach_input(&mut operator, stream_b, b"HOSTED_OK\r", 1).expect("input");
267	    let mut decoder = AttachDecoder::new();
268	    let (mut cursor, mut viewport) = (0u64, Vec::new());
269	    render_until(
270	        &mut operator,
271	        stream_b,
272	        &mut decoder,
273	        &mut cursor,
274	        &mut viewport,
275	        b"HOSTED_OK",
276	    );
277
278	    // Detach: the brought-up session outlives the viewport.
279	    operator
280	        .net_stream_send(stream_b, &[], None, true)
281	        .expect("detach");
282	    let (outcome, mut target) = server.join().expect("serve thread");
283	    assert_eq!(outcome, AttachServeOutcome::Detached);
284	    target.kill_session().expect("cleanup kill");
285	}
286
287	// [unit->REQ-INST-8] drive an agent('s session) on machine A from machine B:
288	// B's keystrokes reach A's real PTY child over the wire (journaled input
289	// path), the child's output streams back as sequenced records, and B renders
290	// its own marker echoed remotely. The serve gate sees the handshake-proven
291	// origin; B detaching ends the viewport with the session still running on A.
292	#[test]
293	fn remote_attach_drives_a_real_pty_cross_daemon() {
294	    init_home();
295	    let dir = tempfile::tempdir().expect("tempdir");
296	    let (name_a, name_b) = (unique_name(), unique_name());
297	    let broker_a = net_broker(&name_a, &dir.path().join("a"));
298	    let _broker_b = net_broker(&name_b, &dir.path().join("b"));
299
300	    // Target A: spawn the echo child.
301	    let mut target = connect_retry(&name_a);
302	    let sid = target
303	        .spawn_session(echo_spawn_req())
304	        .expect("spawn echo child");
305	    let a_addr = target.net_status().expect("a status").addr;
306
307	    // Operator B: dial A, open the attach stream, request the session.
308	    let mut operator = connect_retry(&name_b);
309	    let b_node = operator
310	        .net_status()
311	        .expect("b status")
312	        .node_id_hex
313	        .expect("node id");
314	    let conn = operator.net_dial(a_addr, Some(1)).expect("dial");
315	    let stream_b = request_attach(&mut operator, conn.conn_id, sid, 0, 2).expect("request");
316	    operator
317	        .net_stream_subscribe(stream_b, 0)
318	        .expect("operator subscribe");
319
320	    // Target A: the attach stream arrives; serve it on its own thread under
321	    // the handshake-proven origin (KH 7.5 — the stream table, not payload).
322	    let (stream_a, origin) = wait_for_stream(&mut target);
323	    assert_eq!(
324	        origin, b_node,
325	        "serve gate subject IS the QUIC handshake identity"
326	    );
327	    let server = thread::spawn(move || {
328	        let outcome = serve_attach(&mut target, stream_a, "ling", &origin, 0).expect("serve");
329	        (outcome, target)
330	    });
331
332	    // B types a marker; A's child echoes it; B renders it from the wire.
333	    send_attach_input(&mut operator, stream_b, b"REMOTE_DRIVE_OK\r", 1).expect("input");
334	    let mut decoder = AttachDecoder::new();
335	    let (mut cursor, mut viewport) = (0u64, Vec::new());
336	    render_until(
337	        &mut operator,
338	        stream_b,
339	        &mut decoder,
340	        &mut cursor,
341	        &mut viewport,
342	        b"REMOTE_DRIVE_OK",
343	    );
344
345	    // Detach: B finishes its send side; the serve loop ends with the session
346	    // still alive on A (viewport over, compute stays remote).
347	    operator
348	        .net_stream_send(stream_b, &[], None, true)
349	        .expect("detach");

// [int->REQ-RC-1] loopback self-dial is REFUSED by the transport — pins the
// finding that drives `spt rc`'s LOCAL transport choice. A node cannot QUIC-dial
// its own node id (iroh: "Connecting to ourself is not supported"), so local
// attach CANNOT ride a self-dialed QUIC conn. The one-pump invariant therefore
// holds via the fallback transport (a broker-local loopback conn feeding the same
// `serve_attach` pump), NOT a self-dial — exactly the doyle-approved fallback.
// Cross-node attach (the prize) is unaffected: it dials a real remote peer.
#[test]
fn loopback_self_dial_is_refused_local_uses_fallback_transport() {
    init_home();
    let dir = tempfile::tempdir().expect("tempdir");
    let name = unique_name();
    let _broker = net_broker(&name, &dir.path().join("solo"));
    let mut brain = connect_retry(&name);

    let status = brain.net_status().expect("status");
    // Dialing our OWN advertised address is refused at the transport — fail-fast,
    // never a deadlock; local attach must not be built on a self-dial.
    let err = brain
        .net_dial(status.addr, Some(1))
        .expect_err("self-dial must be refused, not silently succeed");
    assert!(
        err.to_string().contains("ourself") || err.to_string().contains("our own"),
        "self-dial refused for the documented reason, got: {err}"
    );
}

// [int->REQ-HOST-RUN-1] [int->REQ-RC-1] spt-hosted harness bringup + cross-node
// attach, end to end: `harnesshost::launch_harness_brokered_in` spawns a harness's
// `[session.self]` into broker A's PTY (the spt-hosted bringup — the daemon owns
// the PTY, not an external launcher), then an operator on broker B attaches
// CROSS-NODE and drives the brought-up PTY: keystrokes reach it, its output
// streams back. This is the W1 gate proof — "bring up + attach a real harness
// endpoint" over the byte-identical cross-node attach pump; the session outlives
// the viewport (detach never kills it).
#[test]
fn spt_hosted_bringup_then_cross_node_attach_drives_the_pty() {
    init_home();
    let dir = tempfile::tempdir().expect("tempdir");
    let (name_a, name_b) = (unique_name(), unique_name());
    let _broker_a = net_broker(&name_a, &dir.path().join("a"));
    let _broker_b = net_broker(&name_b, &dir.path().join("b"));

    // Bring up an spt-hosted harness on A. Its `[session.self]` is a real echo
    // binary standing in for a harness — the bringup spawns it into broker A's
    // PTY exactly as it would a `claude` command.
    #[cfg(unix)]
    let self_cmd = "cat";
    #[cfg(windows)]
    let self_cmd = "findstr .";
    let manifest = spt_runtime::Manifest::from_toml_str(&format!(
        "[adapter]\nname = \"mock\"\nkind = \"harness\"\nversion = \"1\"\n\
         min_spt_core_version = \"0\"\n\n[session.self]\ncommand = '{self_cmd}'\n"
    ))
    .expect("harness manifest");
    let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(
        &name_a, "cc-ep", "mock", "sess-1", &manifest,
    )
    .expect("spt-hosted bringup spawns into broker A's PTY");

    // Operator on B attaches CROSS-NODE to the brought-up session.
    let mut target = connect_retry(&name_a);
    let a_addr = target.net_status().expect("a status").addr;
    let mut operator = connect_retry(&name_b);
    let b_node = operator
        .net_status()
        .expect("b status")
        .node_id_hex
        .expect("node id");
    let conn = operator.net_dial(a_addr, Some(1)).expect("dial A");
    let stream_b =
        request_attach(&mut operator, conn.conn_id, spawned.session_id, 0, 2).expect("request");
    operator
        .net_stream_subscribe(stream_b, 0)
        .expect("operator subscribe");

    // A serves the inbound attach stream (endpoint = the brought-up id).
    let (stream_a, origin) = wait_for_stream(&mut target);
    assert_eq!(origin, b_node, "serve gate subject is the handshake identity");
    let server = thread::spawn(move || {
        let outcome = serve_attach(&mut target, stream_a, "cc-ep", &origin, 0).expect("serve");
        (outcome, target)
    });

    // Drive the brought-up PTY from B; the echo streams back over the wire.
    send_attach_input(&mut operator, stream_b, b"HOSTED_OK\r", 1).expect("input");
    let mut decoder = AttachDecoder::new();
    let (mut cursor, mut viewport) = (0u64, Vec::new());
    render_until(
        &mut operator,
        stream_b,
        &mut decoder,
        &mut cursor,
        &mut viewport,
        b"HOSTED_OK",
    );

    // Detach: the brought-up session outlives the viewport.
    operator
        .net_stream_send(stream_b, &[], None, true)
        .expect("detach");
    let (outcome, mut target) = server.join().expect("serve thread");
    assert_eq!(outcome, AttachServeOutcome::Detached);
    target.kill_session().expect("cleanup kill");
}

// [unit->REQ-INST-8] drive an agent('s session) on machine A from machine B:
// B's keystrokes reach A's real PTY child over the wire (journaled input
// path), the child's output streams back as sequenced records, and B renders
// its own marker echoed remotely. The serve gate sees the handshake-proven
// origin; B detaching ends the viewport with the session still running on A.
#[test]
fn remote_attach_drives_a_real_pty_cross_daemon() {
    init_home();
    let dir = tempfile::tempdir().expect("tempdir");
    let (name_a, name_b) = (unique_name(), unique_name());
    let broker_a = net_broker(&name_a, &dir.path().join("a"));
    let _broker_b = net_broker(&name_b, &dir.path().join("b"));

    // Target A: spawn the echo child.
    let mut target = connect_retry(&name_a);
    let sid = target
        .spawn_session(echo_spawn_req())
        .expect("spawn echo child");
    let a_addr = target.net_status().expect("a status").addr;

    // Operator B: dial A, open the attach stream, request the session.
    let mut operator = connect_retry(&name_b);
    let b_node = operator
        .net_status()
        .expect("b status")
        .node_id_hex
        .expect("node id");
    let conn = operator.net_dial(a_addr, Some(1)).expect("dial");
    let stream_b = request_attach(&mut operator, conn.conn_id, sid, 0, 2).expect("request");
    operator
        .net_stream_subscribe(stream_b, 0)
        .expect("operator subscribe");

    // Target A: the attach stream arrives; serve it on its own thread under
    // the handshake-proven origin (KH 7.5 — the stream table, not payload).
    let (stream_a, origin) = wait_for_stream(&mut target);
    assert_eq!(
        origin, b_node,
        "serve gate subject IS the QUIC handshake identity"
    );
    let server = thread::spawn(move || {
        let outcome = serve_attach(&mut target, stream_a, "ling", &origin, 0).expect("serve");
        (outcome, target)
    });

    // B types a marker; A's child echoes it; B renders it from the wire.
    send_attach_input(&mut operator, stream_b, b"REMOTE_DRIVE_OK\r", 1).expect("input");
    let mut decoder = AttachDecoder::new();
    let (mut cursor, mut viewport) = (0u64, Vec::new());
    render_until(
        &mut operator,
        stream_b,
        &mut decoder,
        &mut cursor,
        &mut viewport,
        b"REMOTE_DRIVE_OK",
    );

    // Detach: B finishes its send side; the serve loop ends with the session
    // still alive on A (viewport over, compute stays remote).
    operator
        .net_stream_send(stream_b, &[], None, true)
        .expect("detach");


Verified independently — the gate is real, not a rubber-stamp:
- `traceable-reqs check` reproduced: **196 complete, 0 findings**; REQ-HOST-RUN-1 + REQ-RC-1 at impl+unit+int; REQ-HOST-RUN-2 correctly `[]`.
- Int test inspected (`attach.rs:217`): genuinely brings up `[session.self]` via the **real** `launch_harness_brokered_in` into broker A's PTY, operator on B attaches **cross-node**, drives it (`HOSTED_OK` echoes back over the wire), detach → `Detached` + session outlives viewport. Origin asserted = handshake identity (KH 7.5). Not hollow.
- `loopback_self_dial_is_refused` genuinely asserts the iroh self-dial refusal — pins the local-transport choice.

W1 gate = **PASS**. Declaring it:


cd 'C:\Users\decid\Documents\projects\spt-core'; "$OWL" send todlando doyle <<'EOF'
W1 GATE: PASS. ✅ Independently verified, not rubber-stamped:

- traceable-reqs check reproduced here: 196 complete, 0 incomplete, 0 findings. REQ-HOST-RUN-1 + REQ-RC-1 = impl+unit+int; REQ-HOST-RUN-2 = [] (correctly deferred).
- Read the int test (attach.rs:217): it drives the REAL launch_harness_brokered_in — spawns [session.self] into broker A's PTY, operator on B attaches CROSS-NODE, HOSTED_OK round-trips through the PTY over the wire, detach→Detached, session outlives the viewport. Serve gate asserts origin = handshake identity (KH 7.5). Genuine end-to-end proof, not a stub.
- self-dial-refused test genuinely pins the iroh refusal (the WHY for B1).

Gate condition met: spt-hosted harness bringup + cross-node rc attach + pump, all green. Local attach correctly carried to W1.5 (not gated here).

Trusting your reported full-suite (46 bins, 0 fail) + clippy -D warnings EXIT=0 — I reproduced the trace + inspected the load-bearing int test; no need to re-run the whole suite.

PROCEED TO W1.5: B1 broker loopback-conn + local attach int + the "both transports → one pump" assertion. Then REQ-HOST-RUN-2 (cwd) before the M12 gate. Stays uncommitted until the operator calls it. Nice work. — doyle
EOF
echo "---EXIT: $?---"

Declare W1 gate PASS to todlando


➜ SENT:todlando
---EXIT: 0---


W1 gate PASS declared. Updating progress memory + reporting:


Edit

- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) by todlando: bringup+cross-node-rc+pump built+green. Self-dial IMPOSSIBLE (iroh refuses); doyle ruled B1 (broker loopback-conn) scoped W1.5. W1 gate = bringup+cross-node+pump NOW; W1.5 = local attach; REQ-HOST-RUN-2 (cwd) before M12 close. Awaiting todlando W1 gate ping

- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) GATE PASS by doyle 2026-06-14 (traceable reproduced 196/0; int test independently inspected — real launch_harness_brokered_in bringup + cross-node attach + survives-detach). UNCOMMITTED pending operator. NEXT = W1.5 (B1 broker loopback-conn = local attach) → REQ-HOST-RUN-2 (cwd) before M12 close


- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) by todlando: bringup+cross-node-rc+pump built+green. Self-dial IMPOSSIBLE (iroh refuses); doyle ruled B1 (broker loopback-conn) scoped W1.5. W1 gate = bringup+cross-node+pump NOW; W1.5 = local attach; REQ-HOST-RUN-2 (cwd) before M12 close. Awaiting todlando W1 gate ping
- [M9 status](m9-wave1-progress.md) — M9 COMPLETE + SHIPPED: all 4 gates passed (G1/G2/Gateway-WAN/G3), all PRs merged to main (operator GO 2026-06-13), **v0.5.0 tagged** (counter 10, deployah signing). Don't re-plan. See [[v050-published]] for release state
- [spt-claude-code NEXT](spt-claude-code-next.md) — SCOPING DONE 2026-06-14 (SCOPE.md/CONTEXT.md committed @09d6020). M12 handed to todlando as GATING prereq (now executing W1). RESUME = author experimplate → instantiate spt-claude-code → write perri HANDOFF. perri builds from spt-releases+GH-Pages ONLY
- [M10 digest extractor](m10-digest-extractor.md) — doyle GATE PASS 2026-06-13: all 4 waves (39463a2..afa0561), REQ-TERM-5/6/7 evidenced, full suite + capstone E2E green, independently verified. `[digest]` imperative extractor seam (ADR-0019, reverses M9) + thread-spanning (session ledger) + two-origin merge + digest-proof + skip-diagnostics. SHIPPED as v0.6.0 (counter 11) — see [[v060-published]]. Carried: real-CC digest-proof → perri/spt-claude-code acceptance criterion
- [v0.6.0 published](v060-published.md) — M10 PUBLIC 2026-06-13 (counter 11); todlando merge+tag, deployah sign+publish; minor bump (0.5.0→0.6.0); hashes linux 002ed710… win 4f5c61e8…. ROUTINE seamless roll now (respawn-path arc closed); publisher leg proven 5× (counter 7-11). enlyzeam still pending natural-propagation confirm
- [traceable per-wave activation](traceable-per-wave-activation.md) — traceable-reqs check requires ALL declared required_stages evidenced per commit; activate stages incrementally as evidence lands (int at final wave), never the full end-state list upfront
- [v0.5.0 published](v050-published.md) — M9 PUBLIC 2026-06-13 (counter 10); todlando drove merge+tag, deployah signed+published (handoff split vs v0.4.x full-solo); minor bump (breaking [pty_digest] removal pre-1.0); hashes linux 4c079100… win 984298ba…. FLEET ROLL = next phase, operator-gated (their ask was merge+publish, DONE). v0.4.2→v0.5.0 kitsubito apply = FIRST genuine Linux seamless no-bounce proof on fixed broker; bytes-assert→auto-rollback armed. deployah on-call for hash check
- [v0.5.0 release](v050-release.md) — M9 SHIPPED: merged to main (22a9b6f, clean stack collapse; #8 auto-closed by mid-stack --delete-branch, content rode via #9), v0.5.0 tagged (0.4.2→0.5.0 minor: feature milestone + breaking [pty_digest] removal), counter 10, deployah signing. LESSON: don't --delete-branch mid-stack
- [M9 Wave3/G3 digest](m9-wave3-digest-progress.md) — session-digest re-founded on session logs (REQ-TERM-4, ADR-0008 amendment); G3 STAMPED PASS by doyle 2026-06-13, PR#10 held. On-demand projection (topology-independent, no PTY); PTY-parse retired; published digest-record contract; autonomous-freshness+Option-C deferred to consuming-frontend milestone (DEFERRED.md:44)
- [hfenduleam target bloat → CI RED](hfenduleam-target-bloat-ci-red.md) — Win-runner LNK1318/os-error-112 can be disk-full from cargo target bloat (396GB seen), NOT a code regression; cargo clean + re-run; check disk before treating a Windows RED as a code defect
- [v0.4.1 Linux respawn blocker](v041-linux-respawn-blocker.md) — v0.4.1 PUBLIC (counter 8); fleet roll found Linux brain-respawn bug (current_exe follows apply-rename → brain runs OLD bytes, records applied:8). hfenduleam(Win) GREEN, kitsubito(Linux) RED. RESOLVED by v0.4.2 — see [[v042-published]]
- [v0.4.2 published](v042-published.md) — PUBLIC 2026-06-11 (counter 9); FIXES v0.4.1 Linux respawn blocker (respawn via canonical install path not current_exe + bytes-assert→auto-rollback; REQ-HAZARD-BRAIN-RESPAWN-PATH + real-rename E2E). deployah drove full merge→tag→sign→publish solo. hashes linux 500a9e7b… win b9ec8ddd…. ASYMMETRY: v0.4.2 apply still rides buggy v0.4.0 broker → 1 final manual Linux bounce loads fix; seamless proof on NEXT update via debug-channel. todlando driving fleet roll + restoration close
- [pump-IPC-deadline fix](pump-ipc-deadline-fix.md) — DONE: MERGED to main @ 4d8b810 (PR #3), doyle-approved, CI green both runners; reader-thread+channel carrier (nonblock dead on Win named pipes), both amendments folded, no tag (rides v0.4.1). Don't re-plan
- [CI self-host migration](ci-selfhost-migration.md) — DONE: runners live on kitsubito + hfenduleam (gravity retired 2026-06-07); twohost jobs gated by [twohost] commit tag; don't re-plan
- [which process is the listener](spt-daemon-is-live-infra.md) — owl.exe (plugin) = live listener, hands off; target\debug\spt.exe daemon = dev/test artifact, killable
- [self-clear skill API-blocked](self-clear-skill-api-blocked.md) — never invoke /self-clear; usage-policy classifier refuses the turn; continue in-context / user-manual /clear instead
- [Adapter glue-model boundary](adapter-glue-model-boundary.md) — adapters = manifest + binary only; real adapters in own repos; no adapter-aware source in spt-core
- [M6 shipped v0.1.0](m6-shipped-v010.md) — v0.1.0 public 2026-06-05; release procedure = RELEASE-RUNBOOK.md; releases-repo/ is repo-face truth; don't re-plan M6
- [Gravity deploy procedure](gravity-deploy-procedure.md) — ssh david@100.100.62.43; build in ~/spt-core-deploy NOT runner workspace; setsid daemon; ~1 min first-sync after restart
- [Linux SPT_HOME no-bug](linux-spt-home-no-bug.md) — unix spawn_detached inherits env (no env_clear), forwards SPT_HOME; only Win CreateEnvironmentBlock rebuilds/drops it (f99dc25). Default-home daemon = launched w/o the var, not a propagation bug; don't reopen
- [kitsubito Linux rig](kitsubito-linux-rig.md) — ssh reavus@kitsubito (key auth); cargo at ~/.cargo/bin; deploy checkout origin=local runner (NOT GitHub) so unpushed commits transfer via git bundle; live spt-daemon user unit, --user ops need no sudo; build-in-scratch leaves ~/.local/bin/spt untouched
- [Use $OWL send for messaging](owl-send-not-legacy-spt-send.md) — `$OWL send` works (legacy = where live perches run); `spt send` is the NEW spt-core binary but no perches on it yet → bounces NO_PERCH; don't read `$?` after piping send through tail (use PIPESTATUS)
- [daemon service-detection gotcha](daemon-service-detection-gotcha.md) — service detection reading GLOBAL OS state is blind on a single-home non-elevated dev box; gate Linux service routing on SPT_HOME-unset, Windows run-refuse on would-vanish (not is_elevated); push→CI BEFORE tagging a release (v0.3.0 tagged early, CI caught 2 regressions, retagged)
- [v0.3.2 platform-safe update](v032-platform-safe-update.md) — REQ-UPD-8 PUBLISHED + rig-verified 2026-06-09 (counter 6): fetch stages multi-platform update-set, apply refuses unstamped/wrong-OS single. Fleet kitsubito+hfenduleam=0.3.2 (ENLYZEAM TODO). GOTCHA: fetch/pump floor=staged_version → can't re-stage same version single→set; clear `$SPT_HOME/releases` first (Linux ~/.spt-core, Win %LOCALAPPDATA%\spt-core). CORRECTION: release-publish never built a SignedUpdateSet pre-v0.3.2 (was debug_rollout)
- [boot-race self-heal](boot-race-self-heal.md) — v0.3.1: daemon self-heals a net-less boot start (NetHost retry+attach to OnceLock broker); systemd-USER network-online ordering does NOT reliably prevent the race under linger (field-proven kitsubito) — the self-heal is the real guarantee, unit ordering is belt
- [v0.4.0 published + fleet rolled](v040-published-fleet-rolled.md) — restoration release PUBLIC 2026-06-10 (counter 7); D7-4 bytes-match done both nodes (brain.ready exe_hash == artifact); v0.3.2→v0.4.0 apply needed last manual bounce (in-process can't self-relaunch); seamless no-bounce proof + enlyzeam catch-up + milestone close all deferred to v0.4.1
- [v0.4.1 published](v041-published.md) — PUBLIC 2026-06-11 (counter 8); deployah drove full merge→tag→sign→publish solo; D7 SEAMLESS no-bounce fleet-verify payload (apply must roll bytes live w/ pid change, no manual bounce); acceptance hashes linux 35c9544c… win 728a1bfc…; hfenduleam(Win) GREEN no-bounce, kitsubito(Linux) RED → CLOSE BLOCKED: v0.4.0 broker respawns via current_exe() which follows apply-rename to .old-8=OLD bytes (Linux only); artifacts SOUND don't yank; v0.4.2 fix = respawn by canonical path + bytes-assert, needs manual Linux broker bounce; deployah takes counter 9 publish
- [hfenduleam disk-full CI](hfenduleam-disk-full-ci.md) — Win CI LNK1318/os-error-112 = disk full not code; cargo clean dev target (was 396GB), rerun job
- [M9 gateway-wan progress](m9-gateway-wan-progress.md) — REQ-MSG-6 cross-node Gateway WAN user-msg; PR #9 open (base m9-user-surfaces), impl complete + local sweep green, HOLDING on CI; PR #8 G2 now green post disk-fix

-- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) by todlando: bringup+cross-node-rc+pump built+green. Self-dial IMPOSSIBLE (iroh refuses); doyle ruled B1 (broker loopback-conn) scoped W1.5. W1 gate = bringup+cross-node+pump NOW; W1.5 = local attach; REQ-HOST-RUN-2 (cwd) before M12 close. Awaiting todlando W1 gate ping

+- [M12 W1 progress](m12-w1-progress.md) — M12 W1 (spt endpoint run + spt rc) GATE PASS by doyle 2026-06-14 (traceable reproduced 196/0; int test independently inspected — real launch_harness_brokered_in bringup + cross-node attach + survives-detach). UNCOMMITTED pending operator. NEXT = W1.5 (B1 broker loopback-conn = local attach) → REQ-HOST-RUN-2 (cwd) before M12 close