1	# spt-core Roadmap
2
3	> Lightweight-but-structured path from design → fruition. Not a GSD/phase-gated process — a clear sequence with de-risk gates. Planning corpus is complete (PRD + CONTEXT + 5 ADRs + 6 design docs); this is how we build it.
4
5	## End goal
6
7	A harness-independent core for an agent ecosystem — messaging, live-agent lifecycle, terminal hosting, **first-class P2P networking**, and a runtime-manifest harness contract — shipped as Rust crates + a single `spt` binary. The proof: the same agent runs natively across all of a user's machines with a synced mind, reachable and operable from anywhere, with seamless self-update and zero-config cross-machine messaging. First thing built atop it: a rebuilt `spt` plugin at parity with today's modern SPT.
8
9	## Scope decisions (locked)
10
11	- **v1 = the full PRD, networking included.** The networked element is the *reason* spt-core exists; it is not deferrable to v1.1.
12	- **The hard freeze on `claude_skill_owl` is not a clock.** It works today and delivers value as-is while spt-core is built — no schedule pressure from it.
13	- **Process: lightweight but structured.** GSD is too heavy. This roadmap + the design docs are the structure.
14
15	## Path to fruition (sequence)
16
17	**Stage A — Red-team review (before building).** Pressure-test the design adversarially. Focus on the riskiest architectural decisions, not bikeshedding. Tools: `plan-eng-review` (architecture) + `codex` (adversarial second opinion). Scope-cutting is *not* a goal (v1 is locked as full PRD), but surfacing contradictions / over-engineering / unproven assumptions is.
18
19	**Stage B — De-risk spikes.** Throwaway proofs of the load-bearing unknowns — anything that, if it fails, forces a redesign. Do these before committing to the full build. See *De-risk spikes* below.
20
21	**Stage C — Walking skeleton.** Build the killer-quickstart slice end-to-end through the real crate boundaries: **two agents exchange a message** (first local; ultimately cross-node — the real proof). Validates the whole stack early; then thicken.
22
23	**Stage D — Phased build (M0–M5).** Dependency-ordered, each milestone ships something usable.
24
25	## Milestones (all v1)
26
27	- **M0 — Workspace + commodity layer.** Cargo workspace; `spt-proto` (wire envelope grammar, endpoint types, framing, Ed25519 identity, typed+binary payloads) + `spt-store` (spool, perch layout, trust store, registry persistence). Copy-verbatim the stable formats from the sister project (ADR-0001).
28	- **M1 — Local messaging + binary.** `spt-msg` (delivery TCP+spool, routing, send/ring/ready) + the `spt` binary + CLI. **= the killer quickstart (local): two agents exchange a message.** Task breakdown: [`M1-PLAN.md`](./M1-PLAN.md).
29	- **M2 — Harness contract + lifecycle.** **Strictly harness-agnostic — zero Claude Code conventions in this repo.** spt-core defines the adapter contract; the only adapter-shaped artifact in-tree is a generic mock/test adapter (manifest fixture + fake `api` caller) that exercises the contract (PRD R-DOCS-2 dev-agent quickstart). **The rebuilt `spt` plugin (Claude Code) is a SEPARATE downstream project** built atop these crates/binary — spt-core v1's acceptance proof (PRD §"feature parity"), not an spt-core deliverable. It lives and builds outside this repo. **Split into two JIT milestones** (decision 2026-06-01):
30	  - **M2a — Harness contract. ✅ delivered (2026-06-01).** `spt-runtime` (AgentRuntime, ManifestRuntime, manifest schema) + the `api` subcommand surface (seed/listen/bind/state/echo-gate/poll/worker-*/boundary/session-end/presence/history-log/emit/capability) + the **harness-hosted** startup topology (seed→listen→bind, interim seed-file) + local-api-auth + the generic mock adapter + contract E2E. All M2a reqs activated + green. Task breakdown: [`M2a-PLAN.md`](./M2a-PLAN.md).
31	  - **M2b — Live-agent lifecycle. ✅ delivered (2026-06-01).** `spt-live` (fifth layer): history subsystem (fetcher/locate-normalize/native) + spawn-psyche (nested perch, spt-core-owned `$psyche_prompt`) + echo-commune (history brief → single-writer commune drop) + drop-file ingest (single-writer 6.4 + direct-write precedence 6.5) + interim pulse + signoff/boundary ordering invariants (echo-before-signoff 3.3, grace-before-signoff 1.1, stale-signoff sweep 3.2) + resume seam (continue-existing / fresh-with-preload). Hosted by the `api listen` runner + `api shutdown`; mock adapter gained the lifecycle templates; LiveAgent E2E proves spawn→Psyche-perch→commune-ingest→graceful-signoff harness-agnostically. All M2b reqs activated + green. Task breakdown: [`M2b-PLAN.md`](./M2b-PLAN.md).
32	  - **Interim, no daemon** (like M1): the `api listen` process holds the perch + relays; the seed bridges via a file. **The spt-hosted topology (PTY-launch) + the consolidated `spt-daemon` move to M3** (they need the `spt-term` broker) — this narrows the earlier "both topologies in M2" wording.
33	- **M3 — Terminal wrapper + daemon + self-update.** `spt-term` (session-surface, PTY, broker) + the consolidated `spt-daemon` (broker/brain split) + peer-propagated signed self-update. Split M3a/M3b/M3c. Scope + carried-forward Spike #1 gaps: [`M3-PLAN.md`](./M3-PLAN.md).
34	  - **M3a — `spt-term` session-surface mechanism. ✅ delivered (2026-06-02).** The sixth crate (`…→spt-msg→spt-term`, sibling of spt-runtime): the `SessionSurface` trait + native PTY backends (ConPTY/forkpty via `portable-pty`, REQ-TERM-1) · ConPTY-DSR auto-answer drain pump (REQ-HAZARD-CONPTY-DSR) · send-keys/send-line injection (REQ-TERM-2) · bounded byte-stream (REQ-TERM-3) · the PTY-digest parser primitive (REQ-TERM-4 impl/unit, ADR-0008). OS-neutral per Spike #4/#5 (no OS's stream contract baked in; ANSI-stripped, tail-wins repaint tolerance). Validated on Windows (ConPTY) + Linux (forkpty). All M3a reqs activated + green. Task breakdown: [`M3a-PLAN.md`](./M3a-PLAN.md).
35	  - **M3b — `spt-daemon` broker/brain process. ✅ delivered (2026-06-03).** The seventh crate (`…→spt-live→spt-daemon→spt`) hosts many `spt-term` surfaces behind a **versioned local IPC** (named-pipe/UDS behind one `DaemonTransport` trait, forward-compatible frame schema): a **broker** (stable kernel — PTY masters, harness children, sockets; no logic) + a **brain** (restartable logic) so a brain kill/restart leaves the hosted PTY child + its output stream **intact and gapless** (Spike #1 made real; `gen_start = now()` each handoff). Consolidates the interim no-daemon model — the `api listen` pulse/Psyche/drop-ingest loops fold into the brain as config-paced in-process loops (only a thin stateless relay residue) · daemon-authoritative liveness (`info.json` `status` supersedes `is_process_alive` for hosted perches) · exactly-once broker↔brain boundary (broker-owned `EffectJournal` + dedup-at-effect, Spike #6) · auto-start + in-memory seed (the seed file is dead) · supervised-crash orphan-watch (carries the M2b graceful ordering) · the **digest daemon-half** (manifest `pty_digest` seam → `DigestParser` over the broker PTY, `spt digest <id>` pull + delta-stream + opt-in Path-B persistence → REQ-TERM-4 `int`). All M3b reqs activated + green; daemon E2E (spawn→Psyche-loop→commune→brain-restart-survives→graceful-signoff) on the CI matrix (ubuntu + windows). Task breakdown: [`M3b-PLAN.md`](./M3b-PLAN.md).
36	  - **M3c — signed self-update. ✅ delivered (2026-06-03).** The gated, signed, ripple-capable self-update engine (no new crate — it lives in `spt-daemon`, signing primitives in `release.rs`): the **update-class taxonomy** (brain-only / broker-compatible / broker-breaking, ADR-0004 §A) with the **brain-only zero-interruption swap** that drives the M3b handoff substrate (snapshot→drop→re-attach) so a live PTY child + its output stream survive the logic swap **untouched** (REQ-UPD-3, proven E2E `tests/brain_swap.rs`) · **verify-before-handoff** (Ed25519 over signed release metadata + SHA-256 artifact binding; `plan_verified` is the front door — no unverified binary reaches a handoff, REQ-UPD-2) · **rollback hardening** (monotonic version, metadata expiry, channel pinning, key revocation — REQ-HAZARD-UPDATE-ROLLBACK) · **consent gating** (default-gated, delivered to the most-recently-active live session via the new `info.json` `last_active_ms` recency stamp; opt-in full-auto — REQ-UPD-4) · **adapter ripple-update** (self-then-adapters via each manifest `[update]` avenue, with adapter content signing against the adapter's own key for `file_pull` / a `self_verifies` attest for opaque `delegated` updaters — REQ-UPD-5, REQ-SEAM-UPDATE). **Peer-propagation transport (REQ-UPD-1) defers to M4** — the engine ships with self-fetch/out-of-band delivery into the same seam. All M3c reqs activated + green. Task breakdown: [`M3c-PLAN.md`](./M3c-PLAN.md).
37	  - **M3 COMPLETE** (M3a + M3b + M3c all ✅, 2026-06-03): the real ADR-0004 architecture is live — native PTY hosting, the consolidated broker/brain daemon, and seamless signed self-update. Next milestone: M4.
38	- **M4 — Networking + instances. ✅ delivered (2026-06-04).** The eighth crate `spt-net` (Iroh WAN endpoint bound to the node's own Ed25519 identity, mDNS LAN discovery, TOTP-SPAKE2 pairing over a dedicated pre-trust ALPN with transcript binding / rate limiting / seed rotation+transfer, subnet registry + NDJSON replication under the per-node epoch lease) · the multi-instance model (registry rows + bare-id resolution refuse-and-qualify + visibility/sync-membership gates + rename/collision + **resting-state machine** dormant/suspended with measured warm/cold policy [`docs/DORMANCY-BUDGET.md`, red-team #9 closed] + **resource advertisement** REQ-INST-14 + **immutable home subnet & `spt fork`** REQ-INST-15, ADR-0010) · **cross-node Psyche sync** (git-native bundles over broker QUIC streams, two-tier scope gates, vector-precedence merge with conflicts surfaced + Psyche-reconciled, ADR-0013; **the gh-repo interim is retired**) · WAN messaging + remote-drive attach + chunked file transfer + per-endpoint access whitelists · the subnet **notification primitive** (replicated spool, dismiss-replication, boundary resurfacing; update consent + escalations refactored as producers) · the **update peer-propagation transport** into the M3c engine seam (offer-then-fetch, verify-before-stage per node, consent-notified — REQ-UPD-1) · production trigger loops (peer pump + inbound dispatcher — the subnet self-drives). **The real proof ran on the real rig: `[twohost]` CI run `26958175812` — pair → register → cross-node message → remote-drive (A typed into B's live PTY and read the echo) → Psyche sync both directions → update self-heal → notif dismiss-replication, HFENDULEAM (Windows) ↔ gravity-linux, whole ladder ~8 s** (`docs/TWO-HOST-RUNBOOK.md` + evidence appendix; `tests/twohost.rs` carries the nine activated `int` stages). Task breakdowns: [`M4-PLAN.md`](./M4-PLAN.md) + per-task JIT plans (D1–D9).
39	- **M5 — Shells, presence, deferred capabilities** (NEXT). Shell adapters + sleep/wake, PresenceChannel, and the consent-gated capabilities (remote-exec, instantiate-anywhere) landing into the v1 consent seam. Carried-in seams from M4: REQ-INST-6 (deferred-message resting gate), remote `spt suspend id@node` arm, remote fork arm, relay rendezvous routing (D2f Q1), notif-ack → `apply_brain_only` apply orchestration, cross-runner CI two-host job, KH 7.4 binds at per-agent-runtime fan-out.
40
41	## De-risk spikes (do early — these can invalidate the architecture)
42
43	1. **Broker/brain no-terminate handoff** — prove a PTY child + open socket survive a daemon-*logic* restart (FD-passing or stable-broker shim). The hardest invariant in the design (ADR-0004).
44	2. **Iroh + mDNS smoke test** — two nodes, separate NATs, one message end-to-end; measure direct-vs-relay + binary size (research brief §5). Validates the whole networking premise.
45	3. **ConPTY + `portable-pty`** — host and drive a real session in a broker PTY on Windows + Linux.
46	4. **Manifest-driven lifecycle** — a manifest drives one real Claude Code session through spawn → bind → commune → signoff, proving harness-independence works in practice.
47
48	## Immediate next steps
49
50	Progress (2026-05-31):
51	- ✅ **Stage A — codex adversarial pass** — done. 14 findings (5 FATAL, 9 SERIOUS) in `docs/reviews/STAGE-A-codex-redteam.md`.
52	- ✅ **Spike #1 (broker/brain handoff)** — PASS on Windows ConPTY. `docs/spikes/SPIKE-01-broker-handoff.md`. Found+fixed the ConPTY DSR-stall hazard. 4 open gaps carried to M3.
53	- ✅ **Spike #2 (Iroh smoke test)** — PASS (single-host). `docs/spikes/SPIKE-02-iroh-smoke.md`. iroh 0.98 API workable; 13.57 MB release. Cross-NAT/relay proof deferred to M4.
54	- ✅ **Findings folded** into ADR-0002/0003/0004/0005 + KNOWN-HAZARDS (+3) + traceable-reqs.toml (+4 REQ-HAZARD).
55
56	Remaining before M0:
57	1. **Stage A — `plan-eng-review`** (interactive architecture pass; second half of Stage A).
58	2. **Spike #4 (manifest-driven lifecycle)** — a manifest drives one real session spawn→bind→commune→signoff. (Spike #3 ConPTY hosting is largely covered by Spike #1.)
59	3. Resolve the open user decisions logged in the ADR amendments (context-merge model, seed-rotation, R-UPD-3 rewording).
60	4. **M0** (workspace + `spt-proto` + `spt-store`) — **started**; task breakdown in [`M0-PLAN.md`](./M0-PLAN.md). Each milestone gets its own just-in-time plan (this one is M0); the roadmap stays the milestone sequence, the JIT plan is the task layer.
61
62	## Tooling: requirement traceability (adopted)
63
64	`traceable-reqs` is adopted as the requirement-tracing gate. `traceable-reqs.toml` seeds the `REQ-*` registry from PRD `R-*` + KNOWN-HAZARDS invariants (all inactive until milestone activation). The development contract + enforcement (CI gate, milestone activation, agent rule, quality audit) is `docs/TRACEABILITY.md`; the agent rule lives in `CLAUDE.md`. **M0 task:** install the CLI, validate the seed, wire the CI workflow, activate M0 reqs.
65
66	## Open threads carried forward
67
68	- New deferred items logged this session (`docs/DEFERRED.md`): block-level OCC context merge; the rebalance / Memory-Health pass; the consent framework's gated capabilities.
69	- The dual-audience docs strategy (`docs/DOCS-STRATEGY.md`) governs the *shipped* docs, authored as code lands.
70

# spt-core Roadmap

> Lightweight-but-structured path from design → fruition. Not a GSD/phase-gated process — a clear sequence with de-risk gates. Planning corpus is complete (PRD + CONTEXT + 5 ADRs + 6 design docs); this is how we build it.

## End goal

A harness-independent core for an agent ecosystem — messaging, live-agent lifecycle, terminal hosting, **first-class P2P networking**, and a runtime-manifest harness contract — shipped as Rust crates + a single `spt` binary. The proof: the same agent runs natively across all of a user's machines with a synced mind, reachable and operable from anywhere, with seamless self-update and zero-config cross-machine messaging. First thing built atop it: a rebuilt `spt` plugin at parity with today's modern SPT.

## Scope decisions (locked)

- **v1 = the full PRD, networking included.** The networked element is the *reason* spt-core exists; it is not deferrable to v1.1.
- **The hard freeze on `claude_skill_owl` is not a clock.** It works today and delivers value as-is while spt-core is built — no schedule pressure from it.
- **Process: lightweight but structured.** GSD is too heavy. This roadmap + the design docs are the structure.

## Path to fruition (sequence)

**Stage A — Red-team review (before building).** Pressure-test the design adversarially. Focus on the riskiest architectural decisions, not bikeshedding. Tools: `plan-eng-review` (architecture) + `codex` (adversarial second opinion). Scope-cutting is *not* a goal (v1 is locked as full PRD), but surfacing contradictions / over-engineering / unproven assumptions is.

**Stage B — De-risk spikes.** Throwaway proofs of the load-bearing unknowns — anything that, if it fails, forces a redesign. Do these before committing to the full build. See *De-risk spikes* below.

**Stage C — Walking skeleton.** Build the killer-quickstart slice end-to-end through the real crate boundaries: **two agents exchange a message** (first local; ultimately cross-node — the real proof). Validates the whole stack early; then thicken.

**Stage D — Phased build (M0–M5).** Dependency-ordered, each milestone ships something usable.

## Milestones (all v1)

- **M0 — Workspace + commodity layer.** Cargo workspace; `spt-proto` (wire envelope grammar, endpoint types, framing, Ed25519 identity, typed+binary payloads) + `spt-store` (spool, perch layout, trust store, registry persistence). Copy-verbatim the stable formats from the sister project (ADR-0001).
- **M1 — Local messaging + binary.** `spt-msg` (delivery TCP+spool, routing, send/ring/ready) + the `spt` binary + CLI. **= the killer quickstart (local): two agents exchange a message.** Task breakdown: [`M1-PLAN.md`](./M1-PLAN.md).
- **M2 — Harness contract + lifecycle.** **Strictly harness-agnostic — zero Claude Code conventions in this repo.** spt-core defines the adapter contract; the only adapter-shaped artifact in-tree is a generic mock/test adapter (manifest fixture + fake `api` caller) that exercises the contract (PRD R-DOCS-2 dev-agent quickstart). **The rebuilt `spt` plugin (Claude Code) is a SEPARATE downstream project** built atop these crates/binary — spt-core v1's acceptance proof (PRD §"feature parity"), not an spt-core deliverable. It lives and builds outside this repo. **Split into two JIT milestones** (decision 2026-06-01):
  - **M2a — Harness contract. ✅ delivered (2026-06-01).** `spt-runtime` (AgentRuntime, ManifestRuntime, manifest schema) + the `api` subcommand surface (seed/listen/bind/state/echo-gate/poll/worker-*/boundary/session-end/presence/history-log/emit/capability) + the **harness-hosted** startup topology (seed→listen→bind, interim seed-file) + local-api-auth + the generic mock adapter + contract E2E. All M2a reqs activated + green. Task breakdown: [`M2a-PLAN.md`](./M2a-PLAN.md).
  - **M2b — Live-agent lifecycle. ✅ delivered (2026-06-01).** `spt-live` (fifth layer): history subsystem (fetcher/locate-normalize/native) + spawn-psyche (nested perch, spt-core-owned `$psyche_prompt`) + echo-commune (history brief → single-writer commune drop) + drop-file ingest (single-writer 6.4 + direct-write precedence 6.5) + interim pulse + signoff/boundary ordering invariants (echo-before-signoff 3.3, grace-before-signoff 1.1, stale-signoff sweep 3.2) + resume seam (continue-existing / fresh-with-preload). Hosted by the `api listen` runner + `api shutdown`; mock adapter gained the lifecycle templates; LiveAgent E2E proves spawn→Psyche-perch→commune-ingest→graceful-signoff harness-agnostically. All M2b reqs activated + green. Task breakdown: [`M2b-PLAN.md`](./M2b-PLAN.md).
  - **Interim, no daemon** (like M1): the `api listen` process holds the perch + relays; the seed bridges via a file. **The spt-hosted topology (PTY-launch) + the consolidated `spt-daemon` move to M3** (they need the `spt-term` broker) — this narrows the earlier "both topologies in M2" wording.
- **M3 — Terminal wrapper + daemon + self-update.** `spt-term` (session-surface, PTY, broker) + the consolidated `spt-daemon` (broker/brain split) + peer-propagated signed self-update. Split M3a/M3b/M3c. Scope + carried-forward Spike #1 gaps: [`M3-PLAN.md`](./M3-PLAN.md).
  - **M3a — `spt-term` session-surface mechanism. ✅ delivered (2026-06-02).** The sixth crate (`…→spt-msg→spt-term`, sibling of spt-runtime): the `SessionSurface` trait + native PTY backends (ConPTY/forkpty via `portable-pty`, REQ-TERM-1) · ConPTY-DSR auto-answer drain pump (REQ-HAZARD-CONPTY-DSR) · send-keys/send-line injection (REQ-TERM-2) · bounded byte-stream (REQ-TERM-3) · the PTY-digest parser primitive (REQ-TERM-4 impl/unit, ADR-0008). OS-neutral per Spike #4/#5 (no OS's stream contract baked in; ANSI-stripped, tail-wins repaint tolerance). Validated on Windows (ConPTY) + Linux (forkpty). All M3a reqs activated + green. Task breakdown: [`M3a-PLAN.md`](./M3a-PLAN.md).
  - **M3b — `spt-daemon` broker/brain process. ✅ delivered (2026-06-03).** The seventh crate (`…→spt-live→spt-daemon→spt`) hosts many `spt-term` surfaces behind a **versioned local IPC** (named-pipe/UDS behind one `DaemonTransport` trait, forward-compatible frame schema): a **broker** (stable kernel — PTY masters, harness children, sockets; no logic) + a **brain** (restartable logic) so a brain kill/restart leaves the hosted PTY child + its output stream **intact and gapless** (Spike #1 made real; `gen_start = now()` each handoff). Consolidates the interim no-daemon model — the `api listen` pulse/Psyche/drop-ingest loops fold into the brain as config-paced in-process loops (only a thin stateless relay residue) · daemon-authoritative liveness (`info.json` `status` supersedes `is_process_alive` for hosted perches) · exactly-once broker↔brain boundary (broker-owned `EffectJournal` + dedup-at-effect, Spike #6) · auto-start + in-memory seed (the seed file is dead) · supervised-crash orphan-watch (carries the M2b graceful ordering) · the **digest daemon-half** (manifest `pty_digest` seam → `DigestParser` over the broker PTY, `spt digest <id>` pull + delta-stream + opt-in Path-B persistence → REQ-TERM-4 `int`). All M3b reqs activated + green; daemon E2E (spawn→Psyche-loop→commune→brain-restart-survives→graceful-signoff) on the CI matrix (ubuntu + windows). Task breakdown: [`M3b-PLAN.md`](./M3b-PLAN.md).
  - **M3c — signed self-update. ✅ delivered (2026-06-03).** The gated, signed, ripple-capable self-update engine (no new crate — it lives in `spt-daemon`, signing primitives in `release.rs`): the **update-class taxonomy** (brain-only / broker-compatible / broker-breaking, ADR-0004 §A) with the **brain-only zero-interruption swap** that drives the M3b handoff substrate (snapshot→drop→re-attach) so a live PTY child + its output stream survive the logic swap **untouched** (REQ-UPD-3, proven E2E `tests/brain_swap.rs`) · **verify-before-handoff** (Ed25519 over signed release metadata + SHA-256 artifact binding; `plan_verified` is the front door — no unverified binary reaches a handoff, REQ-UPD-2) · **rollback hardening** (monotonic version, metadata expiry, channel pinning, key revocation — REQ-HAZARD-UPDATE-ROLLBACK) · **consent gating** (default-gated, delivered to the most-recently-active live session via the new `info.json` `last_active_ms` recency stamp; opt-in full-auto — REQ-UPD-4) · **adapter ripple-update** (self-then-adapters via each manifest `[update]` avenue, with adapter content signing against the adapter's own key for `file_pull` / a `self_verifies` attest for opaque `delegated` updaters — REQ-UPD-5, REQ-SEAM-UPDATE). **Peer-propagation transport (REQ-UPD-1) defers to M4** — the engine ships with self-fetch/out-of-band delivery into the same seam. All M3c reqs activated + green. Task breakdown: [`M3c-PLAN.md`](./M3c-PLAN.md).
  - **M3 COMPLETE** (M3a + M3b + M3c all ✅, 2026-06-03): the real ADR-0004 architecture is live — native PTY hosting, the consolidated broker/brain daemon, and seamless signed self-update. Next milestone: M4.
- **M4 — Networking + instances. ✅ delivered (2026-06-04).** The eighth crate `spt-net` (Iroh WAN endpoint bound to the node's own Ed25519 identity, mDNS LAN discovery, TOTP-SPAKE2 pairing over a dedicated pre-trust ALPN with transcript binding / rate limiting / seed rotation+transfer, subnet registry + NDJSON replication under the per-node epoch lease) · the multi-instance model (registry rows + bare-id resolution refuse-and-qualify + visibility/sync-membership gates + rename/collision + **resting-state machine** dormant/suspended with measured warm/cold policy [`docs/DORMANCY-BUDGET.md`, red-team #9 closed] + **resource advertisement** REQ-INST-14 + **immutable home subnet & `spt fork`** REQ-INST-15, ADR-0010) · **cross-node Psyche sync** (git-native bundles over broker QUIC streams, two-tier scope gates, vector-precedence merge with conflicts surfaced + Psyche-reconciled, ADR-0013; **the gh-repo interim is retired**) · WAN messaging + remote-drive attach + chunked file transfer + per-endpoint access whitelists · the subnet **notification primitive** (replicated spool, dismiss-replication, boundary resurfacing; update consent + escalations refactored as producers) · the **update peer-propagation transport** into the M3c engine seam (offer-then-fetch, verify-before-stage per node, consent-notified — REQ-UPD-1) · production trigger loops (peer pump + inbound dispatcher — the subnet self-drives). **The real proof ran on the real rig: `[twohost]` CI run `26958175812` — pair → register → cross-node message → remote-drive (A typed into B's live PTY and read the echo) → Psyche sync both directions → update self-heal → notif dismiss-replication, HFENDULEAM (Windows) ↔ gravity-linux, whole ladder ~8 s** (`docs/TWO-HOST-RUNBOOK.md` + evidence appendix; `tests/twohost.rs` carries the nine activated `int` stages). Task breakdowns: [`M4-PLAN.md`](./M4-PLAN.md) + per-task JIT plans (D1–D9).
- **M5 — Shells, presence, deferred capabilities** (NEXT). Shell adapters + sleep/wake, PresenceChannel, and the consent-gated capabilities (remote-exec, instantiate-anywhere) landing into the v1 consent seam. Carried-in seams from M4: REQ-INST-6 (deferred-message resting gate), remote `spt suspend id@node` arm, remote fork arm, relay rendezvous routing (D2f Q1), notif-ack → `apply_brain_only` apply orchestration, cross-runner CI two-host job, KH 7.4 binds at per-agent-runtime fan-out.

## De-risk spikes (do early — these can invalidate the architecture)

1. **Broker/brain no-terminate handoff** — prove a PTY child + open socket survive a daemon-*logic* restart (FD-passing or stable-broker shim). The hardest invariant in the design (ADR-0004).
2. **Iroh + mDNS smoke test** — two nodes, separate NATs, one message end-to-end; measure direct-vs-relay + binary size (research brief §5). Validates the whole networking premise.
3. **ConPTY + `portable-pty`** — host and drive a real session in a broker PTY on Windows + Linux.
4. **Manifest-driven lifecycle** — a manifest drives one real Claude Code session through spawn → bind → commune → signoff, proving harness-independence works in practice.

## Immediate next steps

Progress (2026-05-31):
- ✅ **Stage A — codex adversarial pass** — done. 14 findings (5 FATAL, 9 SERIOUS) in `docs/reviews/STAGE-A-codex-redteam.md`.
- ✅ **Spike #1 (broker/brain handoff)** — PASS on Windows ConPTY. `docs/spikes/SPIKE-01-broker-handoff.md`. Found+fixed the ConPTY DSR-stall hazard. 4 open gaps carried to M3.
- ✅ **Spike #2 (Iroh smoke test)** — PASS (single-host). `docs/spikes/SPIKE-02-iroh-smoke.md`. iroh 0.98 API workable; 13.57 MB release. Cross-NAT/relay proof deferred to M4.
- ✅ **Findings folded** into ADR-0002/0003/0004/0005 + KNOWN-HAZARDS (+3) + traceable-reqs.toml (+4 REQ-HAZARD).

Remaining before M0:
1. **Stage A — `plan-eng-review`** (interactive architecture pass; second half of Stage A).
2. **Spike #4 (manifest-driven lifecycle)** — a manifest drives one real session spawn→bind→commune→signoff. (Spike #3 ConPTY hosting is largely covered by Spike #1.)
3. Resolve the open user decisions logged in the ADR amendments (context-merge model, seed-rotation, R-UPD-3 rewording).
4. **M0** (workspace + `spt-proto` + `spt-store`) — **started**; task breakdown in [`M0-PLAN.md`](./M0-PLAN.md). Each milestone gets its own just-in-time plan (this one is M0); the roadmap stays the milestone sequence, the JIT plan is the task layer.

## Tooling: requirement traceability (adopted)

`traceable-reqs` is adopted as the requirement-tracing gate. `traceable-reqs.toml` seeds the `REQ-*` registry from PRD `R-*` + KNOWN-HAZARDS invariants (all inactive until milestone activation). The development contract + enforcement (CI gate, milestone activation, agent rule, quality audit) is `docs/TRACEABILITY.md`; the agent rule lives in `CLAUDE.md`. **M0 task:** install the CLI, validate the seed, wire the CI workflow, activate M0 reqs.

## Open threads carried forward

- New deferred items logged this session (`docs/DEFERRED.md`): block-level OCC context merge; the rebalance / Memory-Health pass; the consent framework's gated capabilities.
- The dual-audience docs strategy (`docs/DOCS-STRATEGY.md`) governs the *shipped* docs, authored as code lands.


12:[Omitted long matching line]
34:- **Concrete Shells / PresenceChannel implementation / presence gossip** — only the seams ship (§Endpoints).
35:- **Remote command execution on another user's node** and **instantiate-anywhere** — seam-compatible, deferred behind a consent/security gate.
74:- **R-SEAM-history** Two history paths: **A** adapter-owned locate-template + adapter-owned **normalize-command**; **B** spt-core-native `history-log` store (also used for Shells). Adapter-owned normalize is mandated over built-in parsers (Codex-vs-Claude evidence — transcript formats diverge and move fast).
78:- **R-SEAM-capability** Static manifest list of hostable endpoint types (consumed by the registry; a node may host only Shells).
83:- **R-API-2** Surface: `api bind`, `api listen` (long-running relay), `api poll [--include-deferred]`, `api state <busy|idle>` (+ `api echo-gate <set|clear>`, `--no-gate`), `api worker-start|worker-stop|worker-poll`, `api boundary <clear|compact>` (carries new `session_id`), `api session-end [--erase]`, `api history-log`, `api presence`, `api seed --pid <pid>`.
94:## 9. Endpoints, Shells & the Instance model
97:- **R-EP-1** Day-one types: **ReadyAgent, LiveAgent, Psyche, Worker, SptNode** (agent/infra), plus the **Shell** family and **PresenceChannel** (seams only — impl deferred). The type system is **open**.
98:- **R-EP-2** Endpoints split into **agent endpoints** (host an agent) and **Shells** (driven surfaces — nothing intelligent runs there; a remote agent drives them). Shell-vs-agent distinction is present in the type model day-one.
99:- **R-EP-3** Messaging payloads carry **typed operation commands + arbitrary file blobs** (text/audio/image/video), not just text — the seam that lets Shells, file transfer, and rich PresenceChannel exist later.
100:- **R-EP-4** PresenceChannel (deferred impl) is a **broker** endpoint with dispatch/bind/thread styles; presence datum `(last_active_node, last_active_endpoint, ts)`.
101:[Omitted long matching line]
111:- **R-INST-8** **Remote-control** (Shell-like attach to an instance on another node; byte-stream terminal over Iroh) is a distinct mode from local operation. Handoff remote→local: `git pull` (files) + fresh-with-preload resume (mind); remote instance goes dormant, no teardown.
141:[Omitted long matching line]
148:[Omitted long matching line]
167:[Omitted long matching line]
177:[Omitted long matching line]
188:- **R-DOCS-3** **Diátaxis** structure (tutorial / how-to / reference / explanation) per capability vertical; **one canonical way to do X**.
196:[Omitted long matching line]
198:**Deferred (seam-compatible):** see [`docs/DEFERRED.md`](./docs/DEFERRED.md) — concrete Shells, PresenceChannel impl, presence gossip, instantiate-anywhere + consent, remote command exec + security gate, sidecar adapters, manifest includes, scrollback spillover, predictive/state-sync terminal layer, OS-level presence signal, macOS.
212:- ~~Manifest schema concretes~~ — **resolved**, see [`docs/MANIFEST.md`](./docs/MANIFEST.md) (opaque command templates; per-hook `can_inject`; node-wide cross-adapter fallback; global+override config knobs; worked `claude-spt` example). Capability-declaration shape beyond `hostable_types` remains open.
214:[Omitted long matching line]
215:[Omitted long matching line]
216:- ~~Remote-control: formal Shell or bare session-surface?~~ — **resolved**: opposite directions. A **Shell is agent→surface** (agent drives embodiment, receives sensory); **remote-control is user→agent** (a viewport onto a running instance). Remote-control is the bare network session-surface, *not* a Shell. (Shell model now fully specified — CONTEXT §Shell model, `docs/MANIFEST.md` `kind="shell"`, `docs/STORAGE.md`.)


156:id = "REQ-EP-5"
162:id = "REQ-INST-1"
182:id = "REQ-INST-6"
368:id = "REQ-INSTALL-4"


<owl_messages>
<owl_message from="doyle-psyche" priority="normal"><EVENT type="echo_commune" from="doyle-psyche" timestamp="2026-06-04T07:50:43-07:00" machine="HFENDULEAM" project="spt-core" branch="dev-freeform" head_sha="becb6ecb2a5458879c3c4f77139cd1ec31e39a36" head_subject="docs(plan,reqs): M4-D9-7 closeout sweep — M4 DELIVERED" note="Echo commune brief — auto-fired">&lt;live-context&gt;
Generation 16. **M4 FULLY DELIVERED 2026-06-04** — becb6ec CI FINAL: success. All D9 complete. ROADMAP M4 ✅.

## M4 Delivered Summary
- D0–D9 all CI-green. Two-host rig proof REAL (CI 26958175812, full §D9b ladder ~8s over tailscale, first attempt green).
- 26 int-staged reqs. gh-repo interim retired. FLAKE-LEDGER.md on disk.
- M4-PLAN + M4-D9-PLAN plan-status COMPLETE.

## Next: M5 — Shells, Presence, Deferred Capabilities
- Author M5-PLAN.md JIT at start (same pattern as M4). Surface design forks early — don't guess (M4 precedent: grill sessions before D-tasks).
- Carried-in seams (ROADMAP §M5): REQ-INST-6 (deferred-message resting gate), remote spt suspend id@node, remote fork arm, relay rendezvous (D2f Q1), notif-ack→apply_brain_only, cross-runner CI two-host job, KH 7.4 per-agent-runtime fan-out.
- Rule-5 activation candidates: REQ-EP-5, REQ-INST-1, REQ-INSTALL-4, REQ-HAZARD-STDIN-SESSION-ID, REQ-HAZARD-STALE-INDEX-LOCK.

## Rig Facts (durable)
- Tailscale: HFENDULEAM 100.68.35.65 ↔ gravity-linux 100.100.62.43 (~23ms).
- No SSH to gravity (key auth denied) — [twohost] CI gate IS the gravity drive path.

## Gotchas (carry — all 17)
1. Journaled net_stream_send (Some(op_id)) blocks ack + DISCARDS interleaved events. Pump loops use None.
2. Echo children repeat typed input; compare against local OutputLog replay.
3. CI Linux clippy rustc 1.96 deny-defaults newer than local.
4. &quot;Stop once buffer contains needle&quot; overshoots; compare prefix-through-first-needle.
5. Brain::snapshot()/handoff() panics without PTY session — net-only restart tests use fresh cold_start.
6. QUIC bidi stream invisible until first bytes flow — send before wait_for_stream.
7. `gh run watch` exit code LIES — verify FINAL via `gh run list --json conclusion`.
8. Manifest test commands in TOML '…' literals — nested single quotes break parse; Windows masks it.
9. Broker stream table holds LOCALLY-OPENED outbound streams too — snapshot before requester opens.
10. Scripted `&lt;EVENT&gt;` envelopes: raw `\n` splits line — use `&lt;br&gt;` per 4.1 grammar.
11. traceable-reqs scan.roots excludes repo-root *.md — doc tags must land under docs/.
12. Windows PowerShell 5.1 CI steps are ANSI — ASCII-only strings only.
13. `gh run list --limit 1` races fresh push — pin watcher loops to pushed sha.
14. Test brain ops on broker shared with pump MUST use high op-id offset (1e9+); collision = &quot;already applied&quot; wedge.
15. tokio in spt-daemon lacks &quot;time&quot; feature — async test code uses std::thread::sleep.
16. iroh bind_addr rejects String — use SocketAddr/(&amp;str,u16).
17. gh run logs unavailable until job completes — derive everything from env, no mid-run log exchange.

## Flake Ledger (also docs/FLAKE-LEDGER.md)
1. brain_restart_survives_gaplessly — hung once hosted Windows; timeout bounds it.
2. spt-term::digest_parses_a_real_pty_stream — 2 occurrences gravity (sprint-collapse assert digest.rs:86 under load); next = instrument + harden.
3. sync.rs torn_pull + concurrent_writes — HARDENED: wait_for_stream_except 2s→10s.

## CI Topology (stable)
- hfenduleam (Windows): C:\actions-runner detached (NOT service — re-launch: `Start-Process C:\actions-runner\run.cmd`). Queue can sit minutes — poll, don't panic.
- gravity (Linux): systemd service, auto-start. [twohost] CI gate = only gravity drive path.

## Workflow Constraints (carried)
- NO cargo fmt
- Tag [impl/unit-&gt;REQ-…] on real evidence only
- traceable-reqs check from REPO ROOT (exit 0 before push; scan roots = src/tests/docs/crates)
- Linux CI clippy -D warnings is real gate
- Verify FINAL conclusion via `gh run list --json conclusion` pinned to pushed sha (gotchas #7+#13)
- Push each slice → watch green before next
- Commit trailer: `Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;`

## No Blockers
&lt;/live-context&gt;
&lt;project-context&gt;
name=spt-core

M4 DELIVERED + CLOSED 2026-06-04 (becb6ec CI-green-FINAL): networking + instances complete — spt-net (iroh WAN/mDNS/SPAKE2 pairing/registry), multi-instance model (resting states + budget, resources, home+fork), cross-node Psyche sync (gh-interim retired), WAN msg/remote-drive/xfer, notif primitive, update peer-propagation, production trigger loops. Two-host rig proof REAL: CI 26958175812, full §D9b ladder HFENDULEAM↔gravity-linux ~8s (tests/twohost.rs, docs/TWO-HOST-RUNBOOK.md + evidence). Nine int stages live on rig evidence. Next: M5 (shells, presence, deferred capabilities) — M5-PLAN.md to author at start; carried seams in ROADMAP §M5. Conventions hold: no cargo fmt; evidence-only tags; traceable-reqs check repo root; clippy -D warnings; push slice → watch FINAL sha-pinned; commit trailer Claude Opus 4.8 (1M context).
&lt;/project-context&gt;</EVENT></owl_message>
</owl_messages>


94	## 9. Endpoints, Shells & the Instance model
95
96	### 9.1 Endpoint types
97	- **R-EP-1** Day-one types: **ReadyAgent, LiveAgent, Psyche, Worker, SptNode** (agent/infra), plus the **Shell** family and **PresenceChannel** (seams only — impl deferred). The type system is **open**.
98	- **R-EP-2** Endpoints split into **agent endpoints** (host an agent) and **Shells** (driven surfaces — nothing intelligent runs there; a remote agent drives them). Shell-vs-agent distinction is present in the type model day-one.
99	- **R-EP-3** Messaging payloads carry **typed operation commands + arbitrary file blobs** (text/audio/image/video), not just text — the seam that lets Shells, file transfer, and rich PresenceChannel exist later.
100	- **R-EP-4** PresenceChannel (deferred impl) is a **broker** endpoint with dispatch/bind/thread styles; presence datum `(last_active_node, last_active_endpoint, ts)`.
101	- **R-EP-5** (deferred impl, CONTEXT "Shell model") **Concrete shell instantiation model:** `shell spawn` *mints* an owner-exclusive instance (`<adapter>-<n>`, distinct from the `relink`/`persistent`/`wake` online switch); permission to instantiate = shell adapter **registered on the node** (broadcast governs *discovery* only); a per-shell **`require_approval`** gate (`none`|`remembered`|`always`, default none; manifest floor, node/endpoint may tighten) reuses the consent grant store; an optional **`max_instances_per_owner`** cap (online+offline both count) with **`over_cap`** (`reject`|`approve`); instances are **aliasable** (immutable canonical id + optional perch-level alias; `adapter_name` always legible). Discovery = own instances + instantiable adapters (same-node non-`none` broadcast; other-node `subnet` broadcast, spawn gated by instantiate-anywhere).
102
103	### 9.2 Instances (ADR-0003, as amended)
104	- **R-INST-1** Split identity: subnet-wide **endpoint ID** vs per-node **instance**. An instance is the *same* endpoint running **natively** on a machine.
105	- **R-INST-2** Per-node (anchored): files/project + harness session history. **Synced across instances:** the Psyche mind (two-tier — below).
106	- **R-INST-3** **Dormant (warm)** is the default resting state (session running, instant wake); **suspended (cold)** is opt-in (session closed, resume-on-wake) and can be triggered from any node. Registry status: active / dormant / suspended / offline.
107	- **R-INST-4** Active → (dormant|suspended) fires a **transition echo commune** that syncs to the next active instance (catch-up-on-activation).
108	- **R-INST-5** **Two-tier context sync:** live context → all instances; project context → same-project instances only. (Depends on refining Psyche authoring directives.)
109	- **R-INST-6** Deferred messages do **not** deliver to dormant/suspended instances until active.
110	- **R-INST-7** **Subnet registry** (eventually-consistent `endpoint_id → [instances]`) + **resolution policy** (local → most-recently-active → `id@node`) are mandatory v1 foundations.
111	- **R-INST-8** **Remote-control** (Shell-like attach to an instance on another node; byte-stream terminal over Iroh) is a distinct mode from local operation. Handoff remote→local: `git pull` (files) + fresh-with-preload resume (mind); remote instance goes dormant, no teardown.
112	- **R-INST-9** (M4, ADR-0006) **Multi-subnet membership:** a node may belong to **multiple subnets at once** — v1: multiple subnets of the *same user* (each its own TOTP seed/trust-context); cross-*user* membership stays deferred but is a **seam** (seed key generalizes to per-(subnet, user)). Identity is **node-global, advertised per-subnet**; the registry is **per-subnet**.
113	- **R-INST-10** (M4, ADR-0006) **Qualified addressing:** canonical target form `[subnet:]id[@node]` (subnet- and node-qualifiers, combinable). A bare id that is **ambiguous across visible subnets refuses resolution and forces qualification** rather than guessing; within a subnet, bare ids stay unique via a **join-time collision check**.
114	- **R-INST-11** (M4, ADR-0006) **`spt rename <id> <new_id>` (rippled):** change an endpoint's logical ID and propagate to **all instances** (registry, every perch, the `a-<id>` context branch + project refs), collision-checked against every target subnet and reconciled by the 6.5 precedence marker.
115	- **R-INST-12** (M4, ADR-0006) **Endpoint visibility (per-(endpoint, subnet)):** hidden means **excluded** — not advertised *and not routable* from that subnet (a real boundary), not merely unlisted. Hidden iff per-subnet `hide_new_endpoints` (captured at join time) **OR** per-endpoint `default_hide_from_new_subnets`, unless an explicit per-(E,S) override wins; both defaults OFF. **Visibility gates sync.**
116	- **R-INST-13** (M4, ADR-0006) **Subnet-exclusive sync:** an endpoint's mind replicates only within its home subnet by default; endpoint config carries a **subnet-membership list** for sync, replicating across every listed subnet's nodes. Distinct from visibility (addressable ≠ mind-replicated); composes with the R-INST-5 live/project tiers.
117	- **R-INST-14** (M4) **Resource advertisement (subnet resource registry):** a per-endpoint **free-text** blurb of the services/functions it can serve (an agent yellow-pages), **both-authored** (config-seeded + agent-refined via `spt resources set`) and mutable. Not a separate registry — a field on the endpoint record + a **projection** of the subnet registry (`(id, node, blurb)`), **gated by visibility (and, later, the access whitelist)**. Synced as registry data, not context.
118	- **R-INST-15** (M4, ADR-0010) **Immutable home subnet + `spt fork`:** an endpoint's **home subnet** (sync default + bare-name anchor) is assigned at creation (sole subnet auto; multi-subnet must specify; unpaired ⇒ local until first join) and is **immutable** — there is **no re-home**. To place an agent's mind in another subnet, **`spt fork`** clones it into a **new, identity-distinct endpoint** there, seeded with a one-time copy of the mind, then the two **diverge** (a fork is *not* an instance; the source is untouched, optionally `--delete-source`). Also: a new endpoint's **adapter** is chosen from registered `kind="harness"` adapters matching its type (auto-if-one/ask-if-many); changing adapter is done only by **launch/resume under a different registered adapter** (no standalone rebind).
119
120	### 9.3 Off-node reach-back
121	- **R-REACH-1** (v1) An agent can detect it is driven remotely (and from which node) and **transfer files** to/from the driving node.
122	- **R-REACH-2** (deferred) Remote command execution on the user's node — security-gated; workarounds exist via local agents.
123
124	---
125
126	## 10. Networking & pairing
127
128	- **R-NET-1** WAN messaging is first-class day-one, baked into the core crates behind a default-on `net` feature flag (ADR-0002): Iroh + mDNS LAN + pairing.
129	- **R-NET-2** v1 defaults to **n0.computer relays** (free, accountless) for zero-config WAN, with a **self-hostable-relay** config knob and plain-language disclosure. Traffic is end-to-end encrypted; the dependency is metadata-only and escapable.
130	- **R-NET-3** **Cross-node Psyche sync** over P2P **replaces** the GitHub-repo sync (no `gh`/account/setup).
131	- **R-PAIR-1** **TOTP-seeded SPAKE2 pairing** (ADR-0005): the rotating TOTP code is the PAKE password (not a bearer token). The seed is the subnet secret, held by the auth app + all trusted nodes; the user interacts only with the new node.
132	- **R-PAIR-2** Local trust store (TOFU + warn-on-change). Cross-subnet deferred.
133	- **R-PAIR-3** Fetch the current pairing code from any paired node (`spt pair --code` or via an agent).
134	- **R-PAIR-4** Prompt to **name the subnet** on first pairing; show the name on every subsequent pairing.
135	- **R-PAIR-5** (M4, ADR-0005 amend) **Multi-subnet pairing ceremony:** discovery takes **TOTP code + subnet-name** (the name namespaces the relay rendezvous) in all cases; **create-new names the subnet at creation** (no node-name mode); the relay rendezvous token is `H(subnet-name ‖ TOTP-epoch)` (under the hood — raw name+code at the surface; payload already E2E-encrypted).
136	- **R-PAIR-6** (M4, ADR-0005 amend) **Elevation-gated per-subnet code fetch:** retrieving a subnet's current code *from a node* requires OS privilege elevation (Windows UAC / Linux root-or-equivalent) or an elevated agent endpoint; otherwise the user falls back to their authenticator-app store. Fetch is **per-subnet** (`--subnet <name>` / `--create-new`), QR/`otpauth://` optional.
137	- **R-PAIR-7** (M4 data / frontend render) **Subnet icon:** an optional small image (square PNG/WebP, ≤256 KB) representing the subnet in GUIs; stored **inline** in subnet metadata (syncs over the same subnet-material channel as the name), editable by any node in the subnet, GUI-only consumer.
138
139	### 10b. Endpoint access control (ADR-0009)
140
141	- **R-SEC-1** (M4 node-tier; user-tier deferred) **Per-endpoint access whitelist:** an optional allow-list gating **who may remotely reach** an endpoint, checked by **origin node** of the inbound interaction (not sender-endpoint identity — *"the operator must be on a whitelisted machine"*). **Stateful-firewall semantics:** outbound goes to any visible node; an inbound **reply** correlated to the endpoint's own prior outbound (`__REPLY_TO__`) is allowed from any visible node; an **unsolicited/direct** inbound is allowed only from a whitelisted node. **Same-node operation always allowed**; default empty = open. **Node-tier (Ed25519 pubkeys) ships M4; user-tier** ("nodes of whitelisted users") is deferred to the per-(subnet,user) model (ADR-0006 seam) — schema reserves an inert `users` field. Enforced at the target node, synced as security material near the trust store, subnet-settable, revocable. **Composes as the outer gate** before capability grants; distinct table + polarity (origin-node/default-open vs agent-subject/default-deny). Three nested gates: subnet *visibility* → *access whitelist* → *capability grants*.
142
143	---
144
145	## 10b. Notifications (ADR-0007)
146
147	- **R-NOTIF-1** (M4) **Notification primitive:** a first-class user-directed, **dismissable, resurfacing** subnet event, distinct from inter-agent messages. **Per-subnet, replicated spool** (reuses registry distribution); subnet-wide dismiss-state. **`seen` (per-endpoint) vs `dismissed` (explicit)**; resurface undismissed at existing boundaries (state→active, `api boundary clear`/`compact`, new-session-start), gated by seen-per-endpoint + a **cross-endpoint suppression timeout** (~1h); scoped to subnets the endpoint is visible in. **The self-update prompt (R-UPD-4) and consent escalation are refactored to be notif producers** (open producer set). Envelope carries `from` (endpoint+node+subnet; subnet surfaced only to multi-subnet receivers).
148	- **R-NOTIF-2** (M4) **`spt notify` + `notif_command` seam:** any agent may issue a subnet-wide notif (v1: reaches the user's active endpoint). Delivery default = notif-flavored message to the agent's perch (agent surfaces it); optional **`notif_command` manifest template** on **both harness- and shell-adapter manifests** for endpoint-native rendering — if presence resolves to a **Shell** attached to the endpoint, the notif renders via *that shell's* template (the v1 seam and M5 generalization in one). *(Forward: targeting all/specific subnet users once per-(subnet,user) lands.)*
149
150	---
151
152	## 11. Self-update
153
154	- **R-UPD-1** **Peer-propagated** over P2P (layered on self-fetch; out-of-band still supported); the subnet self-heals to latest (ADR-0004).
155	- **R-UPD-2** **All binaries signature-verified before handoff**, regardless of source (spt-core release key).
156	- **R-UPD-3** **No-endpoint-termination invariant, scoped by update class (ADR-0004 §A).** For the **brain-only** class — the routine case — the guarantee is **absolute**: no endpoint process terminates or suspends, satisfied by the broker/brain split (the broker keeps held PTYs/children/sockets alive across brain replacement). **broker-compatible** swaps the broker behind the versioned IPC with endpoints still surviving. **broker-breaking** (held-resource-type / OS-API change) MAY suspend endpoints, but only via a *planned endpoint-cycle* with consent + scheduling — the absolute guarantee is explicitly weakened here.
157	- **R-UPD-4** **Not auto by default** — gated on user confirmation delivered to the most-recently-active live session, with an opt-in full-auto.
158	- **R-UPD-5** spt-core conducts **adapter ripple-updates** after self-update, per each adapter's update declaration and `min_spt_core_version` gate.
159
160	---
161
162	## 12. Terminal wrapper & frontend
163
164	- **R-TERM-1** A process-supervisor terminal wrapper (supersedes "capsule") hosting sessions in broker PTYs, multiplexed and name-addressable, attach/detach, headless↔headed.
165	- **R-TERM-2** A **session-surface** abstraction with native PTY as the day-one impl and network-attached surfaces for remote control; input injection at both `send-keys` (raw) and `send-line` (cooked) granularity; in-memory scrollback ring (disk spillover deferred).
166	- **R-TERM-3** Remote terminal streaming uses a **byte-stream** model for v1 (simple, faithful; near-LAN on Iroh-direct paths). Keep the surface abstraction generic enough that a predictive/state-sync "feels-local" layer can be added later without re-architecting — not built v1.
167	- **R-TERM-4** (M3, ADR-0008) **Live activity buffer (PTY digest):** a rolling, *parsed*, structured (source-tagged) view of recent PTY I/O (last ~N user turns + agent output between; tool sprints collapsed), built by running **adapter-supplied** manifest patterns (`input_pattern`, `agent_pattern`, `tool_pattern[]` + catchall) over the **broker-owned** PTY bytes — honors the no-built-in-parser rule (adapter owns the pattern). **Spt-hosted-primary** (needs the broker PTY); **harness-hosted is capability-gated** (an adapter that can feed the stream opts in; CC cannot). Distinct from scrollback (R-TERM-2) and transcript history (Paths A/B). Two access modes — **`spt digest <[subnet:]id[@node]>` snapshot pull** + a **broker-pushed structured-delta stream** (subscribe; changes only) riding the R-TERM-3 substrate; **address-gated** like messaging. Optional opt-in **Path-B persistence** as a coarse activity log (the "option C" endpoint-logging surface).
168	- **R-FRONT-1** Day-one headed frontend is a **launcher/manager** (not raw attach): list running + historic endpoints; launch a historic endpoint from its original manifest; tap into a running one; init a new one via a known adapter. Realizes the "guided resume" (XMB-style) and "management GUI" (per-endpoint panes) sketches. Its **CLI sibling** is a no-arg **`spt resume`** guided-resume picker — endpoints grouped by locality (on-node/this-project → on-node/other-project → off-node), MRU within group; selecting a running instance attaches, a non-running one chains into the adapter selector → launch, a "+ new" entry into the creation flow.
169
170	---
171
172	## 13. Installation & migration
173
174	- **R-INSTALL-1** Two paths: standalone (primitive) and harness-bootstrapped (calls into standalone). One-line signed-binary install script that registers PATH + the daemon as an OS service (systemd-user / Windows service), with `api`-triggered auto-start fallback.
175	- **R-INSTALL-2** Marketplace-repackaging-friendly (PortMaster/handheld, Android) — relocatable binary, minimal non-OS-entangled logic.
176	- **R-INSTALL-3** First run idempotent + interactive-optional: generate node identity, start daemon; pairing + subnet-naming are separate explicit steps.
177	- **R-INSTALL-4** (CONTEXT "adapter registration") **Adapter registration lifecycle:** `spt adapter add <path>` (or `--github <user/repo>`, manifest-first then install via the declared `[update]` avenue — *install is the first update*) validates the manifest and records it under `{SPT_HOME}/…/adapters/` (copy for `file_pull`, pointer for `delegated`); one command/dir for `kind="harness"` and `kind="shell"`. Node-local (drive/launch capability, not subnet advertisement). `spt adapter remove` is **soft-deregister** (hidden from new-creation; existing/live instances keep running) + an optional manifest **`uninstall`** template run once quiesced (or `--force`). The registered set is what self-update ripples (R-UPD-5).
178	- **R-MIGRATE-1** Standalone install **auto-detects a `claude_skill_owl` install and offers migration** (identity, agents, tracked Psyche context). First-class supported path.
179
180	---
181
182	## 14. Project infrastructure
183	- **R-INFRA-1** Issue/feature tracking on **GitHub** for v1; **tangled.org** documented as the principled migration target.
184
185	### 14.1 Documentation (designed-in, dual-audience)
186	- **R-DOCS-1** Docs serve **both human devs and AI dev-agents** — authored once in clean markdown, served in two depths. See [`docs/DOCS-STRATEGY.md`](./docs/DOCS-STRATEGY.md).
187	- **R-DOCS-2** A **sub-10-minute, runnable, deterministic killer quickstart** for each audience (humans: two agents exchange a message; dev-agents: a minimal adapter satisfying the manifest + `api` contract).
188	- **R-DOCS-3** **Diátaxis** structure (tutorial / how-to / reference / explanation) per capability vertical; **one canonical way to do X**.
189	- **R-DOCS-4** **Agent-consumable layer:** CI-generated `llms.txt`/`llms-full.txt`, markdown content negotiation, a machine-readable manifest JSON Schema at a stable path, an **MCP doc/resource server** exposing the harness contract as deterministic resources, and structured CLI help (`spt <cmd> --help` + a `--json` mode).
190	- **R-DOCS-5** **Anti-drift via CI:** rustdoc, the manifest schema, the agent exports, and CLI help are generated + checked in CI — doc quality on the same footing as tests.
191
192	---
193
194	## 15. v1 scope summary
195
196	**In v1:** harness-independent core (library + binary); manifest + `api` harness contract; both startup topologies; one-daemon broker/brain; LiveAgent/ReadyAgent/Psyche/Worker/SptNode endpoints + Shell-vs-agent seam + typed/binary payloads; multi-instance data model + subnet registry + resolution policy + dormant/suspended + two-tier context sync + remote-drive of running instances + off-node file transfer; first-class WAN networking + TOTP-SPAKE2 pairing + subnet naming; cross-node Psyche sync (replaces gh-sync); peer-propagated gated signed self-update + adapter ripple; terminal wrapper + launcher frontend (byte-stream remote streaming); one-line install + OS-service + legacy auto-migration; rebuilt spt-plugin to parity.
197
198	**Deferred (seam-compatible):** see [`docs/DEFERRED.md`](./docs/DEFERRED.md) — concrete Shells, PresenceChannel impl, presence gossip, instantiate-anywhere + consent, remote command exec + security gate, sidecar adapters, manifest includes, scrollback spillover, predictive/state-sync terminal layer, OS-level presence signal, macOS.
199
200	## 16. Success criteria (v1)
201
202	1. A non-CC harness (or a headless spike) drives the full live-agent lifecycle through manifest + `api` alone — proving harness independence.
203	2. The rebuilt spt-plugin reaches **feature parity** with frozen `claude_skill_owl`.
204	3. Two of a user's machines, freshly paired via one TOTP code, exchange messages and the user drives an agent on machine A from machine B — zero config beyond pairing.
205	4. A self-update rolls across the subnet **without terminating any endpoint**.
206	5. The same agent's mind follows the user across machines (two-tier sync); switching machines leaves the prior instance dormant and catches up the new active one.
207	6. Every `KNOWN-HAZARDS` invariant has a corresponding passing test.
208
209	## 17. Open design-phase questions
210
211	- ~~Exact `$SPT_HOME` on-disk layout~~ — **resolved**, see [`docs/STORAGE.md`](./docs/STORAGE.md) (single relocatable root; adapter-agnostic ID keying; separate `tracked/` git repo vs node-local `perches/`; one path resolver).
212	- ~~Manifest schema concretes~~ — **resolved**, see [`docs/MANIFEST.md`](./docs/MANIFEST.md) (opaque command templates; per-hook `can_inject`; node-wide cross-adapter fallback; global+override config knobs; worked `claude-spt` example). Capability-declaration shape beyond `hostable_types` remains open.
213	- ~~Refining Psyche authoring directives so the live/project context split holds cleanly~~ — **resolved**, see [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) (per-topic discriminator + default-to-project; commune-source asymmetry; deep tier-tagged memformat with structured blocks; generated Retrieval Map; dense-working-set / sparse-tail injection; patterns from pi-agent-memory).
214	- ~~Warm-vs-cold dormancy default policy + suspend command surface~~ — **resolved**: active = most-recently-interacted + driver-attached (a linked Shell counts as a driver); no idle timer; auto-suspend opt-in (default OFF, node + endpoint override) counted from dormancy onset. Commands `spt suspend`/`wake`/`shutdown`/`refresh`. Boundary communes (`/clear`,`/compact`,`spt suspend`) now Self-authored (resume Self); `refresh`'s `/clear` uses the same rule. See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`.
215	- ~~Consent/security-gate UX~~ — **resolved**: within-one-subnet trust boundary (anti-surprise / anti-runaway / compromise-containment, not stranger-auth); hybrid model — grant store (`capability × agent × target-node`, target-enforced, subnet-settable, revocable) + interactive escalation (prompt to most-recently-active session: allow-once / allow-always / deny). Pre-consent flags are pre-authored grants. v1 ships the framework seam; gated capabilities (remote-exec, instantiate-anywhere) land with it later. See CONTEXT §Consent & security gates.
216	- ~~Remote-control: formal Shell or bare session-surface?~~ — **resolved**: opposite directions. A **Shell is agent→surface** (agent drives embodiment, receives sensory); **remote-control is user→agent** (a viewport onto a running instance). Remote-control is the bare network session-surface, *not* a Shell. (Shell model now fully specified — CONTEXT §Shell model, `docs/MANIFEST.md` `kind="shell"`, `docs/STORAGE.md`.)
217
218	## 18. References
219
220	- [`ROADMAP.md`](./ROADMAP.md) — path to fruition: review → de-risk spikes → walking skeleton → M0–M5.
221	- [`CONTEXT.md`](./CONTEXT.md) — glossary & model (authoritative for meaning).
222	- [`docs/adr/0001`](./docs/adr/0001-clean-room-fork-from-claude-skill-owl.md) … [`0005`](./docs/adr/0005-totp-seeded-spake2-node-pairing.md) — decisions.
223	- [`docs/KNOWN-HAZARDS.md`](./docs/KNOWN-HAZARDS.md) — invariants harvested from the sister project (test checklist).

## 9. Endpoints, Shells & the Instance model

### 9.1 Endpoint types
- **R-EP-1** Day-one types: **ReadyAgent, LiveAgent, Psyche, Worker, SptNode** (agent/infra), plus the **Shell** family and **PresenceChannel** (seams only — impl deferred). The type system is **open**.
- **R-EP-2** Endpoints split into **agent endpoints** (host an agent) and **Shells** (driven surfaces — nothing intelligent runs there; a remote agent drives them). Shell-vs-agent distinction is present in the type model day-one.
- **R-EP-3** Messaging payloads carry **typed operation commands + arbitrary file blobs** (text/audio/image/video), not just text — the seam that lets Shells, file transfer, and rich PresenceChannel exist later.
- **R-EP-4** PresenceChannel (deferred impl) is a **broker** endpoint with dispatch/bind/thread styles; presence datum `(last_active_node, last_active_endpoint, ts)`.
- **R-EP-5** (deferred impl, CONTEXT "Shell model") **Concrete shell instantiation model:** `shell spawn` *mints* an owner-exclusive instance (`<adapter>-<n>`, distinct from the `relink`/`persistent`/`wake` online switch); permission to instantiate = shell adapter **registered on the node** (broadcast governs *discovery* only); a per-shell **`require_approval`** gate (`none`|`remembered`|`always`, default none; manifest floor, node/endpoint may tighten) reuses the consent grant store; an optional **`max_instances_per_owner`** cap (online+offline both count) with **`over_cap`** (`reject`|`approve`); instances are **aliasable** (immutable canonical id + optional perch-level alias; `adapter_name` always legible). Discovery = own instances + instantiable adapters (same-node non-`none` broadcast; other-node `subnet` broadcast, spawn gated by instantiate-anywhere).

### 9.2 Instances (ADR-0003, as amended)
- **R-INST-1** Split identity: subnet-wide **endpoint ID** vs per-node **instance**. An instance is the *same* endpoint running **natively** on a machine.
- **R-INST-2** Per-node (anchored): files/project + harness session history. **Synced across instances:** the Psyche mind (two-tier — below).
- **R-INST-3** **Dormant (warm)** is the default resting state (session running, instant wake); **suspended (cold)** is opt-in (session closed, resume-on-wake) and can be triggered from any node. Registry status: active / dormant / suspended / offline.
- **R-INST-4** Active → (dormant|suspended) fires a **transition echo commune** that syncs to the next active instance (catch-up-on-activation).
- **R-INST-5** **Two-tier context sync:** live context → all instances; project context → same-project instances only. (Depends on refining Psyche authoring directives.)
- **R-INST-6** Deferred messages do **not** deliver to dormant/suspended instances until active.
- **R-INST-7** **Subnet registry** (eventually-consistent `endpoint_id → [instances]`) + **resolution policy** (local → most-recently-active → `id@node`) are mandatory v1 foundations.
- **R-INST-8** **Remote-control** (Shell-like attach to an instance on another node; byte-stream terminal over Iroh) is a distinct mode from local operation. Handoff remote→local: `git pull` (files) + fresh-with-preload resume (mind); remote instance goes dormant, no teardown.
- **R-INST-9** (M4, ADR-0006) **Multi-subnet membership:** a node may belong to **multiple subnets at once** — v1: multiple subnets of the *same user* (each its own TOTP seed/trust-context); cross-*user* membership stays deferred but is a **seam** (seed key generalizes to per-(subnet, user)). Identity is **node-global, advertised per-subnet**; the registry is **per-subnet**.
- **R-INST-10** (M4, ADR-0006) **Qualified addressing:** canonical target form `[subnet:]id[@node]` (subnet- and node-qualifiers, combinable). A bare id that is **ambiguous across visible subnets refuses resolution and forces qualification** rather than guessing; within a subnet, bare ids stay unique via a **join-time collision check**.
- **R-INST-11** (M4, ADR-0006) **`spt rename <id> <new_id>` (rippled):** change an endpoint's logical ID and propagate to **all instances** (registry, every perch, the `a-<id>` context branch + project refs), collision-checked against every target subnet and reconciled by the 6.5 precedence marker.
- **R-INST-12** (M4, ADR-0006) **Endpoint visibility (per-(endpoint, subnet)):** hidden means **excluded** — not advertised *and not routable* from that subnet (a real boundary), not merely unlisted. Hidden iff per-subnet `hide_new_endpoints` (captured at join time) **OR** per-endpoint `default_hide_from_new_subnets`, unless an explicit per-(E,S) override wins; both defaults OFF. **Visibility gates sync.**
- **R-INST-13** (M4, ADR-0006) **Subnet-exclusive sync:** an endpoint's mind replicates only within its home subnet by default; endpoint config carries a **subnet-membership list** for sync, replicating across every listed subnet's nodes. Distinct from visibility (addressable ≠ mind-replicated); composes with the R-INST-5 live/project tiers.
- **R-INST-14** (M4) **Resource advertisement (subnet resource registry):** a per-endpoint **free-text** blurb of the services/functions it can serve (an agent yellow-pages), **both-authored** (config-seeded + agent-refined via `spt resources set`) and mutable. Not a separate registry — a field on the endpoint record + a **projection** of the subnet registry (`(id, node, blurb)`), **gated by visibility (and, later, the access whitelist)**. Synced as registry data, not context.
- **R-INST-15** (M4, ADR-0010) **Immutable home subnet + `spt fork`:** an endpoint's **home subnet** (sync default + bare-name anchor) is assigned at creation (sole subnet auto; multi-subnet must specify; unpaired ⇒ local until first join) and is **immutable** — there is **no re-home**. To place an agent's mind in another subnet, **`spt fork`** clones it into a **new, identity-distinct endpoint** there, seeded with a one-time copy of the mind, then the two **diverge** (a fork is *not* an instance; the source is untouched, optionally `--delete-source`). Also: a new endpoint's **adapter** is chosen from registered `kind="harness"` adapters matching its type (auto-if-one/ask-if-many); changing adapter is done only by **launch/resume under a different registered adapter** (no standalone rebind).

### 9.3 Off-node reach-back
- **R-REACH-1** (v1) An agent can detect it is driven remotely (and from which node) and **transfer files** to/from the driving node.
- **R-REACH-2** (deferred) Remote command execution on the user's node — security-gated; workarounds exist via local agents.

---

## 10. Networking & pairing

- **R-NET-1** WAN messaging is first-class day-one, baked into the core crates behind a default-on `net` feature flag (ADR-0002): Iroh + mDNS LAN + pairing.
- **R-NET-2** v1 defaults to **n0.computer relays** (free, accountless) for zero-config WAN, with a **self-hostable-relay** config knob and plain-language disclosure. Traffic is end-to-end encrypted; the dependency is metadata-only and escapable.
- **R-NET-3** **Cross-node Psyche sync** over P2P **replaces** the GitHub-repo sync (no `gh`/account/setup).
- **R-PAIR-1** **TOTP-seeded SPAKE2 pairing** (ADR-0005): the rotating TOTP code is the PAKE password (not a bearer token). The seed is the subnet secret, held by the auth app + all trusted nodes; the user interacts only with the new node.
- **R-PAIR-2** Local trust store (TOFU + warn-on-change). Cross-subnet deferred.
- **R-PAIR-3** Fetch the current pairing code from any paired node (`spt pair --code` or via an agent).
- **R-PAIR-4** Prompt to **name the subnet** on first pairing; show the name on every subsequent pairing.
- **R-PAIR-5** (M4, ADR-0005 amend) **Multi-subnet pairing ceremony:** discovery takes **TOTP code + subnet-name** (the name namespaces the relay rendezvous) in all cases; **create-new names the subnet at creation** (no node-name mode); the relay rendezvous token is `H(subnet-name ‖ TOTP-epoch)` (under the hood — raw name+code at the surface; payload already E2E-encrypted).
- **R-PAIR-6** (M4, ADR-0005 amend) **Elevation-gated per-subnet code fetch:** retrieving a subnet's current code *from a node* requires OS privilege elevation (Windows UAC / Linux root-or-equivalent) or an elevated agent endpoint; otherwise the user falls back to their authenticator-app store. Fetch is **per-subnet** (`--subnet <name>` / `--create-new`), QR/`otpauth://` optional.
- **R-PAIR-7** (M4 data / frontend render) **Subnet icon:** an optional small image (square PNG/WebP, ≤256 KB) representing the subnet in GUIs; stored **inline** in subnet metadata (syncs over the same subnet-material channel as the name), editable by any node in the subnet, GUI-only consumer.

### 10b. Endpoint access control (ADR-0009)

- **R-SEC-1** (M4 node-tier; user-tier deferred) **Per-endpoint access whitelist:** an optional allow-list gating **who may remotely reach** an endpoint, checked by **origin node** of the inbound interaction (not sender-endpoint identity — *"the operator must be on a whitelisted machine"*). **Stateful-firewall semantics:** outbound goes to any visible node; an inbound **reply** correlated to the endpoint's own prior outbound (`__REPLY_TO__`) is allowed from any visible node; an **unsolicited/direct** inbound is allowed only from a whitelisted node. **Same-node operation always allowed**; default empty = open. **Node-tier (Ed25519 pubkeys) ships M4; user-tier** ("nodes of whitelisted users") is deferred to the per-(subnet,user) model (ADR-0006 seam) — schema reserves an inert `users` field. Enforced at the target node, synced as security material near the trust store, subnet-settable, revocable. **Composes as the outer gate** before capability grants; distinct table + polarity (origin-node/default-open vs agent-subject/default-deny). Three nested gates: subnet *visibility* → *access whitelist* → *capability grants*.

---

## 10b. Notifications (ADR-0007)

- **R-NOTIF-1** (M4) **Notification primitive:** a first-class user-directed, **dismissable, resurfacing** subnet event, distinct from inter-agent messages. **Per-subnet, replicated spool** (reuses registry distribution); subnet-wide dismiss-state. **`seen` (per-endpoint) vs `dismissed` (explicit)**; resurface undismissed at existing boundaries (state→active, `api boundary clear`/`compact`, new-session-start), gated by seen-per-endpoint + a **cross-endpoint suppression timeout** (~1h); scoped to subnets the endpoint is visible in. **The self-update prompt (R-UPD-4) and consent escalation are refactored to be notif producers** (open producer set). Envelope carries `from` (endpoint+node+subnet; subnet surfaced only to multi-subnet receivers).
- **R-NOTIF-2** (M4) **`spt notify` + `notif_command` seam:** any agent may issue a subnet-wide notif (v1: reaches the user's active endpoint). Delivery default = notif-flavored message to the agent's perch (agent surfaces it); optional **`notif_command` manifest template** on **both harness- and shell-adapter manifests** for endpoint-native rendering — if presence resolves to a **Shell** attached to the endpoint, the notif renders via *that shell's* template (the v1 seam and M5 generalization in one). *(Forward: targeting all/specific subnet users once per-(subnet,user) lands.)*

---

## 11. Self-update

- **R-UPD-1** **Peer-propagated** over P2P (layered on self-fetch; out-of-band still supported); the subnet self-heals to latest (ADR-0004).
- **R-UPD-2** **All binaries signature-verified before handoff**, regardless of source (spt-core release key).
- **R-UPD-3** **No-endpoint-termination invariant, scoped by update class (ADR-0004 §A).** For the **brain-only** class — the routine case — the guarantee is **absolute**: no endpoint process terminates or suspends, satisfied by the broker/brain split (the broker keeps held PTYs/children/sockets alive across brain replacement). **broker-compatible** swaps the broker behind the versioned IPC with endpoints still surviving. **broker-breaking** (held-resource-type / OS-API change) MAY suspend endpoints, but only via a *planned endpoint-cycle* with consent + scheduling — the absolute guarantee is explicitly weakened here.
- **R-UPD-4** **Not auto by default** — gated on user confirmation delivered to the most-recently-active live session, with an opt-in full-auto.
- **R-UPD-5** spt-core conducts **adapter ripple-updates** after self-update, per each adapter's update declaration and `min_spt_core_version` gate.

---

## 12. Terminal wrapper & frontend

- **R-TERM-1** A process-supervisor terminal wrapper (supersedes "capsule") hosting sessions in broker PTYs, multiplexed and name-addressable, attach/detach, headless↔headed.
- **R-TERM-2** A **session-surface** abstraction with native PTY as the day-one impl and network-attached surfaces for remote control; input injection at both `send-keys` (raw) and `send-line` (cooked) granularity; in-memory scrollback ring (disk spillover deferred).
- **R-TERM-3** Remote terminal streaming uses a **byte-stream** model for v1 (simple, faithful; near-LAN on Iroh-direct paths). Keep the surface abstraction generic enough that a predictive/state-sync "feels-local" layer can be added later without re-architecting — not built v1.
- **R-TERM-4** (M3, ADR-0008) **Live activity buffer (PTY digest):** a rolling, *parsed*, structured (source-tagged) view of recent PTY I/O (last ~N user turns + agent output between; tool sprints collapsed), built by running **adapter-supplied** manifest patterns (`input_pattern`, `agent_pattern`, `tool_pattern[]` + catchall) over the **broker-owned** PTY bytes — honors the no-built-in-parser rule (adapter owns the pattern). **Spt-hosted-primary** (needs the broker PTY); **harness-hosted is capability-gated** (an adapter that can feed the stream opts in; CC cannot). Distinct from scrollback (R-TERM-2) and transcript history (Paths A/B). Two access modes — **`spt digest <[subnet:]id[@node]>` snapshot pull** + a **broker-pushed structured-delta stream** (subscribe; changes only) riding the R-TERM-3 substrate; **address-gated** like messaging. Optional opt-in **Path-B persistence** as a coarse activity log (the "option C" endpoint-logging surface).
- **R-FRONT-1** Day-one headed frontend is a **launcher/manager** (not raw attach): list running + historic endpoints; launch a historic endpoint from its original manifest; tap into a running one; init a new one via a known adapter. Realizes the "guided resume" (XMB-style) and "management GUI" (per-endpoint panes) sketches. Its **CLI sibling** is a no-arg **`spt resume`** guided-resume picker — endpoints grouped by locality (on-node/this-project → on-node/other-project → off-node), MRU within group; selecting a running instance attaches, a non-running one chains into the adapter selector → launch, a "+ new" entry into the creation flow.

---

## 13. Installation & migration

- **R-INSTALL-1** Two paths: standalone (primitive) and harness-bootstrapped (calls into standalone). One-line signed-binary install script that registers PATH + the daemon as an OS service (systemd-user / Windows service), with `api`-triggered auto-start fallback.
- **R-INSTALL-2** Marketplace-repackaging-friendly (PortMaster/handheld, Android) — relocatable binary, minimal non-OS-entangled logic.
- **R-INSTALL-3** First run idempotent + interactive-optional: generate node identity, start daemon; pairing + subnet-naming are separate explicit steps.
- **R-INSTALL-4** (CONTEXT "adapter registration") **Adapter registration lifecycle:** `spt adapter add <path>` (or `--github <user/repo>`, manifest-first then install via the declared `[update]` avenue — *install is the first update*) validates the manifest and records it under `{SPT_HOME}/…/adapters/` (copy for `file_pull`, pointer for `delegated`); one command/dir for `kind="harness"` and `kind="shell"`. Node-local (drive/launch capability, not subnet advertisement). `spt adapter remove` is **soft-deregister** (hidden from new-creation; existing/live instances keep running) + an optional manifest **`uninstall`** template run once quiesced (or `--force`). The registered set is what self-update ripples (R-UPD-5).
- **R-MIGRATE-1** Standalone install **auto-detects a `claude_skill_owl` install and offers migration** (identity, agents, tracked Psyche context). First-class supported path.

---

## 14. Project infrastructure
- **R-INFRA-1** Issue/feature tracking on **GitHub** for v1; **tangled.org** documented as the principled migration target.

### 14.1 Documentation (designed-in, dual-audience)
- **R-DOCS-1** Docs serve **both human devs and AI dev-agents** — authored once in clean markdown, served in two depths. See [`docs/DOCS-STRATEGY.md`](./docs/DOCS-STRATEGY.md).
- **R-DOCS-2** A **sub-10-minute, runnable, deterministic killer quickstart** for each audience (humans: two agents exchange a message; dev-agents: a minimal adapter satisfying the manifest + `api` contract).
- **R-DOCS-3** **Diátaxis** structure (tutorial / how-to / reference / explanation) per capability vertical; **one canonical way to do X**.
- **R-DOCS-4** **Agent-consumable layer:** CI-generated `llms.txt`/`llms-full.txt`, markdown content negotiation, a machine-readable manifest JSON Schema at a stable path, an **MCP doc/resource server** exposing the harness contract as deterministic resources, and structured CLI help (`spt <cmd> --help` + a `--json` mode).
- **R-DOCS-5** **Anti-drift via CI:** rustdoc, the manifest schema, the agent exports, and CLI help are generated + checked in CI — doc quality on the same footing as tests.

---

## 15. v1 scope summary

**In v1:** harness-independent core (library + binary); manifest + `api` harness contract; both startup topologies; one-daemon broker/brain; LiveAgent/ReadyAgent/Psyche/Worker/SptNode endpoints + Shell-vs-agent seam + typed/binary payloads; multi-instance data model + subnet registry + resolution policy + dormant/suspended + two-tier context sync + remote-drive of running instances + off-node file transfer; first-class WAN networking + TOTP-SPAKE2 pairing + subnet naming; cross-node Psyche sync (replaces gh-sync); peer-propagated gated signed self-update + adapter ripple; terminal wrapper + launcher frontend (byte-stream remote streaming); one-line install + OS-service + legacy auto-migration; rebuilt spt-plugin to parity.

**Deferred (seam-compatible):** see [`docs/DEFERRED.md`](./docs/DEFERRED.md) — concrete Shells, PresenceChannel impl, presence gossip, instantiate-anywhere + consent, remote command exec + security gate, sidecar adapters, manifest includes, scrollback spillover, predictive/state-sync terminal layer, OS-level presence signal, macOS.

## 16. Success criteria (v1)

1. A non-CC harness (or a headless spike) drives the full live-agent lifecycle through manifest + `api` alone — proving harness independence.
2. The rebuilt spt-plugin reaches **feature parity** with frozen `claude_skill_owl`.
3. Two of a user's machines, freshly paired via one TOTP code, exchange messages and the user drives an agent on machine A from machine B — zero config beyond pairing.
4. A self-update rolls across the subnet **without terminating any endpoint**.
5. The same agent's mind follows the user across machines (two-tier sync); switching machines leaves the prior instance dormant and catches up the new active one.
6. Every `KNOWN-HAZARDS` invariant has a corresponding passing test.

## 17. Open design-phase questions

- ~~Exact `$SPT_HOME` on-disk layout~~ — **resolved**, see [`docs/STORAGE.md`](./docs/STORAGE.md) (single relocatable root; adapter-agnostic ID keying; separate `tracked/` git repo vs node-local `perches/`; one path resolver).
- ~~Manifest schema concretes~~ — **resolved**, see [`docs/MANIFEST.md`](./docs/MANIFEST.md) (opaque command templates; per-hook `can_inject`; node-wide cross-adapter fallback; global+override config knobs; worked `claude-spt` example). Capability-declaration shape beyond `hostable_types` remains open.
- ~~Refining Psyche authoring directives so the live/project context split holds cleanly~~ — **resolved**, see [`docs/CONTEXT-MEMORY.md`](./docs/CONTEXT-MEMORY.md) (per-topic discriminator + default-to-project; commune-source asymmetry; deep tier-tagged memformat with structured blocks; generated Retrieval Map; dense-working-set / sparse-tail injection; patterns from pi-agent-memory).
- ~~Warm-vs-cold dormancy default policy + suspend command surface~~ — **resolved**: active = most-recently-interacted + driver-attached (a linked Shell counts as a driver); no idle timer; auto-suspend opt-in (default OFF, node + endpoint override) counted from dormancy onset. Commands `spt suspend`/`wake`/`shutdown`/`refresh`. Boundary communes (`/clear`,`/compact`,`spt suspend`) now Self-authored (resume Self); `refresh`'s `/clear` uses the same rule. See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`.
- ~~Consent/security-gate UX~~ — **resolved**: within-one-subnet trust boundary (anti-surprise / anti-runaway / compromise-containment, not stranger-auth); hybrid model — grant store (`capability × agent × target-node`, target-enforced, subnet-settable, revocable) + interactive escalation (prompt to most-recently-active session: allow-once / allow-always / deny). Pre-consent flags are pre-authored grants. v1 ships the framework seam; gated capabilities (remote-exec, instantiate-anywhere) land with it later. See CONTEXT §Consent & security gates.
- ~~Remote-control: formal Shell or bare session-surface?~~ — **resolved**: opposite directions. A **Shell is agent→surface** (agent drives embodiment, receives sensory); **remote-control is user→agent** (a viewport onto a running instance). Remote-control is the bare network session-surface, *not* a Shell. (Shell model now fully specified — CONTEXT §Shell model, `docs/MANIFEST.md` `kind="shell"`, `docs/STORAGE.md`.)

## 18. References

- [`ROADMAP.md`](./ROADMAP.md) — path to fruition: review → de-risk spikes → walking skeleton → M0–M5.
- [`CONTEXT.md`](./CONTEXT.md) — glossary & model (authoritative for meaning).
- [`docs/adr/0001`](./docs/adr/0001-clean-room-fork-from-claude-skill-owl.md) … [`0005`](./docs/adr/0005-totp-seeded-spake2-node-pairing.md) — decisions.
- [`docs/KNOWN-HAZARDS.md`](./docs/KNOWN-HAZARDS.md) — invariants harvested from the sister project (test checklist).


1	# Deferred Features
2
3	Intended features explicitly cut from v1 scope but committed for later. Not backlog noise — these are decided directions parked for sequencing reasons. Keep this list short and real.
4
5	| Feature | Cut from | Why deferred | Trigger to revisit |
6	|---|---|---|---|
7	| Scrollback on-disk spillover | terminal wrapper v1 | In-memory ring covers the common case; spillover adds a persistence/rotation story | First long-running session that overflows the ring usefully, or any "scroll back further than the buffer" user need |
8	| Sidecar adapter process (long-running, wire-protocol) | harness contract v1 | Manifest + `spt.exe` subcommand surface covers v1 harnesses; sidecar only earns its keep for streaming / in-memory cross-event state | A harness outgrows manifest+hooks (needs streaming or stateful coordination) |
9	| Manifest `include` / composition (option C) | manifest v1 | Single flat manifest per harness is enough until a harness wants to override only part of a section | First sub-project that needs to inherit spt-core defaults but override one section |
10	| Concrete Shell types (GameRobot, OS-notification, in-session-inject) | Shell concept v1 | Model is locked; concrete surfaces are large, platform-specific, and not needed for core | First real use case for a driven surface (e.g. the 2D-world experiment) |
11	| PresenceChannel implementation (broker, dispatch/bind/thread) | endpoint types v1 | Depends on presence gossip + Shells + multi-instance routing; only the seams ship v1 | Once Shells exist and the subnet registry is stable |
12	| Presence gossip (`last_active_node/endpoint/timestamp` across subnet) | networking v1 | Needs the multi-instance registry first; agent-interaction heartbeat (7a-B) is the chosen signal | When PresenceChannel work begins |
13	| OS-level input-activity presence signal (7a-A) | presence v1 | Per-platform, privacy-loaded; agent-interaction heartbeat covers the v1 goal. **Note:** distinct from the v1 broker *sensing* user input on a PTY it already holds (spt-hosted) — that is in v1 and is NOT OS-wide input monitoring | Opt-in enhancement after agent-interaction presence proves out |
14	| Instantiate-anywhere + consent gate | Instances V1-mid | Remote launch over network is additive once remote-drive of running instances works. Consent *model* is now designed (CONTEXT §Consent & security gates — grant store + interactive escalation); v1 ships the framework seam, this capability lands into it later. (The `shell_wake_spawn_anywhere` flag is a pre-consent variant.) | After remote-drive ships; capability drops into the v1 consent seam |
15	| Remote command execution on another node | off-node reach-back v1 | Highest-risk capability; needs the consent/security gate. Workarounds exist (message a local agent; interact with local agent directly) | Designed alongside instantiate-anywhere's consent gate |
16	| Shell-binary (and harness-binary) sandboxing | Shell concept v1 | A capability toolset bounds what the *agent* can ask, not what the *binary* can do with its OS perms. Baseline stance: running any adapter/shell binary = a disclosed, accepted trust risk for all spt-core users | If/when untrusted third-party shell/harness binaries become common enough to warrant OS-level sandboxing |
17	| Concrete Shell adapters (e.g. GameRobot) + the shell binaries themselves | Shell concept v1 | The Shell *model*, manifest schema, perch layout, and `api` surface are now specified; concrete shells are large, platform-specific, and not needed for core | First real driven-surface use case |
18

# Deferred Features

Intended features explicitly cut from v1 scope but committed for later. Not backlog noise — these are decided directions parked for sequencing reasons. Keep this list short and real.

| Feature | Cut from | Why deferred | Trigger to revisit |
|---|---|---|---|
| Scrollback on-disk spillover | terminal wrapper v1 | In-memory ring covers the common case; spillover adds a persistence/rotation story | First long-running session that overflows the ring usefully, or any "scroll back further than the buffer" user need |
| Sidecar adapter process (long-running, wire-protocol) | harness contract v1 | Manifest + `spt.exe` subcommand surface covers v1 harnesses; sidecar only earns its keep for streaming / in-memory cross-event state | A harness outgrows manifest+hooks (needs streaming or stateful coordination) |
| Manifest `include` / composition (option C) | manifest v1 | Single flat manifest per harness is enough until a harness wants to override only part of a section | First sub-project that needs to inherit spt-core defaults but override one section |
| Concrete Shell types (GameRobot, OS-notification, in-session-inject) | Shell concept v1 | Model is locked; concrete surfaces are large, platform-specific, and not needed for core | First real use case for a driven surface (e.g. the 2D-world experiment) |
| PresenceChannel implementation (broker, dispatch/bind/thread) | endpoint types v1 | Depends on presence gossip + Shells + multi-instance routing; only the seams ship v1 | Once Shells exist and the subnet registry is stable |
| Presence gossip (`last_active_node/endpoint/timestamp` across subnet) | networking v1 | Needs the multi-instance registry first; agent-interaction heartbeat (7a-B) is the chosen signal | When PresenceChannel work begins |
| OS-level input-activity presence signal (7a-A) | presence v1 | Per-platform, privacy-loaded; agent-interaction heartbeat covers the v1 goal. **Note:** distinct from the v1 broker *sensing* user input on a PTY it already holds (spt-hosted) — that is in v1 and is NOT OS-wide input monitoring | Opt-in enhancement after agent-interaction presence proves out |
| Instantiate-anywhere + consent gate | Instances V1-mid | Remote launch over network is additive once remote-drive of running instances works. Consent *model* is now designed (CONTEXT §Consent & security gates — grant store + interactive escalation); v1 ships the framework seam, this capability lands into it later. (The `shell_wake_spawn_anywhere` flag is a pre-consent variant.) | After remote-drive ships; capability drops into the v1 consent seam |
| Remote command execution on another node | off-node reach-back v1 | Highest-risk capability; needs the consent/security gate. Workarounds exist (message a local agent; interact with local agent directly) | Designed alongside instantiate-anywhere's consent gate |
| Shell-binary (and harness-binary) sandboxing | Shell concept v1 | A capability toolset bounds what the *agent* can ask, not what the *binary* can do with its OS perms. Baseline stance: running any adapter/shell binary = a disclosed, accepted trust risk for all spt-core users | If/when untrusted third-party shell/harness binaries become common enough to warrant OS-level sandboxing |
| Concrete Shell adapters (e.g. GameRobot) + the shell binaries themselves | Shell concept v1 | The Shell *model*, manifest schema, perch layout, and `api` surface are now specified; concrete shells are large, platform-specific, and not needed for core | First real driven-surface use case |


1:# spt-core
11:## Language
37:### Deliverable shape
46:## Runtime model
77:### Manifest seams (outbound contract, detailed)
122:### Inbound `api` surface (detailed)
140:### Startup flows (the two topologies)
162:### Endpoint types
199:### Agent endpoints vs Shells
277:## Terminal wrapper
301:## Networking
316:### Cross-node Psyche sync
328:### Subnet notifications
346:## Self-update
358:## Instances
406:### Pieces the Instances model requires
438:### Off-node reach-back
445:### v1 scope line (V1-mid)
451:## Workspace & versioning
461:## Pairing & trust
505:## Consent & security gates
529:### Messaging substrate
534:## Installation
555:## Project infrastructure
560:### Frontend (day-one)


PowerShell

(Get-Item CONTEXT.md).Length; (Get-Item M4-PLAN.md).Length; (Get-Item traceable-reqs.toml).Length

Check file sizes


103031
20020
55279