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

  **Live ledger** (full task detail: `RESTORATION-D2-PLAN.md`):

  | # | Loop / consumer | Verb needed | Runs-where now | Status |
  |---|---|---|---|---|
  | D2-1 | Net consumers — inbound dispatcher (`dispatch.rs`) + outbound peer pump (`peerloop.rs`) | **none** — already pure IPC clients (`Brain::cold_start` over `broker_socket_name()`); gated on the existing `net-status` verb | **brain** ✅ | DONE |
  | D2-2 | Digest **parse** feed (`brain.run_digest_feed`) — the per-session projection that populates the hub | existing `subscribe`/output stream; the parse is already brain-side logic, only its *spawn site* (broker boot) moves | broker | TODO |
  | D2-3 | Shellwake reconcile (`shellwake::spawn_wake_host`) | **stays brain-owned** by exception (Q5): watcher children re-reconciled from disk on brain start | broker | TODO (move spawn) |
  | D2-4 | Psyche / pulse loops (`lifecycle.rs`) | per-loop verbs, additive (KH-2.3) | broker | TODO |
  | — | Net **bring-up** + `net_retry_attach` self-heal (REQ-DAEMON-9) | n/a | **broker** (stays — Q5, NetHost is broker-owned) | KEEP |
  | — | Digest **hub serve** (`digesthub::serve_digest_control`) | n/a | **broker** (stays — Q5, live projections + subscriber sockets) | KEEP |
  | — | Seed-control lock + liveness (`seedmap::serve_seed_control`) | n/a | **broker** (stays — Q2 anchor; D3) | KEEP |

  **D2-1 N-1 note (no-verb migration).** The consumers spoke pure IPC already;
  this commit only relocates their *spawn site* (broker process → brain child).
  No verb signature changed, so the verb-surface N-1 window is untouched. The
  one transient hazard — an old broker (pre-D2-1, spawns consumers in-process)
  paired with a new brain (also spawns them) → **double-spawn** — cannot occur:
  broker and brain are the same binary and swap together until D3 introduces
  brain-only swaps, by which point both halves are post-migration. Recorded so
  D3's first brain-only swap lands on an already-relocated consumer set.

  **Live ledger** (full task detail: `RESTORATION-D2-PLAN.md`).

  *Scope correction (D2-2 investigation, 2026-06-09).* An audit of what
  actually runs in the broker process (`Daemon::run`) found the migration list
  is **shorter than the design's loop inventory suggested**: only two logic
  loops are genuinely broker-resident in the production spt-core daemon. The
  rest of the design's list (digest parse, psyche/pulse) was never wired into
  this daemon — they are unwired primitives / legacy-listener-owned, so there
  is nothing to *relocate*. D2 therefore = two cheap pure-IPC spawn-site moves,
  both now done.

  | # | Loop / consumer | Verb needed | Runs-where | Status |
  |---|---|---|---|---|
  | D2-1 | Net consumers — inbound dispatcher (`dispatch.rs`) + outbound peer pump (`peerloop.rs`) | **none** — already pure IPC clients (`Brain::cold_start` over `broker_socket_name()`); gated on the existing `net-status` verb | **brain** ✅ | DONE |
  | D2-2 | Shellwake reconcile (`shellwake::spawn_wake_host`) — offline-shell wake watchers | **none** — pure disk-reconciler + `Brain::cold_start` remote-wake dial; brain-owned by Q5 exception (re-reconciled from disk on brain start) | **brain** ✅ | DONE |
  | — | Net **bring-up** + `net_retry_attach` self-heal (REQ-DAEMON-9) | n/a | **broker** (stays — Q5, NetHost is broker-owned) | KEEP |
  | — | Digest **hub serve** (`digesthub::serve_digest_control`) | n/a | **broker** (stays — Q5, live projections + subscriber sockets) | KEEP |
  | — | Seed-control lock + liveness (`seedmap::serve_seed_control`) | n/a | **broker** (stays — Q2 anchor; D3) | KEEP |
  | n/a | Digest **parse** feed (`brain.run_digest_feed`) | future new verb (brain→broker hub publish over IPC) | **neither** — unwired primitive, no production spawn site; broker's hub is empty in prod | NOT-RESIDENT (future wiring, not a D2 relocation) |
  | n/a | Psyche / pulse loops (`lifecycle.rs`) | n/a | **legacy listener** — not hosted in the spt-core daemon yet (arrives with the live-agent adapter) | NOT-RESIDENT |

  **D2-close invariant (reframed, satisfied):** every logic loop *that actually
  runs in the broker process* now runs in the brain; the broker holds only
  continuity resources (NetHost / digest hub serve / seed-control / PTY masters
  / `EffectJournal`) + the verb server. The digest-parse → hub cross-process
  publish (a new additive verb) and the daemon-hosted psyche loops are
  **forward** wiring for the live-agent buildout, not relocations of D1-left
  loops — tracked there, out of D2.

  **N-1 note (no-verb migrations).** Both D2-1 and D2-2 only relocate a *spawn
  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — it stands up against the
  existing verb set). The one transient hazard — an old broker that still
  spawns these loops in-process paired with a new brain that also spawns them →
  **double-spawn** — cannot occur: broker and brain are the same binary and swap
  together until D3 introduces brain-only swaps, by which point both halves are
  post-migration. Recorded so D3's first brain-only swap lands on an
  already-relocated loop set.

every mid-migration commit. **D2-close invariant:** every logic loop runs in

the brain; the broker holds only continuity resources + the verb server.

-  **Live ledger** (full task detail: `RESTORATION-D2-PLAN.md`):

+  **Live ledger** (full task detail: `RESTORATION-D2-PLAN.md`).

-  | # | Loop / consumer | Verb needed | Runs-where now | Status |

+  *Scope correction (D2-2 investigation, 2026-06-09).* An audit of what

+  actually runs in the broker process (`Daemon::run`) found the migration list

+  is **shorter than the design's loop inventory suggested**: only two logic

+  loops are genuinely broker-resident in the production spt-core daemon. The

+  rest of the design's list (digest parse, psyche/pulse) was never wired into

+  this daemon — they are unwired primitives / legacy-listener-owned, so there

+  is nothing to *relocate*. D2 therefore = two cheap pure-IPC spawn-site moves,

+  | # | Loop / consumer | Verb needed | Runs-where | Status |

-  | D2-2 | Digest **parse** feed (`brain.run_digest_feed`) — the per-session projection that populates the hub | existing `subscribe`/output stream; the parse is already brain-side logic, only its *spawn site* (broker boot) moves | broker | TODO |

-  | D2-3 | Shellwake reconcile (`shellwake::spawn_wake_host`) | **stays brain-owned** by exception (Q5): watcher children re-reconciled from disk on brain start | broker | TODO (move spawn) |

-  | D2-4 | Psyche / pulse loops (`lifecycle.rs`) | per-loop verbs, additive (KH-2.3) | broker | TODO |

+  | D2-2 | Shellwake reconcile (`shellwake::spawn_wake_host`) — offline-shell wake watchers | **none** — pure disk-reconciler + `Brain::cold_start` remote-wake dial; brain-owned by Q5 exception (re-reconciled from disk on brain start) | **brain** ✅ | DONE |

+  | n/a | Digest **parse** feed (`brain.run_digest_feed`) | future new verb (brain→broker hub publish over IPC) | **neither** — unwired primitive, no production spawn site; broker's hub is empty in prod | NOT-RESIDENT (future wiring, not a D2 relocation) |

+  | n/a | Psyche / pulse loops (`lifecycle.rs`) | n/a | **legacy listener** — not hosted in the spt-core daemon yet (arrives with the live-agent adapter) | NOT-RESIDENT |

-  **D2-1 N-1 note (no-verb migration).** The consumers spoke pure IPC already;

-  this commit only relocates their *spawn site* (broker process → brain child).

-  No verb signature changed, so the verb-surface N-1 window is untouched. The

-  one transient hazard — an old broker (pre-D2-1, spawns consumers in-process)

-  paired with a new brain (also spawns them) → **double-spawn** — cannot occur:

-  broker and brain are the same binary and swap together until D3 introduces

-  brain-only swaps, by which point both halves are post-migration. Recorded so

-  D3's first brain-only swap lands on an already-relocated consumer set.

+  **D2-close invariant (reframed, satisfied):** every logic loop *that actually

+  runs in the broker process* now runs in the brain; the broker holds only

+  continuity resources (NetHost / digest hub serve / seed-control / PTY masters

+  / `EffectJournal`) + the verb server. The digest-parse → hub cross-process

+  publish (a new additive verb) and the daemon-hosted psyche loops are

+  **forward** wiring for the live-agent buildout, not relocations of D1-left

+  **N-1 note (no-verb migrations).** Both D2-1 and D2-2 only relocate a *spawn

+  site*; no verb signature changed, so the verb-surface N-1 window is untouched

+  (V6's CI compat test has nothing new to assert yet — it stands up against the

+  existing verb set). The one transient hazard — an old broker that still

+  spawns these loops in-process paired with a new brain that also spawns them →

+  **double-spawn** — cannot occur: broker and brain are the same binary and swap

+  together until D3 introduces brain-only swaps, by which point both halves are

+  post-migration. Recorded so D3's first brain-only swap lands on an

- **Audit** every current in-process `Arc<Broker>` consumer; convert each

direct call to a versioned verb. Per §Q5:

- **Net bring-up + REQ-DAEMON-9 self-heal retry → broker** — *near-free*:


Now update RESTORATION-D2-PLAN.md to match the reframed scope.


Edit

## D2-2..D2-4 — remaining loops (TODO)

- **D2-2 digest parse feed** → brain. The parse (`Brain::run_digest_feed`) is
  already brain-side logic; the hub serve stays broker-side (Q5). Move the
  spawn that wires a hosted session's feed into the brain. The hub itself is a
  broker continuity resource — the brain pushes parsed digests over IPC.
- **D2-3 shellwake** → brain by exception (Q5): watcher children stay
  brain-owned, re-reconciled from disk on brain start (the boot sweep already
  does orphan cleanup). Move `spawn_wake_host` into the brain.
- **D2-4 psyche / pulse loops** (`lifecycle.rs`) → brain, each new direct call a
  versioned additive verb. The pulse loop's phase-significant timing is D5's
  durable-deadline conversion — D2 only moves the loop; D5 makes its clock
  swap-durable.

## D2-2 — Shellwake wake-host → brain  ✅ DONE

`shellwake::spawn_wake_host` is the offline half of shell online/offline
exclusivity: a 5 s reconcile sweep that runs each offline instance's
`wake_command` as a supervised watcher child. Same cheap shape as D2-1 — it
takes only a `stop` flag (no shared `Arc<Broker>`) and reaches the broker only
over the same socket IPC (`Brain::cold_start`) for the remote-wake dial. Q5
names it a **brain-owned** exception; its boot sweep re-reconciles the watcher
children from disk on every brain start, so a brain respawn re-sweeps orphaned
watchers — exactly the Q5 posture.

Changes:
- `brainproc.rs`: `run_brain` spawns the wake host once at brain start (before
  the heartbeat loop), unconditionally (no net dependency).
- `daemon.rs`: drop the broker-side `spawn_wake_host` block.

Evidence: `[impl->REQ-SHELL-2]` + `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`.

## Scope correction — what is NOT a D2 relocation

A `Daemon::run` audit (D2-2) found the design's loop inventory over-counted what
the production spt-core daemon actually hosts:

- **Digest parse feed** (`Brain::run_digest_feed`): has **no production spawn
  site** — `ingest_chunk` is reached only from it, and the broker's `DigestHub`
  is empty in prod. It is an unwired brain-side primitive. Wiring it (the brain
  pushes parsed digests to the broker's hub over a **new additive verb**) is
  forward live-agent work, not a relocation of a D1-left loop.
- **Psyche / pulse loops** (`lifecycle.rs`): not spawned in `Daemon::run` at all
  — the live-agent loops still run in the legacy listener (owl.exe). They arrive
  in the spt-core daemon with the live-agent adapter buildout, on the post-D7
  final topology. The pulse loop's swap-durable timing is D5's concern when it
  does land here.

So D2's structural relocation is **complete** after D2-1 + D2-2: every logic
loop that actually runs in the broker process now runs in the brain.


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

## D2-2 — Shellwake wake-host → brain  ✅ DONE

`shellwake::spawn_wake_host` is the offline half of shell online/offline
exclusivity: a 5 s reconcile sweep that runs each offline instance's
`wake_command` as a supervised watcher child. Same cheap shape as D2-1 — it
takes only a `stop` flag (no shared `Arc<Broker>`) and reaches the broker only
over the same socket IPC (`Brain::cold_start`) for the remote-wake dial. Q5
names it a **brain-owned** exception; its boot sweep re-reconciles the watcher
children from disk on every brain start, so a brain respawn re-sweeps orphaned
watchers — exactly the Q5 posture.

Changes:
- `brainproc.rs`: `run_brain` spawns the wake host once at brain start (before
  the heartbeat loop), unconditionally (no net dependency).
- `daemon.rs`: drop the broker-side `spawn_wake_host` block.

Evidence: `[impl->REQ-SHELL-2]` + `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`.

## Scope correction — what is NOT a D2 relocation

A `Daemon::run` audit (D2-2) found the design's loop inventory over-counted what
the production spt-core daemon actually hosts:

- **Digest parse feed** (`Brain::run_digest_feed`): has **no production spawn
  site** — `ingest_chunk` is reached only from it, and the broker's `DigestHub`
  is empty in prod. It is an unwired brain-side primitive. Wiring it (the brain
  pushes parsed digests to the broker's hub over a **new additive verb**) is
  forward live-agent work, not a relocation of a D1-left loop.
- **Psyche / pulse loops** (`lifecycle.rs`): not spawned in `Daemon::run` at all
  — the live-agent loops still run in the legacy listener (owl.exe). They arrive
  in the spt-core daemon with the live-agent adapter buildout, on the post-D7
  final topology. The pulse loop's swap-durable timing is D5's concern when it
  does land here.

So D2's structural relocation is **complete** after D2-1 + D2-2: every logic
loop that actually runs in the broker process now runs in the brain.

# Restoration D2 — IPC verb surface + loop migration (task plan)

> Working doc for RESTORATION-PLAN.md **D2** (ADR-0018 Q5/V6). The canonical
> loop→verb→moves-at-commit ledger lives in RESTORATION-PLAN.md D2; this doc
> holds the per-commit task detail. D1 (process-split skeleton) is DONE +
> cross-OS CI-green on main (0c95435).

## Goal (D2-close invariant)

Every logic loop runs in the **brain** child; the broker holds only continuity
resources (PTY masters / harness children / `EffectJournal` / NetHost / digest
hub / seed-control) + the verb server. D1 left every loop in the broker process;
D2 relocates them ONE PER COMMIT.

## Per-commit discipline

1. Define the loop's versioned verb (additive/defaulted — KH-2.3) **if** it made
   a direct `Arc<Broker>` call. A consumer that already spoke pure IPC needs no
   new verb — only its spawn site moves.
2. Reroute the direct call over the socket.
3. Move the loop into the brain child.
4. Prove the N-1 window (old-broker × new-brain) holds for the verb.

Gates every commit: `cargo build` · `cargo test` · `cargo clippy` ·
`cargo build --no-default-features` · `traceable-reqs check` (EXIT=0) ·
`xtask check`. Push to a dev-freeform branch → CI both runners before any tag.

---

## D2-1 — Net consumers → brain  ✅ DONE

**Cheapest first** (D1 discovery): the inbound dispatcher (`dispatch.rs`) and
the outbound peer pump (`peerloop.rs`) are ALREADY standalone IPC clients — each
`Brain::cold_start`s its own connection over `broker_socket_name()`, holds no
shared `Arc<Broker>`. They were merely *spawned* in the broker process for
convenience (`daemon.rs::spawn_net_consumers`). The migration is moving that
spawn site into the brain. **No new verb.**

Net-gating: consumers are meaningful only with a NetHost up. The brain polls the
existing `net-status` verb (answered `enabled:false` on a net-less broker, and
it carries `node_id_hex`) and starts the consumers exactly once, when net first
reports enabled — which also covers the boot-race self-heal (REQ-DAEMON-9): the
broker still owns the NetHost bind retry + `attach_net`, and the brain notices
`enabled` flip and starts consumers then. The broker's `net_retry_attach` keeps
the bind retry, drops the consumer spawn.

Why this is strictly better than today: consumers used to live in the broker, so
a (future) brain restart would not touch them and a broker restart killed them;
now they live with the brain, restart with it, and a brain respawn re-spawns
them from the swapped binary — the intended topology.

Changes:
- `brainproc.rs`: brain heartbeat polls `net-status`; on the first `enabled`
  reply, spawn the net consumers (moved `spawn_net_consumers` here, brain-side)
  with `node_id_hex` from the reply. `net-status` doubles as the broker-liveness
  probe (replaces the `sessions()` probe). `consumer_gate` pure helper enforces
  spawn-exactly-once.
- `daemon.rs`: drop both `spawn_net_consumers` call sites (boot `net_up` branch
  + inside `net_retry_attach`); `net_retry_attach` keeps the NetHost bind retry
  + `attach_net`, no longer spawns consumers (param `node_hex` retired).
- unit: `consumer_gate` once-only + net-gating table.

Evidence: `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]` (brain hosts the
consumers; broker no longer does) · `[unit->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`
(the gate table). REQ already `[doc,impl,unit]` active — no toml change.

---

## D2-2..D2-4 — remaining loops (TODO)

- **D2-2 digest parse feed** → brain. The parse (`Brain::run_digest_feed`) is
  already brain-side logic; the hub serve stays broker-side (Q5). Move the
  spawn that wires a hosted session's feed into the brain. The hub itself is a
  broker continuity resource — the brain pushes parsed digests over IPC.
- **D2-3 shellwake** → brain by exception (Q5): watcher children stay
  brain-owned, re-reconciled from disk on brain start (the boot sweep already
  does orphan cleanup). Move `spawn_wake_host` into the brain.
- **D2-4 psyche / pulse loops** (`lifecycle.rs`) → brain, each new direct call a
  versioned additive verb. The pulse loop's phase-significant timing is D5's
  durable-deadline conversion — D2 only moves the loop; D5 makes its clock
  swap-durable.

## V6 — N-1 compat test scaffold (build in D2, gate in D7)

A CI-real old-broker-binary × new-brain-binary exercise across the whole verb
surface. Scaffold the harness here (it has no teeth until a verb signature
changes); D7 makes it a green gate. D2-1 added no verb, so the harness has
nothing to assert yet — stand it up against the existing verb set so each later
D2 verb extends it.

+## D2-2 — Shellwake wake-host → brain  ✅ DONE

-- **D2-2 digest parse feed** → brain. The parse (`Brain::run_digest_feed`) is

-  already brain-side logic; the hub serve stays broker-side (Q5). Move the

-  spawn that wires a hosted session's feed into the brain. The hub itself is a

-  broker continuity resource — the brain pushes parsed digests over IPC.

-- **D2-3 shellwake** → brain by exception (Q5): watcher children stay

-  brain-owned, re-reconciled from disk on brain start (the boot sweep already

-  does orphan cleanup). Move `spawn_wake_host` into the brain.

-- **D2-4 psyche / pulse loops** (`lifecycle.rs`) → brain, each new direct call a

-  versioned additive verb. The pulse loop's phase-significant timing is D5's

-  durable-deadline conversion — D2 only moves the loop; D5 makes its clock

+`shellwake::spawn_wake_host` is the offline half of shell online/offline

+exclusivity: a 5 s reconcile sweep that runs each offline instance's

+`wake_command` as a supervised watcher child. Same cheap shape as D2-1 — it

+takes only a `stop` flag (no shared `Arc<Broker>`) and reaches the broker only

+over the same socket IPC (`Brain::cold_start`) for the remote-wake dial. Q5

+names it a **brain-owned** exception; its boot sweep re-reconciles the watcher

+children from disk on every brain start, so a brain respawn re-sweeps orphaned

+- `brainproc.rs`: `run_brain` spawns the wake host once at brain start (before

+  the heartbeat loop), unconditionally (no net dependency).

+- `daemon.rs`: drop the broker-side `spawn_wake_host` block.

+Evidence: `[impl->REQ-SHELL-2]` + `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`.

+## Scope correction — what is NOT a D2 relocation

+A `Daemon::run` audit (D2-2) found the design's loop inventory over-counted what

+the production spt-core daemon actually hosts:

+- **Digest parse feed** (`Brain::run_digest_feed`): has **no production spawn

+  site** — `ingest_chunk` is reached only from it, and the broker's `DigestHub`

+  is empty in prod. It is an unwired brain-side primitive. Wiring it (the brain

+  pushes parsed digests to the broker's hub over a **new additive verb**) is

+  forward live-agent work, not a relocation of a D1-left loop.

+- **Psyche / pulse loops** (`lifecycle.rs`): not spawned in `Daemon::run` at all

+  — the live-agent loops still run in the legacy listener (owl.exe). They arrive

+  in the spt-core daemon with the live-agent adapter buildout, on the post-D7

+  final topology. The pulse loop's swap-durable timing is D5's concern when it

+So D2's structural relocation is **complete** after D2-1 + D2-2: every logic

+loop that actually runs in the broker process now runs in the brain.


Migrate shellwake wake-host into brain (D2-2)

Move shellwake::spawn_wake_host from broker process to brain child (pure IPC, no shared Arc, Q5 brain-owned exception). Unconditional spawn at brain start; boot sweep re-reconciles orphans on respawn. Then correct the D2 ledger: digest-parse + psyche are NOT broker-resident (unwired/legacy), so D2 structural relocation = net consumers + shellwake only.


Now full gates for D2-2.


Updated task #4 status


Bash

cargo test -p spt-daemon --lib 2>&1 | tail -4 && echo "===CLIPPY===" && cargo clippy -p spt-daemon 2>&1 | tail -3

daemon lib tests + clippy


test sync::tests::select_refs_scopes_two_tiers ... ok

test result: ok. 227 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.19s

===CLIPPY===
    Checking spt-daemon v0.3.2 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.63s


cargo build --no-default-features 2>&1 | tail -3 && echo "===TRACE===" && traceable-reqs check >/dev/null 2>&1; echo "trace EXIT=$?" && echo "===XTASK===" && cargo run -q -p xtask -- check 2>&1 | tail -2

no-default-features + traceable + xtask


cargo test -p spt --test brain_split 2>&1 | tail -4

brain_split smoke


   Compiling spt v0.3.2 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
   Compiling xtask v0.3.2 (C:\Users\decid\Documents\projects\spt-core\crates\xtask)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.64s
===TRACE===
trace EXIT=0
===XTASK===
xtask check: OK


test broker_survives_brain_kill_and_respawns_it ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.08s


All gates green (227 lib tests, clippy, no-default-features, traceable EXIT=0, xtask OK, brain_split smoke). Commit D2-2.


Stage D2-2 + stat


warning: in the working copy of 'RESTORATION-D2-PLAN.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'RESTORATION-PLAN.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'crates/spt-daemon/src/brainproc.rs', LF will be replaced by CRLF the next time Git touches it
 RESTORATION-D2-PLAN.md             | 49 ++++++++++++++++++++++++++++----------
 RESTORATION-PLAN.md                | 48 +++++++++++++++++++++++++------------
 crates/spt-daemon/src/brainproc.rs | 15 ++++++++++++
 crates/spt-daemon/src/daemon.rs    | 19 ++++++---------
 4 files changed, 91 insertions(+), 40 deletions(-)


git commit -q -F - <<'EOF'
feat(daemon): restoration D2-2 — shellwake wake-host moves broker→brain; D2 scope correction (REQ-HAZARD-BROKER-PROCESS-ISOLATION)

Second (and closing) loop migration of the ADR-0018 D2 relocation. shellwake's
wake-host — the offline half of shell online/offline exclusivity, a 5s reconcile
sweep running each offline instance's wake_command as a supervised watcher child
— moves from the broker process into the brain child. Same cheap shape as D2-1:
spawn_wake_host takes only a stop flag (no shared Arc<Broker>) and reaches the
broker only over the same socket IPC (Brain::cold_start) for the remote-wake
dial. Q5 names shellwake a brain-owned exception; its boot sweep re-reconciles
the watcher children from disk on every brain start, so a brain respawn re-sweeps
orphaned watchers — exactly the Q5 posture. No new verb.

Scope correction (a Daemon::run audit during this task): the design's loop
inventory over-counted what the production spt-core daemon actually hosts. Only
TWO logic loops are genuinely broker-resident — the net consumers (D2-1) and
shellwake (D2-2). The rest of the design's list is not a relocation:

- Digest parse feed (Brain::run_digest_feed) has NO production spawn site —
  ingest_chunk is reached only from it, and the broker's DigestHub is empty in
  prod. It is an unwired brain-side primitive; wiring it (brain pushes parsed
  digests to the broker hub over a NEW additive verb) is forward live-agent
  work, not a D1-left-loop relocation.
- Psyche/pulse loops (lifecycle.rs) are not spawned in Daemon::run at all — the
  live-agent loops still run in the legacy listener. They arrive with the
  live-agent adapter on the post-D7 final topology.

So D2's structural relocation is COMPLETE after D2-1 + D2-2: every logic loop
that actually runs in the broker process now runs in the brain. The broker holds
only continuity resources (NetHost / digest hub serve / seed-control / PTY
masters / EffectJournal) + the verb server — the D2-close invariant, reframed to
reality.

Changes:
- brainproc.rs: run_brain spawns the wake host once at brain start (before the
  heartbeat loop), unconditional (no net dependency).
- daemon.rs: drop the broker-side spawn_wake_host block.
- RESTORATION-PLAN.md / RESTORATION-D2-PLAN.md: ledger reframed to the two real
  residents + the two not-resident rows, with the D2-close invariant restated.

N-1 note: both D2 migrations only relocate a spawn site; no verb signature
changed, so the verb-surface N-1 window is untouched and double-spawn cannot
occur (broker+brain swap as one binary until D3's brain-only swaps).

Evidence: [impl->REQ-SHELL-2] + [impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION].

Gates: cargo build/test (227 daemon lib) / clippy / build --no-default-features
/ traceable-reqs check EXIT=0 / xtask check OK. brain_split process-smoke green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git log --oneline -1

Commit D2-2


a9fe443 feat(daemon): restoration D2-2 — shellwake wake-host moves broker→brain; D2 scope correction (REQ-HAZARD-BROKER-PROCESS-ISOLATION)


Push D2-2


To https://github.com/SaberMage/spt-core.git
   f2a17a9..a9fe443  main -> main


sleep 8; gh run list --branch main --limit 1 2>&1 | head -2

Confirm CI kicked off for D2-2


queued		feat(daemon): restoration D2-2 — shellwake wake-host moves broker→bra…	ci	main	push	27248831093	9s	2026-06-10T02:21:55Z


CI run `27248831093` queued. Watching to confirm cross-OS green (D2-2 closes D2's structural relocation).


gh run watch 27248831093 --exit-status 2>&1 | tail -3; echo "WATCH_EXIT=$?"; gh run view 27248831093 --json conclusion,jobs -q '.conclusion, (.jobs[] | select(.name|test("test|traceability")) | "\(.name): \(.conclusion)")' 2>&1 | head -6

Watch + confirm D2-2 CI conclusions


  ✓ Complete job
- twohost-b in 0s (ID 80469271382)
- twohost-a in 0s (ID 80469271456)
WATCH_EXIT=0
success
traceability: success
test (self-hosted, Windows, hfenduleam): success
test (self-hosted, Linux, kitsubito): success


D2-2 CI green cross-OS: **Windows ✓ · Linux ✓ · traceability ✓**. D2's structural relocation is complete and verified.

One open D2 item: the V6 N-1 compat-test scaffold. But D2 changed **zero verbs** (both migrations only moved spawn sites) — the harness has nothing to assert until the first verb signature change, which is **D3** (the versioned spawn-time hello field carrying generation + start-reason + update-vs-crash discriminator). The scaffold belongs there. Recording that decision honestly rather than building a teeth-less harness now.


Edit

## V6 — N-1 compat test scaffold (build in D2, gate in D7)

A CI-real old-broker-binary × new-brain-binary exercise across the whole verb
surface. Scaffold the harness here (it has no teeth until a verb signature
changes); D7 makes it a green gate. D2-1 added no verb, so the harness has
nothing to assert yet — stand it up against the existing verb set so each later
D2 verb extends it.

## V6 — N-1 compat test scaffold → deferred to D3 (decision)

A CI-real old-broker-binary × new-brain-binary exercise across the verb surface.
The design slotted the scaffold in D2, but **D2 changed zero verbs** — both
migrations (D2-1 net consumers, D2-2 shellwake) only relocate a *spawn site*, so
there is no N-1 delta for the harness to assert. Building it now would test that
the *unchanged* verb set round-trips against itself — no teeth.

**Decision (2026-06-09): build the V6 harness in D3.** D3 lands the first real
versioned surface change — the spawn-time hello/argv field carrying
`{generation, start-reason}` + the update-vs-crash discriminator (ADR-0018
Q2/Q4, V2). That is the first additive field whose N-1 (old-broker × new-brain)
window must be proven, so the harness gets its first genuine assertion there and
grows with each later additive field; D7 makes it the green gate. Tracked in the
D3 task list.


## V6 — N-1 compat test scaffold → deferred to D3 (decision)

A CI-real old-broker-binary × new-brain-binary exercise across the verb surface.
The design slotted the scaffold in D2, but **D2 changed zero verbs** — both
migrations (D2-1 net consumers, D2-2 shellwake) only relocate a *spawn site*, so
there is no N-1 delta for the harness to assert. Building it now would test that
the *unchanged* verb set round-trips against itself — no teeth.

**Decision (2026-06-09): build the V6 harness in D3.** D3 lands the first real
versioned surface change — the spawn-time hello/argv field carrying
`{generation, start-reason}` + the update-vs-crash discriminator (ADR-0018
Q2/Q4, V2). That is the first additive field whose N-1 (old-broker × new-brain)
window must be proven, so the harness gets its first genuine assertion there and
grows with each later additive field; D7 makes it the green gate. Tracked in the
D3 task list.

# Restoration D2 — IPC verb surface + loop migration (task plan)

> Working doc for RESTORATION-PLAN.md **D2** (ADR-0018 Q5/V6). The canonical
> loop→verb→moves-at-commit ledger lives in RESTORATION-PLAN.md D2; this doc
> holds the per-commit task detail. D1 (process-split skeleton) is DONE +
> cross-OS CI-green on main (0c95435).

## Goal (D2-close invariant)

Every logic loop runs in the **brain** child; the broker holds only continuity
resources (PTY masters / harness children / `EffectJournal` / NetHost / digest
hub / seed-control) + the verb server. D1 left every loop in the broker process;
D2 relocates them ONE PER COMMIT.

## Per-commit discipline

1. Define the loop's versioned verb (additive/defaulted — KH-2.3) **if** it made
   a direct `Arc<Broker>` call. A consumer that already spoke pure IPC needs no
   new verb — only its spawn site moves.
2. Reroute the direct call over the socket.
3. Move the loop into the brain child.
4. Prove the N-1 window (old-broker × new-brain) holds for the verb.

Gates every commit: `cargo build` · `cargo test` · `cargo clippy` ·
`cargo build --no-default-features` · `traceable-reqs check` (EXIT=0) ·
`xtask check`. Push to a dev-freeform branch → CI both runners before any tag.

---

## D2-1 — Net consumers → brain  ✅ DONE

**Cheapest first** (D1 discovery): the inbound dispatcher (`dispatch.rs`) and
the outbound peer pump (`peerloop.rs`) are ALREADY standalone IPC clients — each
`Brain::cold_start`s its own connection over `broker_socket_name()`, holds no
shared `Arc<Broker>`. They were merely *spawned* in the broker process for
convenience (`daemon.rs::spawn_net_consumers`). The migration is moving that
spawn site into the brain. **No new verb.**

Net-gating: consumers are meaningful only with a NetHost up. The brain polls the
existing `net-status` verb (answered `enabled:false` on a net-less broker, and
it carries `node_id_hex`) and starts the consumers exactly once, when net first
reports enabled — which also covers the boot-race self-heal (REQ-DAEMON-9): the
broker still owns the NetHost bind retry + `attach_net`, and the brain notices
`enabled` flip and starts consumers then. The broker's `net_retry_attach` keeps
the bind retry, drops the consumer spawn.

Why this is strictly better than today: consumers used to live in the broker, so
a (future) brain restart would not touch them and a broker restart killed them;
now they live with the brain, restart with it, and a brain respawn re-spawns
them from the swapped binary — the intended topology.

Changes:
- `brainproc.rs`: brain heartbeat polls `net-status`; on the first `enabled`
  reply, spawn the net consumers (moved `spawn_net_consumers` here, brain-side)
  with `node_id_hex` from the reply. `net-status` doubles as the broker-liveness
  probe (replaces the `sessions()` probe). `consumer_gate` pure helper enforces
  spawn-exactly-once.
- `daemon.rs`: drop both `spawn_net_consumers` call sites (boot `net_up` branch
  + inside `net_retry_attach`); `net_retry_attach` keeps the NetHost bind retry
  + `attach_net`, no longer spawns consumers (param `node_hex` retired).
- unit: `consumer_gate` once-only + net-gating table.

Evidence: `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]` (brain hosts the
consumers; broker no longer does) · `[unit->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`
(the gate table). REQ already `[doc,impl,unit]` active — no toml change.

---

## D2-2 — Shellwake wake-host → brain  ✅ DONE

`shellwake::spawn_wake_host` is the offline half of shell online/offline
exclusivity: a 5 s reconcile sweep that runs each offline instance's
`wake_command` as a supervised watcher child. Same cheap shape as D2-1 — it
takes only a `stop` flag (no shared `Arc<Broker>`) and reaches the broker only
over the same socket IPC (`Brain::cold_start`) for the remote-wake dial. Q5
names it a **brain-owned** exception; its boot sweep re-reconciles the watcher
children from disk on every brain start, so a brain respawn re-sweeps orphaned
watchers — exactly the Q5 posture.

Changes:
- `brainproc.rs`: `run_brain` spawns the wake host once at brain start (before
  the heartbeat loop), unconditionally (no net dependency).
- `daemon.rs`: drop the broker-side `spawn_wake_host` block.

Evidence: `[impl->REQ-SHELL-2]` + `[impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]`.

## Scope correction — what is NOT a D2 relocation

A `Daemon::run` audit (D2-2) found the design's loop inventory over-counted what
the production spt-core daemon actually hosts:

- **Digest parse feed** (`Brain::run_digest_feed`): has **no production spawn
  site** — `ingest_chunk` is reached only from it, and the broker's `DigestHub`
  is empty in prod. It is an unwired brain-side primitive. Wiring it (the brain
  pushes parsed digests to the broker's hub over a **new additive verb**) is
  forward live-agent work, not a relocation of a D1-left loop.
- **Psyche / pulse loops** (`lifecycle.rs`): not spawned in `Daemon::run` at all
  — the live-agent loops still run in the legacy listener (owl.exe). They arrive
  in the spt-core daemon with the live-agent adapter buildout, on the post-D7
  final topology. The pulse loop's swap-durable timing is D5's concern when it
  does land here.

So D2's structural relocation is **complete** after D2-1 + D2-2: every logic
loop that actually runs in the broker process now runs in the brain.

## V6 — N-1 compat test scaffold (build in D2, gate in D7)

A CI-real old-broker-binary × new-brain-binary exercise across the whole verb
surface. Scaffold the harness here (it has no teeth until a verb signature
changes); D7 makes it a green gate. D2-1 added no verb, so the harness has
nothing to assert yet — stand it up against the existing verb set so each later
D2 verb extends it.

-## V6 — N-1 compat test scaffold (build in D2, gate in D7)

+## V6 — N-1 compat test scaffold → deferred to D3 (decision)

-A CI-real old-broker-binary × new-brain-binary exercise across the whole verb

-surface. Scaffold the harness here (it has no teeth until a verb signature

-changes); D7 makes it a green gate. D2-1 added no verb, so the harness has

-nothing to assert yet — stand it up against the existing verb set so each later

+A CI-real old-broker-binary × new-brain-binary exercise across the verb surface.

+The design slotted the scaffold in D2, but **D2 changed zero verbs** — both

+migrations (D2-1 net consumers, D2-2 shellwake) only relocate a *spawn site*, so

+there is no N-1 delta for the harness to assert. Building it now would test that

+the *unchanged* verb set round-trips against itself — no teeth.

+**Decision (2026-06-09): build the V6 harness in D3.** D3 lands the first real

+versioned surface change — the spawn-time hello/argv field carrying

+`{generation, start-reason}` + the update-vs-crash discriminator (ADR-0018

+Q2/Q4, V2). That is the first additive field whose N-1 (old-broker × new-brain)

+window must be proven, so the harness gets its first genuine assertion there and

+grows with each later additive field; D7 makes it the green gate. Tracked in the


142	  site*; no verb signature changed, so the verb-surface N-1 window is untouched
143	  (V6's CI compat test has nothing new to assert yet — it stands up against the
144	  existing verb set). The one transient hazard — an old broker that still
145	  spawns these loops in-process paired with a new brain that also spawns them →
146	  **double-spawn** — cannot occur: broker and brain are the same binary and swap
147	  together until D3 introduces brain-only swaps, by which point both halves are
148	  post-migration. Recorded so D3's first brain-only swap lands on an
149	  already-relocated loop set.
150	- **Audit** every current in-process `Arc<Broker>` consumer; convert each
151	  direct call to a versioned verb. Per §Q5:
152	  - **Net bring-up + REQ-DAEMON-9 self-heal retry → broker** — *near-free*:
153	    NetHost is already broker-owned (`broker.rs:175` `OnceLock`), and net
154	    bring-up already lives in the entry that *becomes* the broker entry.
155	  - **Digest hub → broker** (live projections + subscriber sockets); **the
156	    parse that feeds it → brain** (brain pushes parsed digests over IPC).
157	  - **Seed-lock + liveness → broker** (the Q2 anchor; see D3).
158	  - Every other brain→broker call → a versioned verb, defaulted/additive per
159	    KH-2.3 (handoff-argv-compat).
160	- **[V6] N-1 compat is milestone work.** Steady state after every routine
161	  update is **new-brain × old-broker** (broker almost never updates). The
162	  `classify` pre-swap handshake (`brain_ipc_version ≥ broker.min_compatible`)
163	  gates the forward direction, but each verb conversion must hold the N-1

  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — it stands up against the
  existing verb set). The one transient hazard — an old broker that still
  spawns these loops in-process paired with a new brain that also spawns them →
  **double-spawn** — cannot occur: broker and brain are the same binary and swap
  together until D3 introduces brain-only swaps, by which point both halves are
  post-migration. Recorded so D3's first brain-only swap lands on an
  already-relocated loop set.
- **Audit** every current in-process `Arc<Broker>` consumer; convert each
  direct call to a versioned verb. Per §Q5:
  - **Net bring-up + REQ-DAEMON-9 self-heal retry → broker** — *near-free*:
    NetHost is already broker-owned (`broker.rs:175` `OnceLock`), and net
    bring-up already lives in the entry that *becomes* the broker entry.
  - **Digest hub → broker** (live projections + subscriber sockets); **the
    parse that feeds it → brain** (brain pushes parsed digests over IPC).
  - **Seed-lock + liveness → broker** (the Q2 anchor; see D3).
  - Every other brain→broker call → a versioned verb, defaulted/additive per
    KH-2.3 (handoff-argv-compat).
- **[V6] N-1 compat is milestone work.** Steady state after every routine
  update is **new-brain × old-broker** (broker almost never updates). The
  `classify` pre-swap handshake (`brain_ipc_version ≥ broker.min_compatible`)
  gates the forward direction, but each verb conversion must hold the N-1


  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — it stands up against the
  existing verb set). The one transient hazard — an old broker that still

  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — **the scaffold defers to
  D3**, where the first additive hello/argv field lands; see
  `RESTORATION-D2-PLAN.md`). The one transient hazard — an old broker that still


  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — it stands up against the
  existing verb set). The one transient hazard — an old broker that still

  site*; no verb signature changed, so the verb-surface N-1 window is untouched
  (V6's CI compat test has nothing new to assert yet — **the scaffold defers to
  D3**, where the first additive hello/argv field lands; see
  `RESTORATION-D2-PLAN.md`). The one transient hazard — an old broker that still

**N-1 note (no-verb migrations).** Both D2-1 and D2-2 only relocate a *spawn

-  (V6's CI compat test has nothing new to assert yet — it stands up against the

-  existing verb set). The one transient hazard — an old broker that still

+  (V6's CI compat test has nothing new to assert yet — **the scaffold defers to

+  D3**, where the first additive hello/argv field lands; see

+  `RESTORATION-D2-PLAN.md`). The one transient hazard — an old broker that still

spawns these loops in-process paired with a new brain that also spawns them →

**double-spawn** — cannot occur: broker and brain are the same binary and swap

together until D3 introduces brain-only swaps, by which point both halves are


Doc-only decision record. Commit + push.


Bash

git add -A && git commit -q -F - <<'EOF'
docs(restoration): defer V6 N-1 compat-test scaffold D2→D3 (first additive verb lands in D3)

D2's two migrations (net consumers, shellwake) changed zero verbs — they only
relocate spawn sites — so the old-broker × new-brain N-1 compat harness has no
delta to assert yet. Building it in D2 would test the unchanged verb set against
itself. The first real versioned surface change is D3's spawn-time hello/argv
field ({generation, start-reason} + update-vs-crash discriminator, ADR-0018
Q2/Q4 V2), so the harness gets its first genuine assertion there and grows with
each later additive field; D7 makes it the green gate. Recorded in both
RESTORATION-PLAN.md (D2) and RESTORATION-D2-PLAN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
git push origin main 2>&1 | tail -3

Commit + push V6 deferral decision


warning: in the working copy of 'RESTORATION-D2-PLAN.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'RESTORATION-PLAN.md', LF will be replaced by CRLF the next time Git touches it
To https://github.com/SaberMage/spt-core.git
   a9fe443..7398d7c  main -> main


D2 deferral recorded. Let me commune the status (per project workflow) then report.


Skill

D2 STRUCTURALLY COMPLETE + CI-GREEN CROSS-OS + ON MAIN. D3 UNBLOCKED.

origin/main @ 7398d7c. Three commits this session: D2-1 (f2a17a9, CI run 27248426315 ✓ both runners), D2-2 (a9fe443, CI run 27248831093 ✓ both runners), V6-deferral doc (7398d7c). Windows hfenduleam ✓ + Linux kitsubito ✓ + traceability ✓ on each; twohost skipped (no [twohost] tag, expected).

WHAT D2 DID (ADR-0018 Q5/V6 — relocate broker-resident logic loops into the brain child, one per commit):
- D2-1: net consumers (inbound dispatcher dispatch.rs + outbound peer pump peerloop.rs) moved broker→brain. They were already pure IPC clients (Brain::cold_start over broker_socket_name(), no shared Arc<Broker>) — only their spawn site moved. Gated on the existing net-status verb (now also the brain's broker-liveness probe, replacing sessions()); brain spawns them exactly once on first enabled reply, which also covers the REQ-DAEMON-9 boot-race self-heal (broker keeps NetHost bind+attach_net, net-status flips enabled, brain starts consumers). consumer_gate pure helper + unit test. daemon.rs dropped both spawn sites; net_retry_attach retired its node_hex param. NO new verb.
- D2-2: shellwake spawn_wake_host moved broker→brain (Q5 brain-owned exception). Same cheap shape — stop-flag only, no shared Arc, Brain::cold_start for remote-wake dial. Boot sweep re-reconciles watcher children from disk on every brain start (the Q5 posture). Unconditional spawn at brain start. NO new verb.

KEY SCOPE CORRECTION (Daemon::run audit during D2-2): the design's loop inventory over-counted. Only TWO logic loops are genuinely broker-resident in the production spt-core daemon — net consumers + shellwake, both now in the brain. The rest are NOT relocations: (a) digest parse feed (Brain::run_digest_feed) has NO production spawn site — ingest_chunk reached only from it, broker DigestHub empty in prod; it's an unwired primitive, and wiring it (brain→broker hub publish over a NEW additive verb) is forward live-agent work. (b) psyche/pulse loops (lifecycle.rs) not spawned in Daemon::run at all — still in the legacy listener (owl.exe), arrive with the live-agent adapter on the post-D7 topology. So D2-close invariant (every logic loop that ACTUALLY runs in the broker now runs in the brain; broker holds only continuity + verb server) is SATISFIED after D2-1+D2-2.

V6 N-1 compat-test scaffold DEFERRED D2→D3: D2 changed zero verbs (spawn-site moves only), so the old-broker×new-brain harness has no delta to assert. First real versioned surface change is D3's spawn-time hello/argv field ({generation, start-reason} + update-vs-crash discriminator) — harness gets its first genuine assertion there, D7 makes it the gate.

REQ states unchanged: REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] (int→D7) — D2 added impl/unit evidence under it; REQ-SHELL-2 carries the shellwake move; REQ-DAEMON-9 self-heal preserved. 227 daemon lib tests + brainproc unit (consumer_gate added) + brain_split process-smoke all green both OSes. traceable-reqs EXIT=0.

NEXT = D3 (supervision anchor + update trigger, ADR-0018 Q2/Q3/V2): seed-control lock+liveness move to the broker (the never-restarting anchor); broker supervises+respawns the brain (already does via brainproc::supervise_brain — D3 formalizes it as THE update path); update trigger = apply swaps binary then signals brain to snapshot+self-exit, broker respawns from exe path (now the new binary); [V2] generation custody → broker, handed to brain via the versioned spawn-time hello field (carries the Q4 update-vs-crash discriminator too) — THIS is where the V6 N-1 harness gets built. Plans: RESTORATION-PLAN.md D3 + write RESTORATION-D3-PLAN.md next session. End goal unchanged: seamless self-update with no manual fleet bounce, then spt-claude-code on the final topology.

Low-priority still open: route multipart-truncation bug to legacy claude_skill_owl; reconcile ROADMAP (M8/v0.3.x not itemized).


Launching skill: spt:commune


Base directory for this skill: C:\Users\decid\.ccs\instances\bigscreen\plugins\cache\cplugs\spt\1.11.25\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: D2 STRUCTURALLY COMPLETE + CI-GREEN CROSS-OS + ON MAIN. D3 UNBLOCKED.

origin/main @ 7398d7c. Three commits this session: D2-1 (f2a17a9, CI run 27248426315 ✓ both runners), D2-2 (a9fe443, CI run 27248831093 ✓ both runners), V6-deferral doc (7398d7c). Windows hfenduleam ✓ + Linux kitsubito ✓ + traceability ✓ on each; twohost skipped (no [twohost] tag, expected).

WHAT D2 DID (ADR-0018 Q5/V6 — relocate broker-resident logic loops into the brain child, one per commit):
- D2-1: net consumers (inbound dispatcher dispatch.rs + outbound peer pump peerloop.rs) moved broker→brain. They were already pure IPC clients (Brain::cold_start over broker_socket_name(), no shared Arc<Broker>) — only their spawn site moved. Gated on the existing net-status verb (now also the brain's broker-liveness probe, replacing sessions()); brain spawns them exactly once on first enabled reply, which also covers the REQ-DAEMON-9 boot-race self-heal (broker keeps NetHost bind+attach_net, net-status flips enabled, brain starts consumers). consumer_gate pure helper + unit test. daemon.rs dropped both spawn sites; net_retry_attach retired its node_hex param. NO new verb.
- D2-2: shellwake spawn_wake_host moved broker→brain (Q5 brain-owned exception). Same cheap shape — stop-flag only, no shared Arc, Brain::cold_start for remote-wake dial. Boot sweep re-reconciles watcher children from disk on every brain start (the Q5 posture). Unconditional spawn at brain start. NO new verb.

KEY SCOPE CORRECTION (Daemon::run audit during D2-2): the design's loop inventory over-counted. Only TWO logic loops are genuinely broker-resident in the production spt-core daemon — net consumers + shellwake, both now in the brain. The rest are NOT relocations: (a) digest parse feed (Brain::run_digest_feed) has NO production spawn site — ingest_chunk reached only from it, broker DigestHub empty in prod; it's an unwired primitive, and wiring it (brain→broker hub publish over a NEW additive verb) is forward live-agent work. (b) psyche/pulse loops (lifecycle.rs) not spawned in Daemon::run at all — still in the legacy listener (owl.exe), arrive with the live-agent adapter on the post-D7 topology. So D2-close invariant (every logic loop that ACTUALLY runs in the broker now runs in the brain; broker holds only continuity + verb server) is SATISFIED after D2-1+D2-2.

V6 N-1 compat-test scaffold DEFERRED D2→D3: D2 changed zero verbs (spawn-site moves only), so the old-broker×new-brain harness has no delta to assert. First real versioned surface change is D3's spawn-time hello/argv field ({generation, start-reason} + update-vs-crash discriminator) — harness gets its first genuine assertion there, D7 makes it the gate.

REQ states unchanged: REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] (int→D7) — D2 added impl/unit evidence under it; REQ-SHELL-2 carries the shellwake move; REQ-DAEMON-9 self-heal preserved. 227 daemon lib tests + brainproc unit (consumer_gate added) + brain_split process-smoke all green both OSes. traceable-reqs EXIT=0.

NEXT = D3 (supervision anchor + update trigger, ADR-0018 Q2/Q3/V2): seed-control lock+liveness move to the broker (the never-restarting anchor); broker supervises+respawns the brain (already does via brainproc::supervise_brain — D3 formalizes it as THE update path); update trigger = apply swaps binary then signals brain to snapshot+self-exit, broker respawns from exe path (now the new binary); [V2] generation custody → broker, handed to brain via the versioned spawn-time hello field (carries the Q4 update-vs-crash discriminator too) — THIS is where the V6 N-1 harness gets built. Plans: RESTORATION-PLAN.md D3 + write RESTORATION-D3-PLAN.md next session. End goal unchanged: seamless self-update with no manual fleet bounce, then spt-claude-code on the final topology.

Low-priority still open: route multipart-truncation bug to legacy claude_skill_owl; reconcile ROADMAP (M8/v0.3.x not itemized).


I need to download my Psyche's current context first, then commune only the delta. Let me get my identity and the fresh snapshot.


Bash

$LIVE psyche-download todlando 2>&1 | head -60

Download current Psyche context


<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="7398d7c5f0f08032c00aca25e9e8d9946e1c8890" head_subject="docs(restoration): defer V6 N-1 compat-test scaffold D2→D3 (first additi…" commits_since="0" commits_unpulled="0"/>
**D1 DONE + CI GREEN + ON MAIN. D2 UNBLOCKED.**

origin/main @ 0c95435. Cross-OS CI run 27247580888: Linux kitsubito ✓ 4m3s, Windows hfenduleam ✓ 6m14s, traceability ✓. brain_split.rs de-risk holds on both OSes — AF_UNIX worry answered.

REQ states: REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] (int→D7) · REQ-HAZARD-ROLLBACK-STATE-COMPAT = [doc] (impl/unit→D6). 226 daemon lib tests + brainproc unit + brain_split smoke all green.

**ACTIVE FOCUS — D2 (IPC verb surface + migration ledger, Q5/V6):**
Migrate broker-resident logic loops into brain child ONE PER COMMIT:
1. Define versioned verb
2. Reroute direct call over socket
3. Move loop into brain
4. Prove N-1 window

Cheapest first: net consumers (dispatch + peer pump) already connect over `broker_socket_name()` — no shared Arc. Harder: broker.serve thread, digest hub serve (broker-side), net_retry_attach (Arc-coupled).

Keep loop→verb→moves-at-commit ledger current in RESTORATION-PLAN.md D2. Watch KH-2.3: every new verb additive/defaulted. D7 N-1 gate is backstop.

**D3–D7 remain:** supervision anchor+update trigger · multi-session resume+BrainState retire · durable timing (pulse only) · rollback+two-phase record · proof E2E+fleet verify.

**Low-priority:** route multipart-truncation to legacy; reconcile ROADMAP (M8 not itemized); stale DEFERRED mesh row.

**Last commune:** 2026-06-09T19:01 — HFENDULEAM main @ 0c95435 (no change; D2 unblocked, awaiting first loop-migration commit)<project-context-resolved name="spt-core"/>
spt-core — restoration D1 COMPLETE + cross-OS CI-green + on main (0c95435). The broker/brain process boundary is restored and proven on both OSes.

State: origin/main @ 0c95435. traceable-reqs EXIT=0. REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] satisfied (int→D7); REQ-HAZARD-ROLLBACK-STATE-COMPAT = [doc] (impl/unit→D6). 226 daemon lib tests + brainproc unit + brain_split process-smoke all green on both runners.

What D1 shipped: brainproc.rs (run_brain child entry + supervise_brain process-level supervisor mirroring peerloop::supervise_pump + spawn_brain_supervisor re-execing `current_exe daemon brain`); daemon.rs spawns the supervisor before the foreground seed loop; cli.rs hidden `daemon brain`; endpoint.rs brain_ready_path. NO logic moved — net consumers/digest hub/shellwake/seed all stay broker-resident.

NEXT = D2 (IPC verb surface + migration ledger, ADR-0018 Q5/V6). Migrate each broker-resident logic loop into the brain child ONE PER COMMIT: define its versioned verb → reroute the direct call over the socket → move the loop into the brain → prove the N-1 window. Keep the loop→verb→moves-at-commit ledger current in RESTORATION-PLAN.md D2. D2-close invariant: every logic loop runs in the brain; broker holds only continuity resources + the verb server. Discovery from D1: the net consumers (dispatch + peer pump) ALREADY connect over broker_socket_name() rather than a shared Arc, so they're the cheapest first migrations; the genuinely Arc-coupled pieces are the broker.serve thread, digest hub serve (broker-side), net_retry_attach. Watch KH-2.3: every new verb additive/defaulted (the D7 N-1 old-broker×new-brain compat test is the backstop).

End goal unchanged: seamless self-update with no manual fleet bounce (this milestone makes it real), then spt-claude-code on the final topology.

Plans on disk: RESTORATION-PLAN.md (D1–D7), RESTORATION-D1-PLAN.md (done). Low-priority still open: route multipart-truncation bug to legacy claude_skill_owl; reconcile ROADMAP (M8/v0.3.x not itemized).


Psyche context is stale (still "D1 DONE, D2 UNBLOCKED") — the entire D2 work is the delta. `project="spt-core"` populated → emit both slices. Writing the commune.


Write

<live-context>
**D2 STRUCTURALLY COMPLETE + CI-GREEN CROSS-OS + ON MAIN. D3 UNBLOCKED.** (supersedes the stale "D1 done / D2 unblocked" snapshot — D2 is now done.)

origin/main @ 7398d7c. Three commits this session, all CI-green both runners (Windows hfenduleam ✓ + Linux kitsubito ✓ + traceability ✓; twohost skipped, expected):
- **D2-1** (f2a17a9, CI run 27248426315 ✓): net consumers (inbound dispatcher `dispatch.rs` + outbound peer pump `peerloop.rs`) moved broker→brain. Already pure IPC clients (`Brain::cold_start` over `broker_socket_name()`, no shared `Arc<Broker>`) — only the spawn site moved. Gated on the existing `net-status` verb (now also the brain's broker-liveness probe, replacing `sessions()`); brain spawns them once on first `enabled` reply, which also covers the REQ-DAEMON-9 boot-race self-heal (broker keeps NetHost bind+`attach_net`; `net-status` flips enabled; brain starts consumers). `consumer_gate` pure helper + unit test. daemon.rs dropped both spawn sites; `net_retry_attach` retired its `node_hex` param. **NO new verb.**
- **D2-2** (a9fe443, CI run 27248831093 ✓): shellwake `spawn_wake_host` moved broker→brain (Q5 brain-owned exception). Same cheap shape — stop-flag only, no shared Arc, `Brain::cold_start` for the remote-wake dial. Boot sweep re-reconciles watcher children from disk on every brain start (the Q5 posture). Unconditional spawn at brain start. **NO new verb.**
- **V6-deferral doc** (7398d7c): see below.

**KEY SCOPE CORRECTION** (a `Daemon::run` audit during D2-2): the design's loop inventory over-counted. Only TWO logic loops are genuinely broker-resident in the production spt-core daemon — net consumers + shellwake, both now in the brain. The rest are NOT relocations: (a) **digest parse feed** (`Brain::run_digest_feed`) has NO production spawn site — `ingest_chunk` reached only from it, broker `DigestHub` empty in prod; it's an unwired primitive, and wiring it (brain→broker hub publish over a NEW additive verb) is forward live-agent work. (b) **psyche/pulse loops** (`lifecycle.rs`) not spawned in `Daemon::run` at all — still in the legacy listener (owl.exe), arrive with the live-agent adapter on the post-D7 topology. So the D2-close invariant (every logic loop that ACTUALLY runs in the broker now runs in the brain; broker holds only continuity + verb server) is SATISFIED after D2-1+D2-2.

**V6 N-1 compat-test scaffold DEFERRED D2→D3:** D2 changed zero verbs (spawn-site moves only), so the old-broker×new-brain harness has no delta to assert. First real versioned surface change is D3's spawn-time hello/argv field — harness gets its first genuine assertion there; D7 makes it the gate.

**ACTIVE FOCUS — NEXT = D3** (supervision anchor + update trigger, ADR-0018 Q2/Q3/V2):
- seed-control lock+liveness → broker (the never-restarting anchor; `ensure_running`/`is_running`/`daemon stop` external contracts UNCHANGED).
- broker supervises+respawns brain — already does via `brainproc::supervise_brain`; D3 formalizes it as THE update path.
- update trigger: `apply` swaps binary on disk → signals brain to snapshot+self-exit → broker respawns from exe path (now the new binary) → new brain re-attaches. Reuses the existing `apply_brain_only` snapshot→drop→re-attach primitive.
- [V2] generation custody → broker, handed to brain via the versioned spawn-time hello/argv field (carries the Q4 update-vs-crash discriminator too) — **THIS is where the V6 N-1 harness gets built.**
First D3 step: write RESTORATION-D3-PLAN.md.

**Last commune:** 2026-06-09 — HFENDULEAM main @ 7398d7c (D2 complete + CI-green; D3 unblocked).
</live-context>
<project-context>
spt-core — restoration **D2 COMPLETE** (broker/brain loop relocation) + cross-OS CI-green + on main (7398d7c). D1 restored the process boundary; D2 moved the only two genuinely broker-resident logic loops (net consumers, shellwake) into the brain child. Every logic loop that runs in the broker process now runs in the brain; the broker holds only continuity resources (NetHost / digest hub serve / seed-control / PTY masters / EffectJournal) + the verb server.

State: origin/main @ 7398d7c. traceable-reqs EXIT=0. REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] (int→D7) — D2 added impl/unit evidence under it; REQ-SHELL-2 carries the shellwake move; REQ-DAEMON-9 self-heal preserved (broker still owns NetHost bind+attach; brain starts consumers on `net-status` enabled). 227 daemon lib tests (+1 `consumer_gate`) + brainproc unit + brain_split process-smoke all green both runners.

Durable finding (don't re-investigate): the production spt-core daemon's `Daemon::run` hosts only these resident loops — broker.serve (stays, continuity), net consumers (moved D2-1), net_retry_attach (stays, REQ-DAEMON-9), digest hub serve (stays, Q5), shellwake (moved D2-2), brain supervisor (broker's job), seed-control foreground (stays, Q2 anchor → D3). The digest PARSE and psyche/pulse loops are NOT hosted here yet — unwired primitive + legacy-listener-owned respectively; they arrive with the live-agent adapter on the final topology, not as D2 relocations.

Remaining milestone (ADR-0018 D3–D7): D3 supervision anchor + update trigger (seed-lock→broker, snapshot+self-exit→respawn, [V2] generation custody→broker via versioned hello field, build V6 N-1 harness) · D4 multi-session resume + retire BrainState message · D5 durable absolute-deadline pulse timing (pulse loop only; NOT the idempotent pump cadences) · D6 readiness-gated auto-rollback + two-phase applied record (activates REQ-HAZARD-ROLLBACK-STATE-COMPAT impl/unit) · D7 process-level survival E2E + re-point REQ-DAEMON-2/UPD-3 int tags + N-1 gate + 3-node fleet verify. End goal unchanged: seamless self-update with no manual fleet bounce (this milestone makes it real), then spt-claude-code on the final topology.

Plans on disk: RESTORATION-PLAN.md (D1–D7, ledger current), RESTORATION-D1-PLAN.md (done), RESTORATION-D2-PLAN.md (done — ledger + scope correction + V6→D3 deferral). Next: write RESTORATION-D3-PLAN.md. Low-priority still open: route multipart-truncation bug to legacy claude_skill_owl; reconcile ROADMAP (M8/v0.3.x not itemized).
</project-context>


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

<live-context>
**D2 STRUCTURALLY COMPLETE + CI-GREEN CROSS-OS + ON MAIN. D3 UNBLOCKED.** (supersedes the stale "D1 done / D2 unblocked" snapshot — D2 is now done.)

origin/main @ 7398d7c. Three commits this session, all CI-green both runners (Windows hfenduleam ✓ + Linux kitsubito ✓ + traceability ✓; twohost skipped, expected):
- **D2-1** (f2a17a9, CI run 27248426315 ✓): net consumers (inbound dispatcher `dispatch.rs` + outbound peer pump `peerloop.rs`) moved broker→brain. Already pure IPC clients (`Brain::cold_start` over `broker_socket_name()`, no shared `Arc<Broker>`) — only the spawn site moved. Gated on the existing `net-status` verb (now also the brain's broker-liveness probe, replacing `sessions()`); brain spawns them once on first `enabled` reply, which also covers the REQ-DAEMON-9 boot-race self-heal (broker keeps NetHost bind+`attach_net`; `net-status` flips enabled; brain starts consumers). `consumer_gate` pure helper + unit test. daemon.rs dropped both spawn sites; `net_retry_attach` retired its `node_hex` param. **NO new verb.**
- **D2-2** (a9fe443, CI run 27248831093 ✓): shellwake `spawn_wake_host` moved broker→brain (Q5 brain-owned exception). Same cheap shape — stop-flag only, no shared Arc, `Brain::cold_start` for the remote-wake dial. Boot sweep re-reconciles watcher children from disk on every brain start (the Q5 posture). Unconditional spawn at brain start. **NO new verb.**
- **V6-deferral doc** (7398d7c): see below.

**KEY SCOPE CORRECTION** (a `Daemon::run` audit during D2-2): the design's loop inventory over-counted. Only TWO logic loops are genuinely broker-resident in the production spt-core daemon — net consumers + shellwake, both now in the brain. The rest are NOT relocations: (a) **digest parse feed** (`Brain::run_digest_feed`) has NO production spawn site — `ingest_chunk` reached only from it, broker `DigestHub` empty in prod; it's an unwired primitive, and wiring it (brain→broker hub publish over a NEW additive verb) is forward live-agent work. (b) **psyche/pulse loops** (`lifecycle.rs`) not spawned in `Daemon::run` at all — still in the legacy listener (owl.exe), arrive with the live-agent adapter on the post-D7 topology. So the D2-close invariant (every logic loop that ACTUALLY runs in the broker now runs in the brain; broker holds only continuity + verb server) is SATISFIED after D2-1+D2-2.

**V6 N-1 compat-test scaffold DEFERRED D2→D3:** D2 changed zero verbs (spawn-site moves only), so the old-broker×new-brain harness has no delta to assert. First real versioned surface change is D3's spawn-time hello/argv field — harness gets its first genuine assertion there; D7 makes it the gate.

**ACTIVE FOCUS — NEXT = D3** (supervision anchor + update trigger, ADR-0018 Q2/Q3/V2):
- seed-control lock+liveness → broker (the never-restarting anchor; `ensure_running`/`is_running`/`daemon stop` external contracts UNCHANGED).
- broker supervises+respawns brain — already does via `brainproc::supervise_brain`; D3 formalizes it as THE update path.
- update trigger: `apply` swaps binary on disk → signals brain to snapshot+self-exit → broker respawns from exe path (now the new binary) → new brain re-attaches. Reuses the existing `apply_brain_only` snapshot→drop→re-attach primitive.
- [V2] generation custody → broker, handed to brain via the versioned spawn-time hello/argv field (carries the Q4 update-vs-crash discriminator too) — **THIS is where the V6 N-1 harness gets built.**
First D3 step: write RESTORATION-D3-PLAN.md.

**Last commune:** 2026-06-09 — HFENDULEAM main @ 7398d7c (D2 complete + CI-green; D3 unblocked).
</live-context>
<project-context>
spt-core — restoration **D2 COMPLETE** (broker/brain loop relocation) + cross-OS CI-green + on main (7398d7c). D1 restored the process boundary; D2 moved the only two genuinely broker-resident logic loops (net consumers, shellwake) into the brain child. Every logic loop that runs in the broker process now runs in the brain; the broker holds only continuity resources (NetHost / digest hub serve / seed-control / PTY masters / EffectJournal) + the verb server.

State: origin/main @ 7398d7c. traceable-reqs EXIT=0. REQ-HAZARD-BROKER-PROCESS-ISOLATION = [doc,impl,unit] (int→D7) — D2 added impl/unit evidence under it; REQ-SHELL-2 carries the shellwake move; REQ-DAEMON-9 self-heal preserved (broker still owns NetHost bind+attach; brain starts consumers on `net-status` enabled). 227 daemon lib tests (+1 `consumer_gate`) + brainproc unit + brain_split process-smoke all green both runners.

Durable finding (don't re-investigate): the production spt-core daemon's `Daemon::run` hosts only these resident loops — broker.serve (stays, continuity), net consumers (moved D2-1), net_retry_attach (stays, REQ-DAEMON-9), digest hub serve (stays, Q5), shellwake (moved D2-2), brain supervisor (broker's job), seed-control foreground (stays, Q2 anchor → D3). The digest PARSE and psyche/pulse loops are NOT hosted here yet — unwired primitive + legacy-listener-owned respectively; they arrive with the live-agent adapter on the final topology, not as D2 relocations.

Remaining milestone (ADR-0018 D3–D7): D3 supervision anchor + update trigger (seed-lock→broker, snapshot+self-exit→respawn, [V2] generation custody→broker via versioned hello field, build V6 N-1 harness) · D4 multi-session resume + retire BrainState message · D5 durable absolute-deadline pulse timing (pulse loop only; NOT the idempotent pump cadences) · D6 readiness-gated auto-rollback + two-phase applied record (activates REQ-HAZARD-ROLLBACK-STATE-COMPAT impl/unit) · D7 process-level survival E2E + re-point REQ-DAEMON-2/UPD-3 int tags + N-1 gate + 3-node fleet verify. End goal unchanged: seamless self-update with no manual fleet bounce (this milestone makes it real), then spt-claude-code on the final topology.

Plans on disk: RESTORATION-PLAN.md (D1–D7, ledger current), RESTORATION-D1-PLAN.md (done), RESTORATION-D2-PLAN.md (done — ledger + scope correction + V6→D3 deferral). Next: write RESTORATION-D3-PLAN.md. Low-priority still open: route multipart-truncation bug to legacy claude_skill_owl; reconcile ROADMAP (M8/v0.3.x not itemized).
</project-context>


Commune written — Self listener will ingest it. All session tasks done.

## D2 complete — broker/brain loop relocation

Three commits, all CI-green cross-OS (Windows hfenduleam ✓ · Linux kitsubito ✓ · traceability ✓), on `main`:

| Commit | What |
|---|---|
| `f2a17a9` **D2-1** | Net consumers (dispatcher + peer pump) broker→brain. Pure IPC clients already — only spawn site moved. Gated on existing `net-status` verb (now also brain's liveness probe). `consumer_gate` + unit test. **No new verb.** |
| `a9fe443` **D2-2** | Shellwake wake-host broker→brain (Q5 brain-owned). Stop-flag only, boot sweep re-reconciles on respawn. **No new verb.** |
| `7398d7c` | V6 N-1 harness deferral doc |

**Key finding (scope correction).** Audited `Daemon::run`: the design's loop inventory over-counted. Only **two** logic loops are genuinely broker-resident — net consumers + shellwake, both now in the brain. The rest aren't relocations:
- **Digest parse** (`run_digest_feed`) — no production spawn site, broker hub empty in prod. Unwired primitive; cross-process publish is future new-verb work.
- **Psyche/pulse loops** — not in `Daemon::run` at all; still legacy-listener-owned, arrive with the live-agent adapter.

So **D2-close invariant satisfied**: every logic loop that actually runs in the broker now runs in the brain; broker holds only continuity + verb server. Strictly better topology — consumers/shellwake now respawn *with* the brain from the swapped binary instead of dying on a broker restart.

**V6 N-1 compat harness → D3.** D2 changed zero verbs (spawn-site moves only), so the old-broker×new-brain harness has nothing to assert. D3's versioned spawn-time hello field (`{generation, start-reason}` + update-vs-crash discriminator) is the first real surface change — harness gets built there.

**Gates every commit:** build · 227 daemon lib tests (+1) · clippy · `--no-default-features` · `traceable-reqs` EXIT=0 · `xtask check` · brain_split smoke.

**Next: D3** — supervision anchor + update trigger (seed-lock→broker, snapshot+self-exit→respawn, [V2] generation custody→broker via the versioned hello field, build the V6 harness). First step: write `RESTORATION-D3-PLAN.md`. Say go and I'll start D3.