## Installation

spt-core is per-machine and harness-independent, so it installs *before* and *independent of* any adapter.

**Two install paths, (b) is the primitive:**
- **(a) harness-bootstrapped** — a harness plugin installs spt-core as its bootstrap step (today's cplugs model: `/plugin` pulls the plugin, which fetches the spt-core binary). The plugin's role shrinks to this bootstrap; spt-core self-updates thereafter.
- **(b) standalone** — the user installs spt-core directly with no harness, for a Pi node, a Shell-only node, or a headless server. Path (a) calls into (b).

**Installer form:** a one-line script (`curl | sh` / `irm | iex`) that fetches the **signed** binary for the platform, verifies it, places it, registers the binary dir on system-wide PATH (so adapters call `spt api …` cross-OS), and registers the daemon as an OS service. Chosen for the dev-tool audience, cross-platform reach, and because the binary self-updates after. **Marketplace-repackaging-friendly:** nodes are foreseen on novel platforms (Android, medium-power Linux handhelds), so the install must be easy to repackage for platform marketplaces (e.g. PortMaster for handhelds, F-Droid-style for Android) — a relocatable binary + minimal, non-OS-entangled install logic.

**Daemon lifecycle:** registered as a systemd user service (Linux) / Windows service or scheduled task for the always-on guarantee, with `spt api`-triggered auto-start as the fallback (above). The first network bind triggers the OS firewall prompt here.

**What "installed" comprises:** the `spt` binary + the broker + the `$SPT_HOME` root (holds `node.key`, trust store, registry, spools, in-memory-seed fallback state). **First run is idempotent + interactive-optional:** generates the node identity and starts the daemon unattended; pairing (and subnet naming) is a separate explicit step.

**Legacy migration at install:** standalone install **auto-detects an existing `claude_skill_owl` (modern SPT) install and offers migration** (the migration commitment above) — identity, agents, tracked Psyche context.

## Project infrastructure

+spt-core is per-machine and harness-independent, so it installs *before* and *independent of* any adapter.

+**Two install paths, (b) is the primitive:**

+- **(a) harness-bootstrapped** — a harness plugin installs spt-core as its bootstrap step (today's cplugs model: `/plugin` pulls the plugin, which fetches the spt-core binary). The plugin's role shrinks to this bootstrap; spt-core self-updates thereafter.

+- **(b) standalone** — the user installs spt-core directly with no harness, for a Pi node, a Shell-only node, or a headless server. Path (a) calls into (b).

+**Installer form:** a one-line script (`curl | sh` / `irm | iex`) that fetches the **signed** binary for the platform, verifies it, places it, registers the binary dir on system-wide PATH (so adapters call `spt api …` cross-OS), and registers the daemon as an OS service. Chosen for the dev-tool audience, cross-platform reach, and because the binary self-updates after. **Marketplace-repackaging-friendly:** nodes are foreseen on novel platforms (Android, medium-power Linux handhelds), so the install must be easy to repackage for platform marketplaces (e.g. PortMaster for handhelds, F-Droid-style for Android) — a relocatable binary + minimal, non-OS-entangled install logic.

+**Daemon lifecycle:** registered as a systemd user service (Linux) / Windows service or scheduled task for the always-on guarantee, with `spt api`-triggered auto-start as the fallback (above). The first network bind triggers the OS firewall prompt here.

+**What "installed" comprises:** the `spt` binary + the broker + the `$SPT_HOME` root (holds `node.key`, trust store, registry, spools, in-memory-seed fallback state). **First run is idempotent + interactive-optional:** generates the node identity and starts the daemon unattended; pairing (and subnet naming) is a separate explicit step.

+**Legacy migration at install:** standalone install **auto-detects an existing `claude_skill_owl` (modern SPT) install and offers migration** (the migration commitment above) — identity, agents, tracked Psyche context.


All captured. Confirmations:

- **`api seed` replaces the seed file** — SessionStart calls `spt api seed --pid <pid> …`, daemon holds it **in-memory** (no file, no `$SPT_HOME` resolution for adapters). Strictly better than the file; PATH registration means adapters just call `spt api …` on any OS.
- **Daemon auto-start** on any `api` invocation that needs it; `$SPT listen` transparently spins it up for the first session. Confirmed.
- **Install:** both paths ((b) standalone primitive, (a) bootstraps into it), signed one-line script, **marketplace-repackaging-friendly** (PortMaster/handhelds, Android) — relocatable binary + minimal non-OS-entangled logic, OS-service + auto-start fallback, idempotent first-run, **auto-detect legacy migration**.
- **Fetch TOTP from any paired node** (`spt pair --code` or ask an agent) — every node holds the seed.
- **Subnet naming** on first pair, shown on every subsequent pairing.

That closes the harness contract, startup, install, and pairing UX. **CONTEXT.md is comprehensive; 5 ADRs + KNOWN-HAZARDS + DEFERRED written.**

I think the decision tree is resolved enough to assemble the PRD. Before I do — anything else on your mind, or any area you want grilled further? If not, I'll write `spt-core/PRD.md` synthesizing everything: vision, goals/non-goals, the endpoint/instance model, the daemon architecture, harness contract (manifest + `api` surface + seams), networking/pairing, self-update, install/migration, v1 scope vs deferred, success criteria, and the open design-phase items.


does PTY control (spt-hosted) mean that a terminal session can be "streamed" to any SPT GUI, even over WAN, and still feel native/feel local to the end user?


<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.


Yes — architecturally it's exactly what the design enables, and it *can* feel native. But "feels local over WAN" is an honest engineering bar that needs the right technique, not just byte-forwarding. Let me be precise.

**What's already in place to make it possible:**
- spt-hosted = broker holds the PTY master; the **session-surface abstraction (Q5-C)** generalizes to network-attached surfaces; the **Instances model (ADR-0003)** is daemon-to-daemon PTY proxy over Iroh. So a PTY on node A streaming to a GUI on node B over WAN is the same mechanism as local attach, just with a network surface. The scrollback ring is already daemon-held.
- Iroh connects **direct ~70%** of the time (near-LAN latency — feels great with even naive proxying) and **relay-fallback ~30%** (extra hops, higher latency — where naive proxying would feel like laggy SSH).

**What separates "works" from "feels local":** the thing that makes remote terminals feel laggy is *character echo waiting for the round-trip*. Two delivery models:
- **Byte-stream proxy** (SSH-style): forward raw PTY bytes both ways. Simple. Latency-sensitive — every keystroke waits a round-trip to echo. Fine on direct/LAN, sluggish on relay/high-latency.
- **Screen-state sync** (Mosh-style SSP): sync the *terminal grid* not the byte stream; send screen diffs; **predict + locally echo** keystrokes and reconcile. Instant-feeling typing even at 200ms+, resilient to packet loss, and seamless detach/reattach/roam. This is the gold standard for "native over WAN."

Terminal output is low-bandwidth (text + ANSI), so throughput is never the bottleneck — it's purely a latency/echo problem, which is solvable.

**Honest verdict:**
- **Yes**, the architecture enables WAN terminal streaming to any SPT GUI, with free detach/reattach (session lives on its node's daemon; GUI is a surface).
- **To truly feel local**, it needs Mosh-style predictive echo + screen-state sync over the session-surface stream — real work, and **likely a polish milestone, not v1.** v1 (V1-mid remote-drive) can ship near-byte-stream proxying that feels great on direct connections and SSH-ish on relay.
- **The critical day-one requirement:** model the session-surface as **screen-state-capable** (a terminal grid + diffs + scrollback), *not* a dumb byte pipe — so the predictive-echo/state-sync layer slots in later without re-architecting. The scrollback ring already pushes us this way.

This is also the realization of Image #3's "latest session output as an interactive terminal frame" — a live, attachable, WAN-streamable pane in the management GUI.

It's a genuine headline differentiator: *drive your agent's terminal from a phone or GUI anywhere, feels local.* Want me to capture it that way — as a flagship capability with the screen-state-capable-surface day-one requirement and the Mosh-style "feels local" layer as a named post-v1 milestone?


Design spt-core binary and messaging library architecture


-i'm not sure about "screen-state sync". sounds like it would be incompatible/require stretching or scaling for remote windows which have a different column width than the source. byte stream seems ideal? -question about the Instances model: if a user wants to work with a remote live agent, but operating on their current machine with its local files (probably after git fetch and git pull), how would they smoothly transition from "instance remotely controlling the live agent" (which is effectively a Shell, right?) to "operating the live agent locally"--including all its psyche context from the previous machine? i guess what i'm getting at is, i was thinking of instances as just being the same live agent/endpoint<<harness<<adapter running on multiple machines with synced context updates. a Shell for remotely controlling an endpoint is a separate idea. does this make sense?


Two real corrections. Taking them in turn.

**Byte-stream vs screen-state — you're mostly right, and I over-pushed Mosh.** One clarification though: the column-width issue isn't actually solved by *either* model, and neither "stretches/scales." A PTY has one size; the program renders to that size. Both models handle a differently-sized viewer the same way — by **resizing the PTY** to the viewer (the program reflows). Screen-state doesn't scale a fixed grid; it re-renders at the new size. The *only* genuinely hard case is **two simultaneous viewers with different widths** on one PTY — one size must win (tmux sizes to the smallest + letterboxes). That's identical under byte-stream and screen-state.

So the real tradeoff is narrower than I framed:
- **Byte-stream:** dead simple, perfectly faithful. Costs: keystroke echo waits a round-trip (laggy only on relay/high-latency paths), and resize/reconnect must replay (can briefly corrupt).
- **Screen-state:** predictive local echo + clean reconnect, but you reimplement a terminal emulator server-side. Complex.

Given Iroh is direct ~70% (near-LAN, byte-stream feels fine there) and you value simplicity: **byte-stream is a sound v1 choice.** I'll drop the "screen-state-capable day-one" requirement. Only ask: keep the surface abstraction generic enough that a predictive/state layer *could* slot in later if relay-path lag ever annoys you — not built, just not foreclosed. Agree?

**Instances — yes, your model is cleaner, and I conflated two things. Let me separate them:**

There are really **three** concepts; I wrongly made remote-drive the *definition* of instances:

1. **Instance (your core meaning):** the *same* endpoint (same harness+adapter) running **natively on a machine** — local files, local compute — with **psyche context synced** across its instances. `ling@desktop` and `ling@laptop` are both real local `ling`s; their *files/project are per-node* (can't teleport a git repo), but their *mind (psyche context) syncs*. "Same agent on multiple machines, context kept in sync." This is the heart of it.

2. **Remote-control (separate, Shell-like):** from your machine, attach a control/view surface to an instance *running on another machine*. Compute + files stay remote; you're a viewport. Yes — effectively a Shell (a driven surface), just user→agent direction instead of GameRobot's agent→user. This is the byte-stream terminal attach. You use it when you specifically want *that machine's* environment.

3. **The transition (your scenario):** remote-controlling desktop's `ling` → switching to operating `ling` locally on your laptop with local files. Smooth because: **git pull** brings the files, and **psyche-download via the fresh-with-preload resume seam** brings desktop's latest synced context into the fresh local instance. Both are already v1 (cross-node psyche sync + context-freshness rule + the resume seam). So the handoff is: stop the remote surface → spawn/activate the local instance → it preloads the synced context → continue on local files. The mind follows; the files are local.

So my ADR-0003 framing ("equal functionality = drivable from anywhere via remote PTY") was backwards-weighted. Corrected: **primary = native instances with synced psyche context; remote-control is a secondary attach-a-surface mode.** Amending the docs.

One genuine open question this surfaces: **context-sync scope.** If `ling` is active on *both* desktop and laptop simultaneously on *different* projects, naive "newest context wins" could pull desktop's mind into laptop's unrelated work. Recommend: **scope psyche-context sync per (endpoint, project)** — the freshness rule only catches up instances on the *same* project, so divergent simultaneous work doesn't clobber. Agree, or should sync be global-per-endpoint regardless of project?

Let me make the edits to match this corrected model — confirm the reconciliation lands first.


-i'm not sure about "screen-state sync". sounds like it would be incompatible/require stretching or scaling for remote windows which have a different column width than the source. byte stream seems id…


-byte stream direction: agree. -one correction: to make the transition, stopping the remote surface shouldn't be necessary. `ling@desktop` simply becomes "dormant" while `ling@laptop` is actively driven. if the user then goes home to control `ling@desktop` again, `ling@laptop` becomes the dormant instance. should we memorialize this "instance dormancy" concept? any other angles i'm missing/additional elements or clarifications you can think of? -generally agree that the context-sync scope should be limited to update instances on the same project. that said, psyche context is *already* split into per-agent "live context", and per-project-per-agent "project context". the directives need refining as psyche has been actively putting more project detail in live context than it puts in project context, but the split is there conceptually. so all ling instances should get "live context" sync, and only ones sharing the same project should get "project context" sync.


Both land. Capturing dormancy + the two-tier context split, then a couple angles I can add.


Edit

**instance**:
A materialization of an endpoint ID on a specific node — `ling@desktop`, `ling@laptop` are distinct instances of one ID. **Node-anchored**: an instance's substance (project working dir, harness session history, Psyche context, spool) lives on *its* node's filesystem and cannot teleport. Instances of one ID are distinct *seats*, possibly on different projects — not shared state.

"All instances have equal functionality" means **any instance is drivable with the full command surface from any node** — not that instances share state. The ID is shared identity for addressing; the state is per-node.

**How remote drive works** (req: a LiveAgent operates on any node it's been instantiated on, from any other machine): the instance's session runs inside its node's `spt-daemon` PTY. A second node's `spt-daemon` opens a network session-surface stream to the hosting daemon, which proxies the PTY. This is the network-attached `session surface` implementation (the generalized form of the terminal wrapper's session-surface abstraction) running daemon-to-daemon — the collapse of PTY-host + network-host into one `spt-daemon` is what makes it direct. "Headed on a whim" generalizes to "from any node."

**instance**:
The *same* endpoint (same harness + adapter) running **natively on a machine** — local files, local compute. `ling@desktop` and `ling@laptop` are both real local `ling`s, not remote views of one. What is per-node vs synced:
- **Per-node (anchored, cannot teleport):** the project working directory / files, and the harness session history. A git repo can't teleport; files are obtained locally (e.g. `git pull`).
- **Synced across instances:** the **Psyche context** (the agent's mind). See the two-tier sync below.

This is the core meaning of "the same agent on multiple machines": same identity, native local materialization on each, with the mind kept in sync. The ID is the shared identity for addressing; files are local; the mind follows.

**dormant instance** (instance dormancy):
At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

**Remote-control vs local operation (two distinct modes — not the same as instances):**
- **Operate locally:** drive the native instance on *your* machine (its local files, its synced mind). The normal case.
- **Remote-control (Shell-like):** attach a control/view surface to an instance *running on another node* — compute + files stay remote; you are a viewport (the byte-stream terminal attach, daemon-to-daemon over Iroh). Used when you specifically want *that machine's* environment. This is effectively a Shell (a driven surface, user→agent direction), separate from the instance concept itself.
- **Smooth handoff (remote-control → local):** no teardown required. `git pull` brings the files; the **fresh-with-preload resume seam** preloads the other instance's latest synced context (psyche-download); the remote instance simply goes dormant. The mind follows; the files are local.


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

**instance**:
A materialization of an endpoint ID on a specific node — `ling@desktop`, `ling@laptop` are distinct instances of one ID. **Node-anchored**: an instance's substance (project working dir, harness session history, Psyche context, spool) lives on *its* node's filesystem and cannot teleport. Instances of one ID are distinct *seats*, possibly on different projects — not shared state.

"All instances have equal functionality" means **any instance is drivable with the full command surface from any node** — not that instances share state. The ID is shared identity for addressing; the state is per-node.

**How remote drive works** (req: a LiveAgent operates on any node it's been instantiated on, from any other machine): the instance's session runs inside its node's `spt-daemon` PTY. A second node's `spt-daemon` opens a network session-surface stream to the hosting daemon, which proxies the PTY. This is the network-attached `session surface` implementation (the generalized form of the terminal wrapper's session-surface abstraction) running daemon-to-daemon — the collapse of PTY-host + network-host into one `spt-daemon` is what makes it direct. "Headed on a whim" generalizes to "from any node."

**instance**:
The *same* endpoint (same harness + adapter) running **natively on a machine** — local files, local compute. `ling@desktop` and `ling@laptop` are both real local `ling`s, not remote views of one. What is per-node vs synced:
- **Per-node (anchored, cannot teleport):** the project working directory / files, and the harness session history. A git repo can't teleport; files are obtained locally (e.g. `git pull`).
- **Synced across instances:** the **Psyche context** (the agent's mind). See the two-tier sync below.

This is the core meaning of "the same agent on multiple machines": same identity, native local materialization on each, with the mind kept in sync. The ID is the shared identity for addressing; files are local; the mind follows.

**dormant instance** (instance dormancy):
At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

**Remote-control vs local operation (two distinct modes — not the same as instances):**
- **Operate locally:** drive the native instance on *your* machine (its local files, its synced mind). The normal case.
- **Remote-control (Shell-like):** attach a control/view surface to an instance *running on another node* — compute + files stay remote; you are a viewport (the byte-stream terminal attach, daemon-to-daemon over Iroh). Used when you specifically want *that machine's* environment. This is effectively a Shell (a driven surface, user→agent direction), separate from the instance concept itself.
- **Smooth handoff (remote-control → local):** no teardown required. `git pull` brings the files; the **fresh-with-preload resume seam** preloads the other instance's latest synced context (psyche-download); the remote instance simply goes dormant. The mind follows; the files are local.

The logical identity (`ling`). Shared subnet-wide. The thing you address.

-A materialization of an endpoint ID on a specific node — `ling@desktop`, `ling@laptop` are distinct instances of one ID. **Node-anchored**: an instance's substance (project working dir, harness session history, Psyche context, spool) lives on *its* node's filesystem and cannot teleport. Instances of one ID are distinct *seats*, possibly on different projects — not shared state.

+The *same* endpoint (same harness + adapter) running **natively on a machine** — local files, local compute. `ling@desktop` and `ling@laptop` are both real local `ling`s, not remote views of one. What is per-node vs synced:

+- **Per-node (anchored, cannot teleport):** the project working directory / files, and the harness session history. A git repo can't teleport; files are obtained locally (e.g. `git pull`).

+- **Synced across instances:** the **Psyche context** (the agent's mind). See the two-tier sync below.

-"All instances have equal functionality" means **any instance is drivable with the full command surface from any node** — not that instances share state. The ID is shared identity for addressing; the state is per-node.

+This is the core meaning of "the same agent on multiple machines": same identity, native local materialization on each, with the mind kept in sync. The ID is the shared identity for addressing; files are local; the mind follows.

-**How remote drive works** (req: a LiveAgent operates on any node it's been instantiated on, from any other machine): the instance's session runs inside its node's `spt-daemon` PTY. A second node's `spt-daemon` opens a network session-surface stream to the hosting daemon, which proxies the PTY. This is the network-attached `session surface` implementation (the generalized form of the terminal wrapper's session-surface abstraction) running daemon-to-daemon — the collapse of PTY-host + network-host into one `spt-daemon` is what makes it direct. "Headed on a whim" generalizes to "from any node."

+**dormant instance** (instance dormancy):

+At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

+**Remote-control vs local operation (two distinct modes — not the same as instances):**

+- **Operate locally:** drive the native instance on *your* machine (its local files, its synced mind). The normal case.

+- **Remote-control (Shell-like):** attach a control/view surface to an instance *running on another node* — compute + files stay remote; you are a viewport (the byte-stream terminal attach, daemon-to-daemon over Iroh). Used when you specifically want *that machine's* environment. This is effectively a Shell (a driven surface, user→agent direction), separate from the instance concept itself.

+- **Smooth handoff (remote-control → local):** no teardown required. `git pull` brings the files; the **fresh-with-preload resume seam** preloads the other instance's latest synced context (psyche-download); the remote instance simply goes dormant. The mind follows; the files are local.


**cross-instance context freshness**:
When a parallel instance becomes active again, it is unobtrusively fed the latest Psyche context from the most-recently-active instance — specifically whichever instance holds context that is both newest among peers *and* newer than the reactivating instance's own. Requires per-instance context versioning + the registry + cross-node Psyche sync.

**cross-instance context freshness** (two-tier):
When a dormant instance is activated again, it is unobtrusively fed the latest Psyche context — newest-among-peers *and* newer than its own. Scoped by the existing Psyche-context split:
- **live context** (per-agent, project-independent — the agent's general mind): synced to **all** instances of the endpoint, regardless of project.
- **project context** (per-agent-per-project — project-specific detail): synced **only** to instances sharing the **same project**.

So all `ling` instances catch up on live context; only same-project `ling` instances catch up on project context. This resolves the "divergent simultaneous work" concern (two instances on different projects don't clobber each other's project detail) without losing the "mind follows everywhere" property.

Caveat / dependency: the live-vs-project split exists conceptually today but the **authoring directives need refining** — the Psyche has been putting more project-specific detail into live context than into project context. The sync split only behaves well once those directives are sharpened so each tier holds the right material. The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.


**cross-instance context freshness**:
When a parallel instance becomes active again, it is unobtrusively fed the latest Psyche context from the most-recently-active instance — specifically whichever instance holds context that is both newest among peers *and* newer than the reactivating instance's own. Requires per-instance context versioning + the registry + cross-node Psyche sync.

**cross-instance context freshness** (two-tier):
When a dormant instance is activated again, it is unobtrusively fed the latest Psyche context — newest-among-peers *and* newer than its own. Scoped by the existing Psyche-context split:
- **live context** (per-agent, project-independent — the agent's general mind): synced to **all** instances of the endpoint, regardless of project.
- **project context** (per-agent-per-project — project-specific detail): synced **only** to instances sharing the **same project**.

So all `ling` instances catch up on live context; only same-project `ling` instances catch up on project context. This resolves the "divergent simultaneous work" concern (two instances on different projects don't clobber each other's project detail) without losing the "mind follows everywhere" property.

Caveat / dependency: the live-vs-project split exists conceptually today but the **authoring directives need refining** — the Psyche has been putting more project-specific detail into live context than into project context. The sync split only behaves well once those directives are sharpened so each tier holds the right material. The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

**instantiate-anywhere** (seam day-one, behavior deferred):

From node Y, spin up an instance of an endpoint on node X by routing a launch command to X's daemon, which runs the endpoint's manifest locally. "Agents can spin up instances (including of themselves) on any node, possibly requiring user consent" — so a **consent gate** governs remote instantiation. The consent-UX design is deferred; the model is built to accommodate it.

-When a parallel instance becomes active again, it is unobtrusively fed the latest Psyche context from the most-recently-active instance — specifically whichever instance holds context that is both newest among peers *and* newer than the reactivating instance's own. Requires per-instance context versioning + the registry + cross-node Psyche sync.

+**cross-instance context freshness** (two-tier):

+When a dormant instance is activated again, it is unobtrusively fed the latest Psyche context — newest-among-peers *and* newer than its own. Scoped by the existing Psyche-context split:

+- **live context** (per-agent, project-independent — the agent's general mind): synced to **all** instances of the endpoint, regardless of project.

+- **project context** (per-agent-per-project — project-specific detail): synced **only** to instances sharing the **same project**.

+So all `ling` instances catch up on live context; only same-project `ling` instances catch up on project context. This resolves the "divergent simultaneous work" concern (two instances on different projects don't clobber each other's project detail) without losing the "mind follows everywhere" property.

+Caveat / dependency: the live-vs-project split exists conceptually today but the **authoring directives need refining** — the Psyche has been putting more project-specific detail into live context than into project context. The sync split only behaves well once those directives are sharpened so each tier holds the right material. The precedence/freshness guard (KNOWN-HAZARDS 6.5) — with node identity in the marker — keeps a dormant instance's stale context from clobbering the active authoritative one.

When the user is driving an instance from off-node, the agent should be able to:


-byte stream direction: agree. -one correction: to make the transition, stopping the remote surface shouldn't be necessary. `ling@desktop` simply becomes "dormant" while `ling@laptop` is actively driv…


Now amending ADR-0003 to fix the backwards-weighted framing.


## Status

accepted (2026-05-29) · amended (2026-05-30) — see Amendment below; the original framing over-weighted remote-drive as the *definition* of multi-instance.

## Amendment (2026-05-30): native-instances-with-synced-mind is primary; remote-drive is a separate mode

The original decision below framed "operate a LiveAgent on any node from any other machine" primarily as **remote PTY drive**. That is backwards-weighted. Corrected model:

- **Primary — instance:** the *same* endpoint (same harness+adapter) running **natively** on a machine (local files, local compute), with the **Psyche mind synced** across instances. Files/session-history are per-node and cannot teleport; the mind follows. This is the core meaning of "same agent on multiple machines."
- **Instance dormancy:** typically one instance is actively-driven; others are **dormant** (live, state-preserved, re-activatable in place). Switching machines makes the previously-driven instance dormant — no explicit stop. Registry status is active / dormant / offline.
- **Remote-control (separate, Shell-like):** attaching a control/view surface to an instance *running on another node* (compute+files stay remote; you are a viewport — the byte-stream terminal attach). A driven surface, user→agent direction; distinct from the instance concept.
- **Handoff (remote-control → local):** `git pull` brings files; the fresh-with-preload resume seam preloads the other instance's synced context; the remote instance goes dormant. No teardown.
- **Two-tier context sync:** **live context** (per-agent) syncs to all instances; **project context** (per-agent-per-project) syncs only to same-project instances. Resolves divergent simultaneous work without losing "mind follows everywhere." (Depends on refining the Psyche authoring directives so each tier holds the right material.)

The decision below stands except where it implies remote-drive is the primary realization of multi-instance — read "drivable from anywhere" as "runnable natively on any of its nodes with synced mind, **plus** an optional remote-control surface mode."


The file C:\Users\decid\Documents\projects\spt-core\docs\adr\0003-multi-instance-node-anchored-identity-model.md has been updated successfully. (file state is current in your context — no need to Read it back)

## Status

accepted (2026-05-29) · amended (2026-05-30) — see Amendment below; the original framing over-weighted remote-drive as the *definition* of multi-instance.

## Amendment (2026-05-30): native-instances-with-synced-mind is primary; remote-drive is a separate mode

The original decision below framed "operate a LiveAgent on any node from any other machine" primarily as **remote PTY drive**. That is backwards-weighted. Corrected model:

- **Primary — instance:** the *same* endpoint (same harness+adapter) running **natively** on a machine (local files, local compute), with the **Psyche mind synced** across instances. Files/session-history are per-node and cannot teleport; the mind follows. This is the core meaning of "same agent on multiple machines."
- **Instance dormancy:** typically one instance is actively-driven; others are **dormant** (live, state-preserved, re-activatable in place). Switching machines makes the previously-driven instance dormant — no explicit stop. Registry status is active / dormant / offline.
- **Remote-control (separate, Shell-like):** attaching a control/view surface to an instance *running on another node* (compute+files stay remote; you are a viewport — the byte-stream terminal attach). A driven surface, user→agent direction; distinct from the instance concept.
- **Handoff (remote-control → local):** `git pull` brings files; the fresh-with-preload resume seam preloads the other instance's synced context; the remote instance goes dormant. No teardown.
- **Two-tier context sync:** **live context** (per-agent) syncs to all instances; **project context** (per-agent-per-project) syncs only to same-project instances. Resolves divergent simultaneous work without losing "mind follows everywhere." (Depends on refining the Psyche authoring directives so each tier holds the right material.)

The decision below stands except where it implies remote-drive is the primary realization of multi-instance — read "drivable from anywhere" as "runnable natively on any of its nodes with synced mind, **plus** an optional remote-control surface mode."

# Multi-instance, node-anchored identity model

## Status

accepted (2026-05-29)

## Context

The sister project models an agent as essentially one perch on one machine, and explicitly deferred "agent name resolution" across a subnet. spt-core commits to two user-facing requirements that break that model: (a) the user can interact with any endpoint from any machine, and (b) a LiveAgent can be operated on any node it has ever been instantiated on, from any other machine. The Instances concept (each endpoint classifies as an instance; the same endpoint ID can have instances across multiple machines with equal functionality) requires deciding what an "instance" actually is before any of it can be built.

## Decision

Split agent identity into two layers: a subnet-wide **endpoint ID** (the logical identity, `ling`) and an **instance** (a materialization of that ID on a specific node, `ling@desktop`).

**Instances are node-anchored, not portable.** An instance's substance — project working directory, harness session history, Psyche context, spool — lives on its node's filesystem and does not teleport. Instances of one ID are distinct *seats* (possibly on different projects), sharing only identity. "Equal functionality across all machines" therefore means *any instance is drivable with the full command surface from any node*, NOT that instances share state.

Rejected alternative — **portable/synced instances** (one logical conversation+state replicated across nodes): a LiveAgent's whole point is operating inside a project on a machine; the project filesystem can't be teleported, and full state replication across nodes is a distributed-consistency problem with no payoff for the actual use case (drive my desktop agent from my laptop).

Remote drive is realized by daemon-to-daemon PTY proxying over the network session-surface abstraction — enabled by the prior decision (ADR-0002) to collapse PTY-host and network-host into one `spt-daemon`.

**v1 scope (V1-mid):** ship the data-model split, the subnet registry (the now-mandatory name-resolution layer), the resolution policy, remote-drive of already-running instances, and cross-node Psyche sync transport. Defer (seam-compatible) instantiate-anywhere + its consent gate, remote command execution + its security gate, and the cross-instance context-freshness auto-feed rule.

## Consequences

- The subnet registry becomes a mandatory, first-class subsystem (eventually-consistent `endpoint_id → [instances]`), not a deferred nicety.
- Bare-ID addressing needs a deterministic resolution policy (local → most-recently-active → explicit `id@node` override).
- The networking decision (ADR-0002) is now load-bearing for a flagship user feature, not just messaging — remote drive depends on it working well.
- Cross-node Psyche sync over P2P replaces the sister project's GitHub-repo sync, removing the `gh`/account/setup dependency.
- Remote command execution and remote instantiation are the highest-risk capabilities in the system; both are gated behind a consent/security model whose design is deferred but whose existence is assumed by the architecture.

+accepted (2026-05-29) · amended (2026-05-30) — see Amendment below; the original framing over-weighted remote-drive as the *definition* of multi-instance.

+## Amendment (2026-05-30): native-instances-with-synced-mind is primary; remote-drive is a separate mode

+The original decision below framed "operate a LiveAgent on any node from any other machine" primarily as **remote PTY drive**. That is backwards-weighted. Corrected model:

+- **Primary — instance:** the *same* endpoint (same harness+adapter) running **natively** on a machine (local files, local compute), with the **Psyche mind synced** across instances. Files/session-history are per-node and cannot teleport; the mind follows. This is the core meaning of "same agent on multiple machines."

+- **Instance dormancy:** typically one instance is actively-driven; others are **dormant** (live, state-preserved, re-activatable in place). Switching machines makes the previously-driven instance dormant — no explicit stop. Registry status is active / dormant / offline.

+- **Remote-control (separate, Shell-like):** attaching a control/view surface to an instance *running on another node* (compute+files stay remote; you are a viewport — the byte-stream terminal attach). A driven surface, user→agent direction; distinct from the instance concept.

+- **Handoff (remote-control → local):** `git pull` brings files; the fresh-with-preload resume seam preloads the other instance's synced context; the remote instance goes dormant. No teardown.

+- **Two-tier context sync:** **live context** (per-agent) syncs to all instances; **project context** (per-agent-per-project) syncs only to same-project instances. Resolves divergent simultaneous work without losing "mind follows everywhere." (Depends on refining the Psyche authoring directives so each tier holds the right material.)

+The decision below stands except where it implies remote-drive is the primary realization of multi-instance — read "drivable from anywhere" as "runnable natively on any of its nodes with synced mind, **plus** an optional remote-control surface mode."


Captured — dormancy + two-tier sync (live context → all instances; project context → same-project only), ADR-0003 amended.

**Angles I'd add:**

1. **Dormant ≠ offline ≠ stopped** — registry is three-state: *active* (driven now), *dormant* (live seat, node online, not driven), *offline* (node unreachable). Bare-`ling` resolution → active instance, else most-recently-active dormant, else offline-with-spool. Captured.

2. **Wake vs spawn** — re-activating a *dormant* instance is a lightweight "wake" (state's already there), distinct from instantiate-anywhere's fresh spawn. Worth a distinct op.

3. **One genuine open design point — warm vs cold dormancy.** When an instance goes dormant, does its harness session stay *running* (warm — instant re-activation, but consumes RAM/compute on every node, and for spt-plugin a live `claude` session may hold a billable/resource footprint) or get *suspended/closed* and resumed-on-wake (cold — frees resources, but wake pays a resume cost)? Recommend: **default cold (close-and-resume via the existing resume seam), with an opt-in keep-warm** per endpoint for machines where instant switch matters. Cold matches "you're not driving it, why burn resources on three nodes." Your call — warm default, cold default, or per-endpoint config?

4. **Authoritative-mind invariant** — at any moment the actively-driven instance is the authoritative mind; the precedence guard (KNOWN-HAZARDS 6.5, node-id in marker) stops a dormant instance's stale context from clobbering it; on the next activation elsewhere, *that* becomes authoritative. The two-tier sync is catch-up-on-activation, not continuous merge. Captured.

Confirm #3 (warm/cold/config) and anything else on dormancy — then I think the tree's genuinely resolved and I'll assemble the PRD.


1. agree + possible to target dormant instances by addressing their node i.e. `ling@desktop` 2. agree 3. updating language. default warm (as "dormant"), opt-in auto-cold (as "suspended"), with a subcommand that can trigger endpoint suspend from anywhere. 4. any endpoint transition from active-->[dormant|suspended] should fire an echo commune that gets synced to the new active instance. basically a finer point behind catch-up-on-activation. -another note on dormant/suspended endpoints: deferred messages shouldn't deliver to them until they're no longer dormant or suspended.


Locked. Capturing the dormant(warm)/suspended(cold) split + transition echo-commune + deferred-gate.


**dormant instance** (instance dormancy):
At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

**dormant / suspended instance:**
At most one instance of an endpoint is typically the *actively-driven* one; the others rest. Two resting states:
- **dormant** (default, *warm*): a live, state-preserved seat whose harness session stays running → instant re-activation. The default resting state.
- **suspended** (opt-in, *cold*): the harness session is closed and resumed-on-wake → frees RAM/compute (and, for billable harnesses, footprint), at the cost of a resume on wake. Reached via per-endpoint opt-in *auto-suspend*, or on demand: a subcommand can **suspend an endpoint from anywhere** (any node).

Resting is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit stop is needed to switch. A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

**transition echo commune:** any active → (dormant | suspended) transition fires an **echo commune** capturing the outgoing instance's final context delta; that commune syncs to whichever instance becomes active next. This is the finer mechanism behind catch-up-on-activation (and mirrors the sister project's "echo commune before signoff" pattern — KNOWN-HAZARDS 3.3).

**deferred-message gate:** deferred (spool-only, hook-consumer) messages are **not** delivered to a dormant or suspended instance — they hold until it is active again. (Extends the deferred-row semantics, KNOWN-HAZARDS 1.4/4.4, with an instance-state gate.)


1. agree + possible to target dormant instances by addressing their node i.e. `ling@desktop` 2. agree 3. updating language. default warm (as "dormant"), opt-in auto-cold (as "suspended"), with a subco…


**dormant instance** (instance dormancy):
At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

**dormant / suspended instance:**
At most one instance of an endpoint is typically the *actively-driven* one; the others rest. Two resting states:
- **dormant** (default, *warm*): a live, state-preserved seat whose harness session stays running → instant re-activation. The default resting state.
- **suspended** (opt-in, *cold*): the harness session is closed and resumed-on-wake → frees RAM/compute (and, for billable harnesses, footprint), at the cost of a resume on wake. Reached via per-endpoint opt-in *auto-suspend*, or on demand: a subcommand can **suspend an endpoint from anywhere** (any node).

Resting is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit stop is needed to switch. A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

**transition echo commune:** any active → (dormant | suspended) transition fires an **echo commune** capturing the outgoing instance's final context delta; that commune syncs to whichever instance becomes active next. This is the finer mechanism behind catch-up-on-activation (and mirrors the sister project's "echo commune before signoff" pattern — KNOWN-HAZARDS 3.3).

**deferred-message gate:** deferred (spool-only, hook-consumer) messages are **not** delivered to a dormant or suspended instance — they hold until it is active again. (Extends the deferred-row semantics, KNOWN-HAZARDS 1.4/4.4, with an instance-state gate.)

This is the core meaning of "the same agent on multiple machines": same identity, native local materialization on each, with the mind kept in sync. The ID is the shared identity for addressing; files are local; the mind follows.

-**dormant instance** (instance dormancy):

-At most one instance of an endpoint is typically the *actively-driven* one; the others are **dormant** — live, state-preserved seats on their nodes that are simply not currently being driven. Dormancy is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit "stop" is needed to switch — the previously-driven instance simply becomes dormant. A dormant instance retains its files + session + last context, can still receive messages (spool), and is re-activatable in place ("wake," lighter than a fresh spawn). Registry status is three-valued: **active / dormant / offline** (offline = the node itself is unreachable, distinct from dormant-but-online).

+At most one instance of an endpoint is typically the *actively-driven* one; the others rest. Two resting states:

+- **dormant** (default, *warm*): a live, state-preserved seat whose harness session stays running → instant re-activation. The default resting state.

+- **suspended** (opt-in, *cold*): the harness session is closed and resumed-on-wake → frees RAM/compute (and, for billable harnesses, footprint), at the cost of a resume on wake. Reached via per-endpoint opt-in *auto-suspend*, or on demand: a subcommand can **suspend an endpoint from anywhere** (any node).

+Resting is attention-based and emergent: driving `ling@laptop` leaves `ling@desktop` dormant; later driving `ling@desktop` again leaves `ling@laptop` dormant. No explicit stop is needed to switch. A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).

+**transition echo commune:** any active → (dormant | suspended) transition fires an **echo commune** capturing the outgoing instance's final context delta; that commune syncs to whichever instance becomes active next. This is the finer mechanism behind catch-up-on-activation (and mirrors the sister project's "echo commune before signoff" pattern — KNOWN-HAZARDS 3.3).

+**deferred-message gate:** deferred (spool-only, hook-consumer) messages are **not** delivered to a dormant or suspended instance — they hold until it is active again. (Extends the deferred-row semantics, KNOWN-HAZARDS 1.4/4.4, with an instance-state gate.)

**Remote-control vs local operation (two distinct modes — not the same as instances):**

- **Operate locally:** drive the native instance on *your* machine (its local files, its synced mind). The normal case.

- **Remote-control (Shell-like):** attach a control/view surface to an instance *running on another node* — compute + files stay remote; you are a viewport (the byte-stream terminal attach, daemon-to-daemon over Iroh). Used when you specifically want *that machine's* environment. This is effectively a Shell (a driven surface, user→agent direction), separate from the instance concept itself.


Captured — dormant(warm)/suspended(cold), suspend-from-anywhere, transition echo-commune feeding catch-up, deferred-gate, 4-state registry.

The decision tree is now genuinely resolved end to end. Reading CONTEXT back once to synthesize faithfully, then assembling the PRD.