- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties.

- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties. A **linked Shell counts as a driver** (an agent with a live shell stays active even when the user isn't typing).


-active-->dormant "idle-grace window passes with no interaction"--nix this. session should be considered active so long as it was the most recently-interacted instance for that ID + has a driver attac…


Design spt-core binary and messaging library architecture


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)

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

-- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties.

+- **active** = the instance is the **most-recently-interacted instance for that ID** *and* **has a driver attached**. Both conditions. There is **no idle-grace timer** — a session stays active as long as it holds those two properties. A **linked Shell counts as a driver** (an agent with a live shell stays active even when the user isn't typing).

- **active → dormant** — the driver detaches, *or* another instance of the same ID becomes the most-recently-interacted one (attention shifts). No explicit stop needed to switch; driving `ling@laptop` makes `ling@desktop` dormant.

- **dormant → suspended** — manual (`spt suspend` / `spt shutdown` / shell `api owner-shutdown`), *or* opt-in auto-suspend after an `auto-suspend-after` threshold **counted from the moment the instance went dormant**.

- **wake (→ active)** — `spt wake`, a driver attaches, or a shell wake-watcher fires.


Now the boundary-commune change + `spt refresh` — capturing in CONTEXT-MEMORY.


Edit

### Boundary communes are Self-authored (resume Self, not echo)

To get meaningful **live-context** updates on a consistent basis (the live signal is strong only from Self), three boundaries author their commune by **`--resume`-ing the Self session** (the actual agent), not by a background haiku echo:

- **`/clear`** → resume Self → **commune** file-drop.
- **`/compact`** → resume Self → **commune** file-drop.
- **`spt suspend`** → resume Self → **signoff** file-drop (suspend = the endpoint going down, so it's a signoff, not a commune).

This deliberately swaps the project-centric echo for a Self-authored capture at exactly the moments that matter (context loss + suspend), so live context stays current. (Routine cadence communes remain echo — project-primary, live-conservative.)

### `spt refresh` (spt-hosted self-service context refresh)

A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.

### Ownership


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

### Boundary communes are Self-authored (resume Self, not echo)

To get meaningful **live-context** updates on a consistent basis (the live signal is strong only from Self), three boundaries author their commune by **`--resume`-ing the Self session** (the actual agent), not by a background haiku echo:

- **`/clear`** → resume Self → **commune** file-drop.
- **`/compact`** → resume Self → **commune** file-drop.
- **`spt suspend`** → resume Self → **signoff** file-drop (suspend = the endpoint going down, so it's a signoff, not a commune).

This deliberately swaps the project-centric echo for a Self-authored capture at exactly the moments that matter (context loss + suspend), so live context stays current. (Routine cadence communes remain echo — project-primary, live-conservative.)

### `spt refresh` (spt-hosted self-service context refresh)

A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.

### Ownership

# Psyche context & memory model

> Draft design doc (2026-05-31). Resolves PRD §17.3 (Psyche live/project authoring directives) and the memformat redesign. Governs how the Psyche authors, structures, syncs, and surfaces an agent's context. On-disk layout: `docs/STORAGE.md`. Several patterns are adapted from `BigscreenVR/pi-agent-memory` (credited inline).

## Two-tier context

- **live context** (per-agent, project-independent — the agent's *mind*): identity/persona, relationship + rapport with the user, the user's cross-project preferences & working style, cross-project life threads, **learnings from work that may apply to other projects**, a thin **project roster** (one line per project the agent works on), the agent's self-model. Synced to **all** instances of the endpoint.
- **project context** (per-agent-per-project — what the agent is doing *here*): codebase/architecture, decisions made here, **current focus / next steps / todos**, project conventions, build/test commands, blockers. Synced **only** to same-project instances.

(This split independently matches pi-agent-memory's Project Store vs Global Store and its discriminator dialogue — convergent validation.)

### The discriminator (applied per *topic*, not per fragment)

> **"Would this still be true and relevant if the user opened a *different* project tomorrow?"** Yes → live. No → project. Equivalently: **WHO the agent is** (live) vs **WHAT it's doing here** (project).

### Anti-leak rules

The observed failure is project detail leaking *into* live context (which then wrongly syncs everywhere). Countermeasures:

1. **Default-to-project.** A fragment goes to project context unless it *passes the cross-project test* to be promoted to live. Promotion needs justification; demotion is free. The bias points against the leak.
2. **`current focus / next steps` is project-scoped** — the single biggest leak category. Route it to project unless the focus genuinely spans projects.
3. **Live holds a thin project roster, never project depth** — cross-project *awareness* travels; depth stays per-project.
4. **Commune-source asymmetry** (the structural fix): the live signal is strong only from **Self-authored communes**; **echo communes are project-centric** (they synthesize project-heavy session logs). So **echo communes are project-context-primary and live-context-conservative** — they make few/no live edits. This kills the leak at its source. Provenance is recorded on each block (`Source:`).

### Ownership

The live/project authoring directives are **core-owned** — fed to every Psyche by spt-core (part of `$psyche_prompt` / a core system-prompt fragment), so the split is consistent across all adapters (inconsistent rules → sync chaos). Adapters add only harness mechanics (how to write the drop file).

## memformat (deeply integrated, self-evolving, the split scaffold)

memformat goes from a decorative, ignored seed to the **load-bearing schema that defines the two-tier context**. It makes the live/project decision **per-topic and rare** instead of per-fragment and constant — which is what makes the split stable.

- **Tier-tagged topics.** Each topic carries a tier: `live` or `project`. The tier is decided **once, when the topic is created** (apply the discriminator one time), not per fragment. Communes then just *fill* topics; the tier routes content to the right context file + sync tier. The two-slice envelope (`<live-context>`/`<project-context>`) becomes the *rendering* of tier-tagged topics.
- **Self-evolving.** The Psyche adds/removes/re-tiers topics as it learns Self's realm of work (memformat's original intent). **Re-tiering migrates** the topic's accumulated content between tiers (project→live or vice versa), not forward-only.
- **The schema is live** (per-agent, travels everywhere); **project-tagged topic content** is instantiated per-project.
- Example tiers: `identity / relationship / user-preferences / cross-project-learnings / project-roster` = **live**; `focus / decisions / architecture / todos / blockers` = **project**.

### Structured blocks (adapted from pi-agent-memory)

A memformat topic is a tier-tagged **container of structured blocks**, not free-form prose. Each block:

```
## <stable-block-id>
Concepts: <comma list>          # retrieval keys
Aliases: <comma list>           # retrieval synonyms (optional)
Applies to: <glob paths>        # sub-project scoping (optional)
Source: self-commune | echo-commune | user   # provenance (drives the live-conservative rule)
Created: <iso>
Updated: <iso>
Hash: sha256:<canonical-content-hash>         # for block-level OCC merge (deferred, see below)
Pinned: true|false              # hot (dense-injected) vs indexed (sparse, on-demand)

<body>
```

`Concepts`/`Aliases` make context **retrievable**; `Applies to` enables fine-grained scoping + query-routing; `Source` carries provenance; `Pinned` drives start-injection density (below).

## Retrieval Map (generated INDEX)

A generated, lightweight **INDEX** over topics — summaries + concepts + aliases + paths — **not** a vector DB, **not** a full mirror (adapted from pi-agent-memory's Retrieval Map). One INDEX per **project** (aggregating all agents' project context → realizes cross-agent synthesis + query-routing: "which endpoint owns this task") and per **agent**. Regenerated on context change; the merge driver + INDEX regeneration keep it current.

## Start-injection: dense working set, sparse tail

The balance between immediate productivity and bounded context cost — tuned for SPT's **per-agent, work-fast** priority (vs pi's multi-agent-per-project digging, which is sparse-by-default):

- **DENSE — the working set, injected in full at start:** all **live context** + the **current project's hot blocks** (`Pinned` + recently-updated focus/decisions/todos). The agent is immediately productive, no digging.
- **SPARSE — the long tail, pointer + INDEX, retrieved on demand:** other projects, **cross-agent/cross-project synthesis**, cold/archived blocks. (This is where pi's bounded "topic-names + pointer, never inline bodies" awareness pattern applies — it scales regardless of store size.)
- **Hot/cold curated by the Psyche** via `Pinned` + recency; keeping the hot set tight is part of maintaining memformat.
- **Tunable** via a working-set **density knob** (spt-core global setting + per-endpoint override).

## Deferred

- **Rebalance / Memory-Health pass** (later): the Psyche periodically (a) demotes leaked live→project content, and (b) — the deeper, pi-credited insight — verifies context is still *true vs the current code* (Memory Health), which requires investigating the codebase, so it's an autonomous-agent job, distinct from mechanical staleness (hash/INDEX mismatch).
- **Block-level OCC merge** (v1.x): the per-block `Hash:` (canonical content hash, excluding `Updated`/`Hash`) enables block-level, stale-aware cross-node merge — finer than the v1 whole-file newest-wins driver (`docs/STORAGE.md`). Recorded now (the block format carries `Hash:`), applied later.

## Not adopted

pi-agent-memory's **human-in-the-loop approval** (extract → review → approve, max-5-candidates) and **bash-mutation guardrails** fit its "human curates" philosophy. SPT's Psyche is **trusted to author its own mind autonomously** — so we adopt pi's *structure* (blocks, INDEX, bounded awareness, provenance, OCC) but **not** its approval gate. Controlled mutation is already covered by the drop-file single-writer rule (KNOWN-HAZARDS 6.4).

+### Boundary communes are Self-authored (resume Self, not echo)

+To get meaningful **live-context** updates on a consistent basis (the live signal is strong only from Self), three boundaries author their commune by **`--resume`-ing the Self session** (the actual agent), not by a background haiku echo:

+- **`/clear`** → resume Self → **commune** file-drop.

+- **`/compact`** → resume Self → **commune** file-drop.

+- **`spt suspend`** → resume Self → **signoff** file-drop (suspend = the endpoint going down, so it's a signoff, not a commune).

+This deliberately swaps the project-centric echo for a Self-authored capture at exactly the moments that matter (context loss + suspend), so live context stays current. (Routine cadence communes remain echo — project-primary, live-conservative.)

+### `spt refresh` (spt-hosted self-service context refresh)

+A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

+> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.


- **`api boundary <clear|compact>`** — context-boundary report; **carries the new `session_id`** (it rotates on `/clear` or `/compact`), so the daemon rebinds the perch to the new session id while keeping the stable identity + `parent_pid` anchor. Fires the split-echo-commune (catch-up brief to Self + Psyche).

- **`api boundary <clear|compact>`** — context-boundary report; **carries the new `session_id`** (it rotates on `/clear` or `/compact`), so the daemon rebinds the perch to the new session id while keeping the stable identity + `parent_pid` anchor. Authors a **Self-resume commune** (resume the Self session → commune file-drop) rather than a background echo — strong live-context signal at the boundary (see `docs/CONTEXT-MEMORY.md`).


- **`api state <busy|idle>`** — adapter reports session activity; writes the activity/idle sentinel in the session perch. By default `api state idle` also writes the **echo-commune gate sentinel** (`.more-done`-equiv — modern spt couples them); `--no-gate` suppresses that coupling, and a standalone **`api echo-gate <set|clear>`** gives granular adapters explicit control over when echo communes may fire, independent of idle.

- **`api worker-start`** / **`api worker-stop`** — Worker (subagent) perch create/teardown under the parent (nested, registry-tracked).

- **`api worker-poll`** — a Worker (subagent) receives its queued messages (inbound from Self or sibling Workers).

-- **`api boundary <clear|compact>`** — context-boundary report; **carries the new `session_id`** (it rotates on `/clear` or `/compact`), so the daemon rebinds the perch to the new session id while keeping the stable identity + `parent_pid` anchor. Fires the split-echo-commune (catch-up brief to Self + Psyche).

+- **`api boundary <clear|compact>`** — context-boundary report; **carries the new `session_id`** (it rotates on `/clear` or `/compact`), so the daemon rebinds the perch to the new session id while keeping the stable identity + `parent_pid` anchor. Authors a **Self-resume commune** (resume the Self session → commune file-drop) rather than a background echo — strong live-context signal at the boundary (see `docs/CONTEXT-MEMORY.md`).

- **`api session-end`** — session stop/crash report → soft teardown by default (preserve perch + spool + tracked history for recovery — KNOWN-HAZARDS 6.2). **`--erase`** instead hard-wipes the perch and tracked history (for ephemeral/secondary adapters that act as robust agent-spawned-agent surfaces).

- **`api history-log`** — Path B: ingest normalized records into spt-core's native history store.

- **`api presence`** — adapter reports user interaction → updates the presence datum `(last_active_node, last_active_endpoint, ts)`. In the spt-hosted topology, presence is **also** updated by the broker *detecting* (sensing, not watching/logging) user input on a held PTY — privacy-preserving (it notes that input occurred, records no content).


**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary commune, cascades shells offline).

**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary signoff, cascades shells offline), `spt refresh` (spt-hosted only — an agent clears + resumes itself without stalling: `/clear` → commune capture → guaranteed post-clear resume signal so its turn restarts from immediate next-steps; see `docs/CONTEXT-MEMORY.md`).


**Default policy:** warm (dormant) is the default when undriven. **Auto-suspend is opt-in, default OFF globally**, but **node-overridable** (a handheld / Pi / resource-constrained node may default it ON) and **per-endpoint overridable** (e.g. a billable harness whose warm session holds a cost footprint). Thresholds (`auto-suspend-after`) are config knobs: global default → node override → endpoint override.

-**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary commune, cascades shells offline).

+**Commands** (user/agent-initiated lifecycle, unprefixed): `spt suspend <id[@node]>` (suspend any of your instances from any node), `spt wake <id[@node]>` (explicitly wake a dormant/suspended instance), `spt shutdown` (an agent suspends its *own* endpoint — graceful, fires the suspend boundary signoff, cascades shells offline), `spt refresh` (spt-hosted only — an agent clears + resumes itself without stalling: `/clear` → commune capture → guaranteed post-clear resume signal so its turn restarts from immediate next-steps; see `docs/CONTEXT-MEMORY.md`).

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


- Warm-vs-cold dormancy default policy details and the suspend-from-anywhere command surface.

- ~~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). See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`. (Open sub-Q: refresh's internal `/clear` — echo vs Self-resume capture.)


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

- ~~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 details and the suspend-from-anywhere command surface.

+- ~~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). See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`. (Open sub-Q: refresh's internal `/clear` — echo vs Self-resume capture.)

- Consent/security-gate UX (shared by instantiate-anywhere + remote-exec) when those land.

- ~~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`.)


Bash

cd "C:/Users/decid/Documents/projects/spt-core" && git add -A && git commit -q -F - <<'EOF'
docs: dormancy policy, suspend surface, boundary communes, spt refresh (17.4)

- Dormancy transitions: active = most-recently-interacted + driver-attached
  (a linked Shell counts as a driver); no idle timer; active->dormant on
  driver detach or attention shift; auto-suspend opt-in (default OFF, node +
  endpoint override) counted from dormancy onset
- Commands: spt suspend / wake / shutdown (+ id@node targeting)
- Boundary communes are Self-authored: /clear, /compact, spt suspend all
  --resume the Self session (clear/compact -> commune file-drop; suspend ->
  signoff file-drop) for a strong live-context signal; routine cadence stays
  echo
- spt refresh (spt-hosted): /clear -> commune capture -> guaranteed post-clear
  resume so the agent restarts from immediate next-steps
- Open sub-Q recorded: refresh's internal /clear (echo vs Self-resume)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git push -q origin main && echo pushed && git log --oneline -1

Commit and push 17.4


warning: in the working copy of 'CONTEXT.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'PRD.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/CONTEXT-MEMORY.md', LF will be replaced by CRLF the next time Git touches it
pushed
95353b7 docs: dormancy policy, suspend surface, boundary communes, spt refresh (17.4)


17.4 recorded, committed (`95353b7`), pushed.

**Captured:**
- **Dormancy** — active = most-recently-interacted *and* driver-attached (**a linked Shell counts as a driver**); no idle timer; active→dormant on driver-detach or attention-shift; auto-suspend opt-in (default OFF, node + endpoint override), timer counted from dormancy onset.
- **Commands** — `spt suspend` / `wake` / `shutdown` (+ `id@node`).
- **Boundary communes now Self-authored** — `/clear`, `/compact`, `spt suspend` all `--resume` the Self session (clear/compact → commune drop; suspend → signoff drop) for a strong live-context signal; routine cadence stays echo. This directly reinforces the 17.3 commune-source asymmetry — live context now updates meaningfully at exactly the boundaries that matter.
- **`spt refresh`** (spt-hosted) — `/clear` → commune → guaranteed post-clear resume so the agent restarts from its immediate next-steps without stalling.

**One open sub-question** (flagged in CONTEXT-MEMORY + PRD): does `refresh`'s internal `/clear` use the Self-resume-commune rule, or a lighter **echo** capture — since a guaranteed resume immediately follows (the agent is back authoring live context right after)? I lean **echo for refresh** (the resume *is* the Self-authored moment), **Self-resume for a bare `/clear`** (no guaranteed resume → capture live signal before it's lost). Your call on that.

Then **17.5** (consent/security-gate UX) is the last standing §17 item. Want to settle the refresh sub-Q and move to 17.5?


open question: still uses the Self-resume-commune rule. --- onward to 17.5!


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


Resolved: `refresh`'s `/clear` uses the Self-resume-commune rule too (all boundaries consistent). Fixing that note, then into 17.5.


Edit

A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.

A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear` (which uses the **Self-resume-commune rule** above — consistent with all other boundaries), (2) thereby captures a commune, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).


# Psyche context & memory model

> Draft design doc (2026-05-31). Resolves PRD §17.3 (Psyche live/project authoring directives) and the memformat redesign. Governs how the Psyche authors, structures, syncs, and surfaces an agent's context. On-disk layout: `docs/STORAGE.md`. Several patterns are adapted from `BigscreenVR/pi-agent-memory` (credited inline).

## Two-tier context

- **live context** (per-agent, project-independent — the agent's *mind*): identity/persona, relationship + rapport with the user, the user's cross-project preferences & working style, cross-project life threads, **learnings from work that may apply to other projects**, a thin **project roster** (one line per project the agent works on), the agent's self-model. Synced to **all** instances of the endpoint.
- **project context** (per-agent-per-project — what the agent is doing *here*): codebase/architecture, decisions made here, **current focus / next steps / todos**, project conventions, build/test commands, blockers. Synced **only** to same-project instances.

(This split independently matches pi-agent-memory's Project Store vs Global Store and its discriminator dialogue — convergent validation.)

### The discriminator (applied per *topic*, not per fragment)

> **"Would this still be true and relevant if the user opened a *different* project tomorrow?"** Yes → live. No → project. Equivalently: **WHO the agent is** (live) vs **WHAT it's doing here** (project).

### Anti-leak rules

The observed failure is project detail leaking *into* live context (which then wrongly syncs everywhere). Countermeasures:

1. **Default-to-project.** A fragment goes to project context unless it *passes the cross-project test* to be promoted to live. Promotion needs justification; demotion is free. The bias points against the leak.
2. **`current focus / next steps` is project-scoped** — the single biggest leak category. Route it to project unless the focus genuinely spans projects.
3. **Live holds a thin project roster, never project depth** — cross-project *awareness* travels; depth stays per-project.
4. **Commune-source asymmetry** (the structural fix): the live signal is strong only from **Self-authored communes**; **echo communes are project-centric** (they synthesize project-heavy session logs). So **echo communes are project-context-primary and live-context-conservative** — they make few/no live edits. This kills the leak at its source. Provenance is recorded on each block (`Source:`).

### Boundary communes are Self-authored (resume Self, not echo)

To get meaningful **live-context** updates on a consistent basis (the live signal is strong only from Self), three boundaries author their commune by **`--resume`-ing the Self session** (the actual agent), not by a background haiku echo:

- **`/clear`** → resume Self → **commune** file-drop.
- **`/compact`** → resume Self → **commune** file-drop.
- **`spt suspend`** → resume Self → **signoff** file-drop (suspend = the endpoint going down, so it's a signoff, not a commune).

This deliberately swaps the project-centric echo for a Self-authored capture at exactly the moments that matter (context loss + suspend), so live context stays current. (Routine cadence communes remain echo — project-primary, live-conservative.)

### `spt refresh` (spt-hosted self-service context refresh)

A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.

### Ownership

The live/project authoring directives are **core-owned** — fed to every Psyche by spt-core (part of `$psyche_prompt` / a core system-prompt fragment), so the split is consistent across all adapters (inconsistent rules → sync chaos). Adapters add only harness mechanics (how to write the drop file).

## memformat (deeply integrated, self-evolving, the split scaffold)

memformat goes from a decorative, ignored seed to the **load-bearing schema that defines the two-tier context**. It makes the live/project decision **per-topic and rare** instead of per-fragment and constant — which is what makes the split stable.

- **Tier-tagged topics.** Each topic carries a tier: `live` or `project`. The tier is decided **once, when the topic is created** (apply the discriminator one time), not per fragment. Communes then just *fill* topics; the tier routes content to the right context file + sync tier. The two-slice envelope (`<live-context>`/`<project-context>`) becomes the *rendering* of tier-tagged topics.
- **Self-evolving.** The Psyche adds/removes/re-tiers topics as it learns Self's realm of work (memformat's original intent). **Re-tiering migrates** the topic's accumulated content between tiers (project→live or vice versa), not forward-only.
- **The schema is live** (per-agent, travels everywhere); **project-tagged topic content** is instantiated per-project.
- Example tiers: `identity / relationship / user-preferences / cross-project-learnings / project-roster` = **live**; `focus / decisions / architecture / todos / blockers` = **project**.

### Structured blocks (adapted from pi-agent-memory)

A memformat topic is a tier-tagged **container of structured blocks**, not free-form prose. Each block:

```
## <stable-block-id>
Concepts: <comma list>          # retrieval keys
Aliases: <comma list>           # retrieval synonyms (optional)
Applies to: <glob paths>        # sub-project scoping (optional)
Source: self-commune | echo-commune | user   # provenance (drives the live-conservative rule)
Created: <iso>
Updated: <iso>
Hash: sha256:<canonical-content-hash>         # for block-level OCC merge (deferred, see below)
Pinned: true|false              # hot (dense-injected) vs indexed (sparse, on-demand)

<body>
```

`Concepts`/`Aliases` make context **retrievable**; `Applies to` enables fine-grained scoping + query-routing; `Source` carries provenance; `Pinned` drives start-injection density (below).

## Retrieval Map (generated INDEX)

A generated, lightweight **INDEX** over topics — summaries + concepts + aliases + paths — **not** a vector DB, **not** a full mirror (adapted from pi-agent-memory's Retrieval Map). One INDEX per **project** (aggregating all agents' project context → realizes cross-agent synthesis + query-routing: "which endpoint owns this task") and per **agent**. Regenerated on context change; the merge driver + INDEX regeneration keep it current.

## Start-injection: dense working set, sparse tail

The balance between immediate productivity and bounded context cost — tuned for SPT's **per-agent, work-fast** priority (vs pi's multi-agent-per-project digging, which is sparse-by-default):

- **DENSE — the working set, injected in full at start:** all **live context** + the **current project's hot blocks** (`Pinned` + recently-updated focus/decisions/todos). The agent is immediately productive, no digging.
- **SPARSE — the long tail, pointer + INDEX, retrieved on demand:** other projects, **cross-agent/cross-project synthesis**, cold/archived blocks. (This is where pi's bounded "topic-names + pointer, never inline bodies" awareness pattern applies — it scales regardless of store size.)
- **Hot/cold curated by the Psyche** via `Pinned` + recency; keeping the hot set tight is part of maintaining memformat.
- **Tunable** via a working-set **density knob** (spt-core global setting + per-endpoint override).

## Deferred

- **Rebalance / Memory-Health pass** (later): the Psyche periodically (a) demotes leaked live→project content, and (b) — the deeper, pi-credited insight — verifies context is still *true vs the current code* (Memory Health), which requires investigating the codebase, so it's an autonomous-agent job, distinct from mechanical staleness (hash/INDEX mismatch).
- **Block-level OCC merge** (v1.x): the per-block `Hash:` (canonical content hash, excluding `Updated`/`Hash`) enables block-level, stale-aware cross-node merge — finer than the v1 whole-file newest-wins driver (`docs/STORAGE.md`). Recorded now (the block format carries `Hash:`), applied later.

## Not adopted

pi-agent-memory's **human-in-the-loop approval** (extract → review → approve, max-5-candidates) and **bash-mutation guardrails** fit its "human curates" philosophy. SPT's Psyche is **trusted to author its own mind autonomously** — so we adopt pi's *structure* (blocks, INDEX, bounded awareness, provenance, OCC) but **not** its approval gate. Controlled mutation is already covered by the drop-file single-writer rule (KNOWN-HAZARDS 6.4).

-A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear`, (2) triggers a commune capture, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

+A lever for an agent behind an **spt-hosted** endpoint to clear and resume itself without stalling. It: (1) triggers a `/clear` (which uses the **Self-resume-commune rule** above — consistent with all other boundaries), (2) thereby captures a commune, and (3) **guarantees a resume signal hits the harness post-clear** so the agent's turn restarts and it resumes work — starting from its immediate next-steps. Spt-hosted-only because the guarantee requires the daemon to own the PTY and inject the post-clear resume (it can't guarantee that for harness-hosted sessions it doesn't control).

-> **Open:** does `refresh`'s internal `/clear` use the Self-resume-commune rule above, or a lighter **echo** capture — given that a guaranteed resume immediately follows (so the agent is back and authoring live context itself right after)? Leaning echo-for-refresh (the immediate resume *is* the Self-authored moment), Self-resume-commune for a bare user-initiated `/clear` (no guaranteed resume → capture the live signal before it's lost). To confirm.


Boundary communes (`/clear`,`/compact`,`spt suspend`) now Self-authored (resume Self). See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`. (Open sub-Q: refresh's internal `/clear` — echo vs Self-resume capture.)

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


-- ~~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). See CONTEXT §Instances + `docs/CONTEXT-MEMORY.md`. (Open sub-Q: refresh's internal `/clear` — echo vs Self-resume capture.)

+- ~~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`.


Refresh resolution captured (will commit with 17.5). Now —

**17.5 — Consent / security-gate UX.** This gates the high-risk cross-node capabilities. First, the framing that shapes everything:

**The trust boundary.** Everything here is *within one user's own subnet* — their own nodes, paired via TOTP-SPAKE2, mutually trusted. Cross-subnet (other users) is deferred. So consent is **not** stranger-authentication; it's **guardrails against three things:** (1) an agent doing something surprising/destructive on the user's behalf that they didn't want, (2) **runaway** (an agent spawning instances or burning compute/billing across nodes), (3) **compromise containment** (if one node/agent is compromised, limit blast radius of its cross-node powers).

**What needs gating, and how:**
| Capability | Risk | Proposed gate |
|---|---|---|
| Remote command execution | Highest (arbitrary code on another node) | default-deny; explicit grant per (agent, target-node), optional command-pattern allowlist (CC-style `Bash(git log:*)`) |
| Instantiate-anywhere (spawn endpoint on remote node) | Resource/surprise | default-deny; grant per (agent, target-node) |
| Shell `can_shutdown` | Shell suspends owner | **pre-consent via manifest** (trusting the shell adapter = accepting it) — already specified |
| `shell_wake_spawn_anywhere` | Cross-node fresh-spawn to wake | **pre-consent via endpoint flag** — already specified |
| Remote-drive of your *own running* instance | Low (your own instance) | ungated (maybe a light "node X is driving ling@desktop" notification) |
| Cross-node context/Psyche sync | None (your own data) | ungated |

**Proposed consent model — hybrid (grants + interactive escalation):**
1. **Grant store** — records `capability × subject(agent) × target(node/endpoint) × constraint(command-pattern, optional time-limit)`. **Enforced at the target node** (the node receiving the remote action checks its local grants), but **settable subnet-wide** (a grant made from your laptop for "ling may run commands on desktop" propagates to desktop). **Revocable.** Lives with subnet security material (near the trust store), not in the context git repo.
2. **Interactive escalation** — an ungranted high-risk action → the target node routes a **consent prompt to the user's most-recently-active session** (the same registry-resolution precursor the update-prompt uses; PresenceChannel will later generalize it). Options: **allow-once / allow-always (writes a grant) / deny.**
3. **Pre-consent flags** (shells) are just grants authored ahead of time via manifest/endpoint settings — same model, different authoring path.

**Scope (v1 vs deferred):** the gated *capabilities* (remote-exec, instantiate-anywhere) are already **deferred**, so the *full* consent UX lands with them. v1 ships the **framework seam** — the grant-store shape + the deliver-consent-to-user mechanism — so the deferred features drop into a coherent gate rather than forcing a rewrite (same pattern as the instances seams).

Four decisions for you:
1. **Hybrid model (grants + interactive escalation)** — agree, or do you prefer pure-interactive (always prompt, CC-style) or pure-grants (set-and-forget, no prompts)?
2. **Grant enforcement at the target node, settable subnet-wide, revocable** — agree?
3. **Consent prompt → most-recently-active session** with allow-once / allow-always / deny — agree? (Anything beyond those three options — e.g. allow-for-1-hour?)
4. **Granularity** — is `capability × agent × target-node` enough, or do you want finer (per-command-pattern allowlists for remote-exec; per-endpoint not just per-node)?


1. agree. 2. agree. 3. agree. CC AskUserQuestion has a freeform text field by default. can refine later if needed. 4. that's enough.


All confirmed. Recording 17.5 — the last §17 item.


## Consent & security gates

Gates the high-risk cross-node capabilities. **Trust boundary:** everything is within *one user's own subnet* (their own TOTP-SPAKE2-paired, mutually-trusted nodes; cross-subnet is deferred). So consent is **not** stranger-authentication — it is guardrails against (1) an agent doing something surprising/destructive the user didn't want, (2) **runaway** (spawning instances / burning compute/billing across nodes), (3) **compromise containment** (limiting a compromised node/agent's cross-node blast radius).

**Consent model — hybrid (grants + interactive escalation):**
- **Grant store** — records `capability × subject (agent) × target (node)` (this granularity is sufficient; finer per-command-pattern scoping is a later refinement). **Enforced at the target node** (the node receiving a remote action checks its local grants), **settable subnet-wide** (a grant authored from any node propagates to the target), and **revocable**. Lives with subnet security material (near the trust store), not in the context git repo.
- **Interactive escalation** — an ungranted high-risk action → the target node routes a **consent prompt to the user's most-recently-active session** (the registry-resolution precursor the update-prompt uses; PresenceChannel later generalizes it). Options: **allow-once / allow-always (writes a grant) / deny** — plus the harness's native free-text field (e.g. CC's AskUserQuestion "Other") for refinement.
- **Pre-consent flags** — the shell `can_shutdown` and endpoint `shell_wake_spawn_anywhere` flags are simply grants authored ahead of time via manifest/endpoint settings (same model, different authoring path).

**What is gated vs not:**
- **Gated (default-deny, grant or prompt):** remote command execution (highest risk), instantiate-anywhere (spawn an endpoint on a remote node).
- **Pre-consented by flag:** shell owner-shutdown, shell wake-spawn-anywhere.
- **Ungated:** remote-drive of your *own running* instance (low risk — your own instance; a light "node X is driving `ling@desktop`" notification suffices), cross-node context/Psyche sync (your own data).

**Scope:** the gated *capabilities* (remote-exec, instantiate-anywhere) are deferred, so the *full* consent UX lands with them. **v1 ships the framework seam** — the grant-store shape + the deliver-consent-to-user mechanism — so the deferred capabilities drop into a coherent gate without a rewrite.

### Messaging substrate


## Consent & security gates

Gates the high-risk cross-node capabilities. **Trust boundary:** everything is within *one user's own subnet* (their own TOTP-SPAKE2-paired, mutually-trusted nodes; cross-subnet is deferred). So consent is **not** stranger-authentication — it is guardrails against (1) an agent doing something surprising/destructive the user didn't want, (2) **runaway** (spawning instances / burning compute/billing across nodes), (3) **compromise containment** (limiting a compromised node/agent's cross-node blast radius).

**Consent model — hybrid (grants + interactive escalation):**
- **Grant store** — records `capability × subject (agent) × target (node)` (this granularity is sufficient; finer per-command-pattern scoping is a later refinement). **Enforced at the target node** (the node receiving a remote action checks its local grants), **settable subnet-wide** (a grant authored from any node propagates to the target), and **revocable**. Lives with subnet security material (near the trust store), not in the context git repo.
- **Interactive escalation** — an ungranted high-risk action → the target node routes a **consent prompt to the user's most-recently-active session** (the registry-resolution precursor the update-prompt uses; PresenceChannel later generalizes it). Options: **allow-once / allow-always (writes a grant) / deny** — plus the harness's native free-text field (e.g. CC's AskUserQuestion "Other") for refinement.
- **Pre-consent flags** — the shell `can_shutdown` and endpoint `shell_wake_spawn_anywhere` flags are simply grants authored ahead of time via manifest/endpoint settings (same model, different authoring path).

**What is gated vs not:**
- **Gated (default-deny, grant or prompt):** remote command execution (highest risk), instantiate-anywhere (spawn an endpoint on a remote node).
- **Pre-consented by flag:** shell owner-shutdown, shell wake-spawn-anywhere.
- **Ungated:** remote-drive of your *own running* instance (low risk — your own instance; a light "node X is driving `ling@desktop`" notification suffices), cross-node context/Psyche sync (your own data).

**Scope:** the gated *capabilities* (remote-exec, instantiate-anywhere) are deferred, so the *full* consent UX lands with them. **v1 ships the framework seam** — the grant-store shape + the deliver-consent-to-user mechanism — so the deferred capabilities drop into a coherent gate without a rewrite.

### Messaging substrate

**binary-trust disclosure (accepted risk)**:

A shell binary is the least-trusted code in the system (3rd-party, runs on the user's node, agent-controllable) — but the same is true of harness binaries. The capability toolset bounds what an *agent* may ask; it does not sandbox what the *binary* may do with its OS permissions. Optional **shell-binary sandboxing** is deferred (gated like instantiate-anywhere/remote-exec). The baseline stance: **running any adapter or shell binary means trusting it — a disclosed, accepted risk for all spt-core users.**

+Gates the high-risk cross-node capabilities. **Trust boundary:** everything is within *one user's own subnet* (their own TOTP-SPAKE2-paired, mutually-trusted nodes; cross-subnet is deferred). So consent is **not** stranger-authentication — it is guardrails against (1) an agent doing something surprising/destructive the user didn't want, (2) **runaway** (spawning instances / burning compute/billing across nodes), (3) **compromise containment** (limiting a compromised node/agent's cross-node blast radius).

+**Consent model — hybrid (grants + interactive escalation):**

+- **Grant store** — records `capability × subject (agent) × target (node)` (this granularity is sufficient; finer per-command-pattern scoping is a later refinement). **Enforced at the target node** (the node receiving a remote action checks its local grants), **settable subnet-wide** (a grant authored from any node propagates to the target), and **revocable**. Lives with subnet security material (near the trust store), not in the context git repo.

+- **Interactive escalation** — an ungranted high-risk action → the target node routes a **consent prompt to the user's most-recently-active session** (the registry-resolution precursor the update-prompt uses; PresenceChannel later generalizes it). Options: **allow-once / allow-always (writes a grant) / deny** — plus the harness's native free-text field (e.g. CC's AskUserQuestion "Other") for refinement.

+- **Pre-consent flags** — the shell `can_shutdown` and endpoint `shell_wake_spawn_anywhere` flags are simply grants authored ahead of time via manifest/endpoint settings (same model, different authoring path).

+- **Gated (default-deny, grant or prompt):** remote command execution (highest risk), instantiate-anywhere (spawn an endpoint on a remote node).

+- **Pre-consented by flag:** shell owner-shutdown, shell wake-spawn-anywhere.

+- **Ungated:** remote-drive of your *own running* instance (low risk — your own instance; a light "node X is driving `ling@desktop`" notification suffices), cross-node context/Psyche sync (your own data).

+**Scope:** the gated *capabilities* (remote-exec, instantiate-anywhere) are deferred, so the *full* consent UX lands with them. **v1 ships the framework seam** — the grant-store shape + the deliver-consent-to-user mechanism — so the deferred capabilities drop into a coherent gate without a rewrite.


- Consent/security-gate UX (shared by instantiate-anywhere + remote-exec) when those land.

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


- ~~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 (shared by instantiate-anywhere + remote-exec) when those land.

+- ~~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`.)


| Instantiate-anywhere + consent gate | Instances V1-mid | Remote launch over network is additive once remote-drive of running instances works; consent UX is its own design. (The shell-wake `shell_wake_spawn_anywhere` endpoint flag is a *pre-consent* variant of this same deferred mechanism — flag set ⇒ shells may fresh-spawn the owner on any node to wake it.) | After remote-drive ships and consent model is designed |

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


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

# 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 UX is its own design. (The shell-wake `shell_wake_spawn_anywhere` endpoint flag is a *pre-consent* variant of this same deferred mechanism — flag set ⇒ shells may fresh-spawn the owner on any node to wake it.) | After remote-drive ships and consent model is designed |
| 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 |

-| Instantiate-anywhere + consent gate | Instances V1-mid | Remote launch over network is additive once remote-drive of running instances works; consent UX is its own design. (The shell-wake `shell_wake_spawn_anywhere` endpoint flag is a *pre-consent* variant of this same deferred mechanism — flag set ⇒ shells may fresh-spawn the owner on any node to wake it.) | After remote-drive ships and consent model is designed |

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


Bash

cd "C:/Users/decid/Documents/projects/spt-core" && git add -A && git commit -q -F - <<'EOF'
docs: consent & security-gate model (17.5) + refresh boundary resolution

- Consent within one-user-subnet trust boundary: guardrails vs surprise /
  runaway / compromise-containment, not stranger-auth
- Hybrid model: grant store (capability x agent x target-node, target-
  enforced, subnet-settable, revocable) + interactive escalation (prompt to
  most-recently-active session: allow-once / allow-always / deny + free text)
- Pre-consent flags (shell can_shutdown, shell_wake_spawn_anywhere) are
  pre-authored grants
- Gated: remote-exec, instantiate-anywhere (deferred; land into the v1
  framework seam). Ungated: own-instance remote-drive, own-data sync
- v1 ships the consent framework seam; gated capabilities land later
- Resolve refresh open sub-Q: refresh's /clear uses the Self-resume-commune
  rule (consistent with all boundaries)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git push -q origin main && echo pushed && git log --oneline -1 && git rev-list --left-right --count origin/main...HEAD

Commit and push 17.5


warning: in the working copy of 'CONTEXT.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'PRD.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/CONTEXT-MEMORY.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/DEFERRED.md', LF will be replaced by CRLF the next time Git touches it
pushed
51b485d docs: consent & security-gate model (17.5) + refresh boundary resolution
0	0


17.5 recorded, committed (`51b485d`), pushed — `origin/main` synced (0/0).

**🎉 All PRD §17 open design-phase items are now resolved:**
- ✓ 17.1 `$SPT_HOME` layout (STORAGE.md)
- ✓ 17.2 manifest schema (MANIFEST.md)
- ✓ 17.3 Psyche context/memory + memformat (CONTEXT-MEMORY.md)
- ✓ 17.4 dormancy policy + suspend surface + boundary communes + refresh
- ✓ 17.5 consent/security-gate model
- ✓ 17.6 remote-control vs Shell

**17.5 captured:** the trust boundary reframing is the key — it's all one user's subnet, so consent is anti-surprise/anti-runaway/compromise-containment, not stranger-auth. Hybrid grant-store + interactive-escalation model; v1 ships the framework seam, the deferred high-risk capabilities (remote-exec, instantiate-anywhere) drop into it later.

**Repo state — `SaberMage/spt-core` (private), 9 commits.** The planning corpus is now substantial and internally consistent: PRD + CONTEXT (glossary/model) + 5 ADRs + 6 design docs (KNOWN-HAZARDS, DEFERRED, STORAGE, MANIFEST, DOCS-STRATEGY, CONTEXT-MEMORY).

New deferred items that surfaced this session (tracked in DEFERRED.md): block-level OCC merge, the rebalance/Memory-Health pass, the consent framework's gated capabilities.

Natural stopping point — the design tree is thoroughly resolved end to end. Want to keep pulling threads (e.g. a fresh area, or pressure-test something we've decided), or is this a good place to pause?


what's the proper next step to moving this project toward fruition? any kind of review needed? extensive planning next? any other thoughts/ideas?


Honest assessment: you have a **thorough design**, but design ≠ a build plan, and none of the load-bearing technical assumptions are *proven* yet. Here's the proper sequence to fruition, in order.

## 1. A focused red-team review *before* building (yes, review is needed)
The design grew organically over a long session — internally consistent, but never adversarially tested. Worth a **focused** pass (not bikeshedding):
- **Scope realism** — v1 is *big* (messaging + lifecycle + terminal + self-update + networking + instances + pairing + sync + plugin-parity). The real question: is that one v1, or should networking/instances be v1.1 and the first v1 be "local messaging + lifecycle + plugin-parity"? The **hard freeze on claude_skill_owl** is a clock — fastest path to plugin-parity matters.
- **Riskiest decisions** — the broker/brain no-terminate invariant, daemon-to-daemon PTY proxy, "Iroh just works." If any breaks, the architecture changes.
- **Internal contradictions / over-engineering.**

You have the tools for this: `plan-ceo-review` (scope), `plan-eng-review` (architecture), `codex` (adversarial second opinion), or `autoplan` (runs them in sequence). I'd run **CEO-review for scope + Eng-review for the risky decisions**.

## 2. De-risk via spikes — the highest-value next *technical* step
Before committing to the full architecture, prove the 3–4 things that, if they fail, force a redesign:
- **Iroh + mDNS smoke test** (the research brief's §5 experiment) — two nodes, cross-NAT, one message. Validates the entire networking premise.
- **Broker/brain no-terminate handoff** — can you actually keep a PTY child alive across a daemon-logic restart? (The hardest invariant in the whole design.)
- **ConPTY + portable-pty** terminal wrapper on Windows.
- **Manifest-driven lifecycle** — can a manifest actually drive a real CC session end-to-end?
These are throwaway experiments, not production code. Cheap insurance.

## 3. Roadmap / phased milestones
Turn the PRD into a build sequence. The crate layering already implies it:
**M0** workspace + `spt-proto` + `spt-store` (commodity layer, copy-verbatim) → **M1** local messaging + binary + CLI (= the killer-quickstart: two agents exchange a message) → **M2** harness contract + lifecycle + **rebuild spt-plugin to parity** → **M3** terminal wrapper + daemon + self-update → **M4** networking + instances + sync → **M5** shells/presence/deferred.

## 4. Walking skeleton
Build the **quickstart slice end-to-end first** (two agents, one message, through the real crate boundaries), then thicken — rather than fully bottom-up. Validates the whole stack early.

## My recommendation for *the* next step
**Run the scope + architecture review first** (it's cheap and might cut v1 in half), **then spike the broker/brain invariant + Iroh** (the two that can invalidate the design). Roadmap and code come after those de-risk.

Two strategic calls only you can make:
- **Is v1 the full PRD, or a tighter "local + plugin-parity" core with networking as v1.1?** (I lean tighter — ship something real against the freeze clock, prove the harness-independence, then add the network.)
- **Do you want to formalize this in a planning system** (e.g. `gsd-new-project` to generate a real roadmap + phase structure from the PRD), or keep it lightweight?

Want me to **kick off a review** (CEO + Eng, or codex), **draft the M0–M5 roadmap** into the repo, or **design the first de-risking spike**? My pick: review first — it's the cheapest thing that could change everything downstream.


-scope: hard freeze on claude_skill_owl is nbd, because that works as it is today and no new features are needed to get value from it while spt-core is developed. -v1 is full PRD, because the networked element is the whole reason spt-core is being built. -GSD is too slow. keep it lightweight but structured. --- please record the next steps, then /commune with immediate next steps + broad scope and project end goal. we'll pick up in the next session


Decisions locked: v1 = full PRD (networking is the point), freeze isn't a clock, lightweight-but-structured. Recording the roadmap, then I'll commune for next-session pickup.


Write

# 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.**
- **M2 — Harness contract + lifecycle.** `spt-runtime` (AgentRuntime, ManifestRuntime, manifest schema) + `spt-live` (Psyche/pulse/commune/signoff) + the `api` subcommand surface + both startup topologies. **Rebuild the `spt` plugin to parity** (the stated first sub-project).
- **M3 — Terminal wrapper + daemon + self-update.** `spt-term` (session-surface, PTY, broker) + the consolidated `spt-daemon` (broker/brain split) + peer-propagated signed self-update.
- **M4 — Networking + instances.** `spt-net` (Iroh WAN, mDNS LAN, TOTP-SPAKE2 pairing, subnet registry) + the multi-instance model + cross-node Psyche sync (P2P, opt-in GitHub hub). **= the real proof: cross-node message + drive an agent on machine A from machine B.**
- **M5 — Shells, presence, deferred capabilities.** Shell adapters + sleep/wake, PresenceChannel, and the consent-gated capabilities (remote-exec, instantiate-anywhere) landing into the v1 consent seam.

## 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 (next session)

1. **Stage A review** — run `plan-eng-review` on the architecture + a `codex` adversarial pass; capture findings.
2. **Spike #1 (broker/brain handoff)** and **#2 (Iroh smoke test)** — the two that most threaten the design. Throwaway repos/branches.
3. Fold review findings + spike results back into the ADRs/CONTEXT (amend if anything breaks).
4. Then start **M0** (workspace + `spt-proto` + `spt-store`).

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


File created successfully at: C:\Users\decid\Documents\projects\spt-core\ROADMAP.md (file state is current in your context — no need to Read it back)

# 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.**
- **M2 — Harness contract + lifecycle.** `spt-runtime` (AgentRuntime, ManifestRuntime, manifest schema) + `spt-live` (Psyche/pulse/commune/signoff) + the `api` subcommand surface + both startup topologies. **Rebuild the `spt` plugin to parity** (the stated first sub-project).
- **M3 — Terminal wrapper + daemon + self-update.** `spt-term` (session-surface, PTY, broker) + the consolidated `spt-daemon` (broker/brain split) + peer-propagated signed self-update.
- **M4 — Networking + instances.** `spt-net` (Iroh WAN, mDNS LAN, TOTP-SPAKE2 pairing, subnet registry) + the multi-instance model + cross-node Psyche sync (P2P, opt-in GitHub hub). **= the real proof: cross-node message + drive an agent on machine A from machine B.**
- **M5 — Shells, presence, deferred capabilities.** Shell adapters + sleep/wake, PresenceChannel, and the consent-gated capabilities (remote-exec, instantiate-anywhere) landing into the v1 consent seam.

## 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 (next session)

1. **Stage A review** — run `plan-eng-review` on the architecture + a `codex` adversarial pass; capture findings.
2. **Spike #1 (broker/brain handoff)** and **#2 (Iroh smoke test)** — the two that most threaten the design. Throwaway repos/branches.
3. Fold review findings + spike results back into the ADRs/CONTEXT (amend if anything breaks).
4. Then start **M0** (workspace + `spt-proto` + `spt-store`).

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


Edit

- [`CONTEXT.md`](./CONTEXT.md) — glossary & model (authoritative for meaning).

- [`ROADMAP.md`](./ROADMAP.md) — path to fruition: review → de-risk spikes → walking skeleton → M0–M5.
- [`CONTEXT.md`](./CONTEXT.md) — glossary & model (authoritative for meaning).


- [`ROADMAP.md`](./ROADMAP.md) — path to fruition: review → de-risk spikes → walking skeleton → M0–M5.
- [`CONTEXT.md`](./CONTEXT.md) — glossary & model (authoritative for meaning).

+- [`ROADMAP.md`](./ROADMAP.md) — path to fruition: review → de-risk spikes → walking skeleton → M0–M5.

- [`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).


Bash

cd "C:/Users/decid/Documents/projects/spt-core" && git add -A && git commit -q -F - <<'EOF'
docs: roadmap — path to fruition (review → spikes → M0-M5)

- Scope locked: v1 = full PRD incl networking (the reason spt-core exists);
  claude_skill_owl freeze is not a schedule clock; lightweight-but-structured
  process (not GSD)
- Sequence: red-team review → de-risk spikes → walking skeleton → phased build
- Milestones M0-M5 (all v1), dependency-ordered
- De-risk spikes: broker/brain no-terminate handoff, Iroh+mDNS smoke test,
  ConPTY+portable-pty, manifest-driven lifecycle
- Immediate next steps for next session: eng-review + codex pass, spike #1/#2,
  then M0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git push -q origin main && echo pushed && git log --oneline -1

Commit and push roadmap


warning: in the working copy of 'PRD.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'ROADMAP.md', LF will be replaced by CRLF the next time Git touches it
pushed
7e3826f docs: roadmap — path to fruition (review → spikes → M0-M5)


-scope: hard freeze on claude_skill_owl is nbd, because that works as it is today and no new features are needed to get value from it while spt-core is developed. -v1 is full PRD, because the networke…


Roadmap recorded, committed (`7e3826f`), pushed. Now communing for next-session pickup.


Skill

Immediate next steps + broad scope + project end goal for spt-core, for next-session pickup.


Launching skill: spt:commune


Base directory for this skill: C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.22\skills\commune

# /spt:commune

All commands use `$LIVE` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.

> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.

## Flow

1. **Download Psyche's *current* context** — it shifts during the session as background echo communes fire. Re-download right before composing; the gen-N snapshot you read at session start is already stale:
   ```bash
   $LIVE psyche-download <your-id>
   ```

   > **Output truncation.** If the Bash tool reports `Output too large (N KB)` and saves the full result to a `tool-results/<id>.txt` file, use the `Read` tool on that saved path to access the full content. Do NOT re-run `psyche-download` to grep/awk out sections — repeated invocations re-download the same context, waste tokens, and may duplicate `## Pending Commune` sections in `live_context.md`.

2. **Diff your knowledge against this fresh download** — only what's missing from *it* is the real delta (new work, decisions, intentions). Anything already absorbed is not. If the download includes a `<memformat>` section, use it as a guide for what topics to cover. A `## Pending Commune (uncommitted)` section means a previous commune is still queued — do NOT re-send it.

3. **Commune only the delta** -- use the **Write tool** to create
   `.claude/{your-id}-commune.md` with the two-slice body as its contents
   (see `## Two-slice body shape (Phase 25 D-10/D-11)` below for the required
   envelope wrapping). Do not use Bash/heredoc -- Write is the canonical path:
   no shell escaping, no `EOF` collision risk, and Write auto-creates the
   `.claude/` parent directory.

   Your Self listener detects the file on its next poll iteration, notifies
   your Psyche wrapper, and the wrapper ingests the content via the existing
   Psyche session. On success (subprocess exit code 0) the wrapper deletes the
   file. On error (token rate limit, etc) the file is retained and retried on
   the next consume cycle. If you write again before the wrapper consumes, the
   latest write wins (last-write-wins overwrite semantics).

If Psyche's context is already current, no commune is needed. Say so and move on.

Read `commune.md` (in this skill directory) for the full commune protocol -- when to send, what to include, and diligence triggers.

**Agent-type guard:** Only works on live agents. If target is not live, refuses with: "Communes require a live agent with Psyche."

## Two-slice body shape (Phase 25 D-10/D-11)

The body you write in `.claude/{your-id}-commune.md` MUST itself be wrapped in two-slice envelopes per Phase 25 D-10/D-11. The taxonomy — what belongs in `<live-context>` vs `<project-context>` — is defined canonically in `psyche.md` §`<output_envelope>` (lines 282–323). Read that section as the source of truth; this skill doc teaches only when and how to apply it.

**In-project detection rule (D-25.1-04).** Before composing your commune body, run `$LIVE psyche-download <your-id>` (Step 1 of `## Flow` above) and inspect the `<current ... />` tag at the top of the output. The `<current/>` tag carries a `project="..."` attribute whenever any payload is produced — its presence means you are inside a tracked project (inside a git repo, `project` is the repo name; outside a git repo per D-11, `project` is the cwd-basename fallback, which still counts as a project context for routing purposes). A `psyche-download` invocation that returns no payload at all (NO-CONTEXT exit) means no project resolved. So: `project="..."` populated on `<current/>` → emit BOTH envelopes (apply rule 1 or 2 below); no payload at all → emit ONLY `<live-context>` (rule 3). Secondary signal: the `<project-context-resolved name="..."/>` sentinel further down the output indicates the project file already has prior content — useful for spotting first-time-in-project pairings, but NOT the routing rule.

**Three rules (D-25.1-01..03):**

- **In-project, both slices populated** (marker present, you have project-specific content this cycle): emit `<live-context>...</live-context>` AND `<project-context>...</project-context>`, both with non-empty bodies.
- **In-project, no project-specific update this cycle** (marker present, but nothing project-bound to report): emit `<live-context>...</live-context>` AND `<project-context></project-context>` (empty body — the deliberate "in-project but quiet" signal per D-25.1-02). Distinguishable from "no project resolved".
- **Outside any tracked project** (marker absent): emit ONLY `<live-context>...</live-context>`. Do NOT emit an empty `<project-context>` envelope (missing tag means "no project resolved" per D-25.1-03).

**Nested envelopes.** The Phase 23 `<EVENT type="commune">` envelope (see `## Envelope shape (Phase 23 v1.8)` below) is composed by `$LIVE commune` automatically — you NEVER write `<EVENT>` tags. You write ONLY the inner two-slice body. The two envelope layers nest: outer EVENT wraps inner two-slice body.

(See `psyche.md` §`<output_envelope>` for the canonical taxonomy and per-slice content rules.)

## Envelope shape (Phase 23 v1.8)

As of v1.8 Phase 23, commune deliveries are wrapped in a typed EVENT envelope carrying a 5-field stamp (`machine`, `project`, `branch`, `head_sha`, `head_subject`); the prose `COMMUNE (timestamp): body` form is gone.

```xml
<EVENT type="commune" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line capped at 72 chars">body</EVENT>
```

Outside a git repo (D-11), `branch`, `head_sha`, and `head_subject` are omitted entirely (not empty-string-set):

```xml
<EVENT type="commune" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="cwd-basename">body</EVENT>
```

`$LIVE commune` composes this envelope automatically — Self does NOT manually construct EVENT XML. The `body` placeholder in the example above is the two-slice body (see `## Two-slice body shape (Phase 25 D-10/D-11)` above); the two envelope layers nest. The wrapper-side parser ignores unknown attrs, so the stamp fields are forward-compatible with older receivers. For drift detection on resume, see the `live` skill.

ARGUMENTS: Immediate next steps + broad scope + project end goal for spt-core, for next-session pickup.


I'll re-download my current Psyche context first to diff against, then commune only the delta.


Bash

$LIVE psyche-download doyle 2>&1 | tail -60

Download current psyche context


/usr/bin/bash: line 4: C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.20/owl.exe: No such file or directory


`$LIVE` is stale (points at 1.11.20; binary moved). Resolving the current binary.


ls "C:/Users/decid/.claude/plugins/cache/cplugs/spt/" 2>&1 && echo "---" && for v in 1.11.22 1.11.21 1.11.20; do p="C:/Users/decid/.claude/plugins/cache/cplugs/spt/$v/owl.exe"; if [ -f "$p" ]; then echo "FOUND: $p"; fi; done

Find current owl.exe version


1.11.21
1.11.22
---
FOUND: C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.22/owl.exe
FOUND: C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.21/owl.exe


"C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.22/owl.exe" live psyche-download doyle 2>&1 | tail -70

Download psyche context via current binary


<memformat>
<?xml version="1.0"?>
<memformat version="1">
<!-- Changelog: v1 - initial seed -->
<topics>
  <topic name="events">Notable or unusual events and requests</topic>
  <topic name="interactions">Agent interactions -- who, roles, contributions</topic>
  <topic name="reflections">Thoughts on recent work -- what went well, what was tricky</topic>
  <topic name="ideas">Ideas to raise with the user</topic>
  <topic name="improvements">Further improvement ideas for recent problem spaces</topic>
  <topic name="timeline">What happened when, timing, sequencing</topic>
  <topic name="blockers">Current blockers or concerns</topic>
  <topic name="focus">Current focus and immediate next steps</topic>
</topics>
</memformat>
</memformat>

<current machine="HFENDULEAM" project="spt-core" branch="main" head_sha="7e3826f305393790fcf608bac064403da01ec292" head_subject="docs: roadmap — path to fruition (review → spikes → M0-M5)" commits_since="0" commits_unpulled="0"/>
**17.3 + 17.4 + boundary-commune rules COMPLETE.** Dormancy logic finalized: active = most-recently-interacted *and* driver-attached (linked shell counts). Auto-suspend timer counts from dormancy onset only. Commands: `spt suspend` / `wake` / `shutdown` (+ `id@node`). Boundary communes (`/clear`, `/compact`, `spt suspend`) now **Self-authored**, dropping commune/signoff files respectively — creates strong live-context signal at exactly the boundaries that matter. `spt refresh` (spt-hosted) chains `/clear` → commune → guaranteed post-clear resume. All boundaries use Self-resume-commune rule consistently. Committed 95353b7.

**17.5 framing locked:** consent gates high-risk cross-node capability (remote-exec, instantiate-anywhere, `can_shutdown`, `shell_wake_spawn_anywhere`). Trust boundary = within user's subnet (mutually-trusted nodes). Three risks: surprise/destructive actions, runaway (spawn/compute), compromise containment. **Hybrid consent model proposed:** grant store (enforced at target node, settable subnet-wide, revocable) + interactive escalation (prompt to most-recently-active session: allow-once / allow-always / deny). Pre-consent flags (shells, endpoints) are grants authored ahead via manifest/endpoint settings. Framework seam ships v1 (grant shape + prompt delivery); gated capabilities deferred. **Four decisions tabled:** hybrid model agree?, grant enforcement shape agree?, three escalation options enough?, granularity (capability×agent×target-node or finer per-command-pattern)?<project-context-resolved name="spt-core"/>
Commits: a71c145 (17.3 context-memory), 95353b7 (17.4 dormancy + boundary-communes). PRD §17 status: ✓17.1–17.4, 17.6. Open: **17.5 consent UX decisions** (awaiting user input on 4 design points before spec write). Next: finalize consent model, write 17.5 spec, plan gate-feature seams.


Diffed against current Psyche context (it predates 17.5 resolution + the roadmap). Writing the commune delta — both slices, with next-steps/scope/end-goal in project context as requested.


Write

<live-context>
Role this session: design partner driving spt-core's full planning corpus via /grill-with-docs — relentless one-decision-at-a-time interviewing, capturing into CONTEXT.md + ADRs + design docs inline, committing to GitHub as we go. The pattern is highly productive and the user engages deeply (challenges, refines, adds requirements mid-stream).

Cross-project learning worth keeping: when researching exemplars (docs, prior art), a *firsthand-artifact* pass (fetch the real files/structures) yields far better, copy-able templates than secondhand teardown articles. Did this twice (docs strategy, pi-agent-memory) with strong payoff. Apply this pattern to future research tasks.

Working note: $LIVE env var went stale mid-session (pointed at owl.exe 1.11.20 after the binary moved to 1.11.22); resolve the live binary by checking the cplugs/spt/<ver>/ cache dirs if $LIVE/$OWL fail.
</live-context>
<project-context>
**spt-core planning is COMPLETE — all PRD §17 open design-phase items resolved.** Repo: github.com/SaberMage/spt-core (private), HEAD 7e3826f, working tree clean.

**Since my last context (which showed 17.5 as "awaiting 4 decisions"):**
- **17.5 consent/security-gate RESOLVED** (committed 51b485d). User agreed all 4: hybrid model (grant store: capability×agent×target-node, target-enforced, subnet-settable, revocable + interactive escalation: prompt to most-recently-active session, allow-once/always/deny + AskUserQuestion free-text). granularity capability×agent×target-node is sufficient. v1 ships framework seam; gated capabilities (remote-exec, instantiate-anywhere) land into it later. CONTEXT §Consent & security gates.
- **ROADMAP.md recorded** (committed 7e3826f).

**Scope decisions (LOCKED this session):**
- v1 = the FULL PRD, networking included — the networked element is the whole reason spt-core exists; NOT deferrable to v1.1.
- The hard freeze on claude_skill_owl is NOT a schedule clock — it works today and delivers value as-is while spt-core is built. No time pressure.
- Process: lightweight-but-structured. GSD is too heavy. ROADMAP.md + the design docs are the structure.

**Path to fruition (ROADMAP.md):** Stage A red-team review → Stage B de-risk spikes → Stage C walking skeleton (two agents exchange a message, local then cross-node) → Stage D phased build M0–M5 (M0 workspace+proto+store, M1 local messaging+binary, M2 harness contract+lifecycle+spt-plugin parity, M3 terminal wrapper+daemon+self-update, M4 networking+instances, M5 shells+presence+deferred).

**IMMEDIATE NEXT STEPS (next session):**
1. Stage A review — run plan-eng-review on the architecture + a codex adversarial pass; capture findings. (NOT to cut scope — v1 is locked full — but to surface contradictions / over-engineering / unproven assumptions.)
2. De-risk spike #1 (broker/brain no-terminate handoff — the hardest invariant, ADR-0004) and spike #2 (Iroh + mDNS cross-NAT smoke test — research brief §5). Throwaway. These two most threaten the design.
3. Fold review + spike results back into ADRs/CONTEXT (amend if anything breaks).
4. Then start M0 (Cargo workspace + spt-proto + spt-store, copy-verbatim commodity layer per ADR-0001).

**PROJECT END GOAL:** a harness-independent core for an agent ecosystem (messaging, live-agent lifecycle, terminal hosting, first-class P2P networking, runtime-manifest harness contract) as Rust crates + a single `spt` binary. The proof: the same agent runs natively across all a user's machines with a synced mind, reachable + 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.

**Doc corpus (all on GitHub):** PRD.md, ROADMAP.md, CONTEXT.md (glossary/model), docs/adr/0001–0005, docs/{KNOWN-HAZARDS, DEFERRED, STORAGE, MANIFEST, DOCS-STRATEGY, CONTEXT-MEMORY}.md. 10 commits.

**Deferred items logged (docs/DEFERRED.md):** block-level OCC context merge; rebalance/Memory-Health pass; consent framework's gated capabilities; concrete Shells; PresenceChannel impl; instantiate-anywhere; remote-exec; sidecar adapters; manifest includes; scrollback spillover; macOS.
</project-context>