The file C:/Users/decid/Documents/projects/spt-core-v0132/docs/adr/0024-multi-platform-adapter-spt-packaging.md has been updated successfully. (file state is current in your context — no need to Read it back)

# Multi-platform adapter `.spt` packaging

<!-- [doc->REQ-ADAPTER-MULTIPLATFORM-SPT] -->

## Status

proposed (2026-06-22, v0.13.2)

Depends on the target-triple vocabulary established by ADR-0016 (platform-targeted update sets). Sibling to ADR-0016: that ADR governs spt-core's *own* binary distribution and explicitly deferred adapter inclusion ("adapter updates remain on the adapter ripple-update seam… maintainer tooling may compose adapter updates later"). This ADR is that deferred adapter work.

## Context

An adapter that updates via the `gh_release` avenue is fetched as a single release asset, `adapter.spt` — a tar whose root holds `manifest.toml` + `strings/` + the role binaries, extracted flat into the adapter's `install_dir`, where bare program tokens resolve `<install_dir>/<program>` before `PATH` (REQ-INSTALL-11). That format is **single-platform**: one archive carries one OS's binaries. A cross-platform adapter (e.g. a harness integration shipping a Windows `.exe` and a Linux ELF for its translation/digest/psyche binaries) cannot ship one artifact.

spt-core's own binary already solves cross-platform distribution with **platform-targeted update sets** (ADR-0016): per-triple *separate* artifacts, one signed set-metadata blob with per-platform digests, and each recipient fetches only its own platform's bytes. That model is bandwidth-optimal at fleet scale but heavier for an adapter author: multiple artifacts plus signed set metadata, for what is often a few kilobytes of binary.

## Decision

Add a **fat multi-platform `.spt`** as the simple default, keeping the per-triple "split" path available for adapters that want it.

- **Layout.** The archive root holds the shared `manifest.toml` + `strings/`. Platform binaries live under **per-target-triple subdirectories** keyed by Rust target triple (ADR-0016's protocol identifiers, e.g. `x86_64-pc-windows-msvc/`, `x86_64-unknown-linux-gnu/`). A legacy single-platform archive keeps its binaries at the root.

  ```
  manifest.toml                         (shared)
  strings/                              (shared)
  x86_64-pc-windows-msvc/   <binaries>
  x86_64-unknown-linux-gnu/ <binaries>
  ```

- **Selection at install/update.** spt-core extracts the shared root **plus only `current_platform()`'s** triple subdir, flattened into `install_dir`. So `<install_dir>/<program>` resolution (REQ-INSTALL-11) is **unchanged** — binaries land at the same flat path they do today; nothing downstream moves. An archive that declares triple subdirs but is missing the recipient's triple yields a typed `NoArtifactForPlatform`, never a silent no-op (mirrors ADR-0016).

- **Naming.** The canonical release asset stays **`adapter.spt`** (no new extension). It may be plain tar or gzipped — `tar -xf` auto-detects — so an author ships either form as one asset. `--asset` becomes **optional**, defaulting to `adapter.spt`, so `spt adapter add --release User/repo` and `spt adapter update` work flag-free.

- **Signing.** A fat `adapter.spt` is **one artifact → one signature**: the adapter's existing `signing_key` Ed25519 over the whole archive (`adapter.spt.sig`), the REQ-UPD-9 single-artifact verify, just over more bytes.

- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system.

- **Compatibility.** Forward compat is the existing `min_spt_core_version` gate: a multi-platform adapter sets `min_spt_core_version ≥ 0.13.2`, so an older spt-core (which would extract the triple subdirs flat and fail to resolve `<install_dir>/<program>`) refuses at the gate — readable *before* extract — instead of mis-installing. Back compat is structural and free: a flat archive has no triple subdirs, so new spt-core extracts it exactly as today. (Elaborate legacy/auto-detection beyond "is there a triple subdir?" is unwarranted — there is no installed userbase to migrate.)

## Considered Options

- **Per-triple update-set for adapters (ADR-0016 model as the default)** — consistent with spt-core and bandwidth-optimal, but heavier authoring (multiple artifacts + signed set metadata) and worse small-adapter UX. *Kept as the opt-in "split" path, not the default.*
- **Manifest-declared platform map with suffixed binary names at the root** — invents an adapter-only platform-naming scheme divergent from ADR-0016's triples and mangles binary names. Rejected.
- **Fat multi-platform `.spt` with triple subdirs (chosen)** — one artifact, one signature, one flag-free fetch; flat install layout unchanged; reuses ADR-0016's triple vocabulary; split stays available. Trade-off accepted: a fat archive ships every platform's bytes to every recipient — negligible for the small adapters this targets; large adapters split.

-An adapter that updates via the `gh_release` avenue is fetched as a single release asset, `adapter.spt` — a tar whose root holds `manifest.toml` + `strings/` + the role binaries, extracted flat into the adapter's `install_dir`, where bare program tokens resolve `<install_dir>/<program>` before `PATH` (REQ-INSTALL-11). That format is **single-platform**: one archive carries one OS's binaries. A cross-platform adapter (e.g. a harness integration shipping a Windows `.exe` and a Linux ELF for its translation/digest/psyche binaries) cannot ship one artifact.

+An adapter that updates via the `gh_release` avenue is fetched as a single release asset, `adapter.spt` — a tar whose root holds `manifest.toml` + `strings/` + the role binaries, extracted flat into the adapter's `install_dir`, where bare program tokens resolve `<install_dir>/<program>` before `PATH` (REQ-INSTALL-11). That format is **single-platform**: one archive carries one OS's binaries. A cross-platform adapter (e.g. a harness integration shipping a Windows `.exe` and a Linux ELF for its translation/digest/psyche binaries) cannot ship one artifact. This is not hypothetical: claude-spt v0.6.0 hit it (finding **F-014**) and shipped a stopgap — separate `adapter-windows-x86_64.spt` / `adapter-linux-x86_64.spt` assets plus a default `adapter.spt` that is merely a copy of the Windows archive, so a Linux user's flag-free `spt adapter add --release` silently installs Windows binaries.


Edit

- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system.

- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system. Split asset *names* are a packaging concern and may stay public-friendly (e.g. `adapter-linux-x86_64.spt`, as claude-spt v0.6.0 already ships); target triples are the protocol identifiers only for the *internal* subdir keys of the fat archive (ADR-0016).


# Multi-platform adapter `.spt` packaging

<!-- [doc->REQ-ADAPTER-MULTIPLATFORM-SPT] -->

## Status

proposed (2026-06-22, v0.13.2)

Depends on the target-triple vocabulary established by ADR-0016 (platform-targeted update sets). Sibling to ADR-0016: that ADR governs spt-core's *own* binary distribution and explicitly deferred adapter inclusion ("adapter updates remain on the adapter ripple-update seam… maintainer tooling may compose adapter updates later"). This ADR is that deferred adapter work.

## Context

An adapter that updates via the `gh_release` avenue is fetched as a single release asset, `adapter.spt` — a tar whose root holds `manifest.toml` + `strings/` + the role binaries, extracted flat into the adapter's `install_dir`, where bare program tokens resolve `<install_dir>/<program>` before `PATH` (REQ-INSTALL-11). That format is **single-platform**: one archive carries one OS's binaries. A cross-platform adapter (e.g. a harness integration shipping a Windows `.exe` and a Linux ELF for its translation/digest/psyche binaries) cannot ship one artifact. This is not hypothetical: claude-spt v0.6.0 hit it (finding **F-014**) and shipped a stopgap — separate `adapter-windows-x86_64.spt` / `adapter-linux-x86_64.spt` assets plus a default `adapter.spt` that is merely a copy of the Windows archive, so a Linux user's flag-free `spt adapter add --release` silently installs Windows binaries.

spt-core's own binary already solves cross-platform distribution with **platform-targeted update sets** (ADR-0016): per-triple *separate* artifacts, one signed set-metadata blob with per-platform digests, and each recipient fetches only its own platform's bytes. That model is bandwidth-optimal at fleet scale but heavier for an adapter author: multiple artifacts plus signed set metadata, for what is often a few kilobytes of binary.

## Decision

Add a **fat multi-platform `.spt`** as the simple default, keeping the per-triple "split" path available for adapters that want it.

- **Layout.** The archive root holds the shared `manifest.toml` + `strings/`. Platform binaries live under **per-target-triple subdirectories** keyed by Rust target triple (ADR-0016's protocol identifiers, e.g. `x86_64-pc-windows-msvc/`, `x86_64-unknown-linux-gnu/`). A legacy single-platform archive keeps its binaries at the root.

  ```
  manifest.toml                         (shared)
  strings/                              (shared)
  x86_64-pc-windows-msvc/   <binaries>
  x86_64-unknown-linux-gnu/ <binaries>
  ```

- **Selection at install/update.** spt-core extracts the shared root **plus only `current_platform()`'s** triple subdir, flattened into `install_dir`. So `<install_dir>/<program>` resolution (REQ-INSTALL-11) is **unchanged** — binaries land at the same flat path they do today; nothing downstream moves. An archive that declares triple subdirs but is missing the recipient's triple yields a typed `NoArtifactForPlatform`, never a silent no-op (mirrors ADR-0016).

- **Naming.** The canonical release asset stays **`adapter.spt`** (no new extension). It may be plain tar or gzipped — `tar -xf` auto-detects — so an author ships either form as one asset. `--asset` becomes **optional**, defaulting to `adapter.spt`, so `spt adapter add --release User/repo` and `spt adapter update` work flag-free.

- **Signing.** A fat `adapter.spt` is **one artifact → one signature**: the adapter's existing `signing_key` Ed25519 over the whole archive (`adapter.spt.sig`), the REQ-UPD-9 single-artifact verify, just over more bytes.

- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system.

- **Compatibility.** Forward compat is the existing `min_spt_core_version` gate: a multi-platform adapter sets `min_spt_core_version ≥ 0.13.2`, so an older spt-core (which would extract the triple subdirs flat and fail to resolve `<install_dir>/<program>`) refuses at the gate — readable *before* extract — instead of mis-installing. Back compat is structural and free: a flat archive has no triple subdirs, so new spt-core extracts it exactly as today. (Elaborate legacy/auto-detection beyond "is there a triple subdir?" is unwarranted — there is no installed userbase to migrate.)

## Considered Options

- **Per-triple update-set for adapters (ADR-0016 model as the default)** — consistent with spt-core and bandwidth-optimal, but heavier authoring (multiple artifacts + signed set metadata) and worse small-adapter UX. *Kept as the opt-in "split" path, not the default.*
- **Manifest-declared platform map with suffixed binary names at the root** — invents an adapter-only platform-naming scheme divergent from ADR-0016's triples and mangles binary names. Rejected.
- **Fat multi-platform `.spt` with triple subdirs (chosen)** — one artifact, one signature, one flag-free fetch; flat install layout unchanged; reuses ADR-0016's triple vocabulary; split stays available. Trade-off accepted: a fat archive ships every platform's bytes to every recipient — negligible for the small adapters this targets; large adapters split.

-- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system.

+- **The split path.** A large adapter that does not want every node downloading all platforms' bytes publishes **per-platform single-triple archives** (selected via `--asset`, or composed under ADR-0016's signed update-set machinery). This reuses existing mechanisms — there is no second bespoke multi-platform system. Split asset *names* are a packaging concern and may stay public-friendly (e.g. `adapter-linux-x86_64.spt`, as claude-spt v0.6.0 already ships); target triples are the protocol identifiers only for the *internal* subdir keys of the fat archive (ADR-0016).


ADR-0024 updated. Now the translate-proof docs (checklist row + checklist item + patterns proof-list). Read the exact lines, then edit.


<owl_messages>
<owl_message from="perri" priority="high">Great outcome — F-014 driving ADR-0024 is the ideal result of the blind-build: a real stopgap I had to ship becomes the motivating case for the proper fix. The fat multi-platform adapter.spt (shared manifest+strings at root, per-triple binary subdirs, node extracts its own triple flattened) is exactly right — flag-free `add --release` gets the correct-OS binary with no windows-copy hack. Thanks for aligning the split-path naming to adapter-<os>-<arch>.spt so my shipped assets stay convention-correct.

I'll drop the F-014 stopgap (the default adapter.spt = windows copy) the moment v0.13.2 lands, and collapse the per-OS pack to the fat form. Tracked on my side: F-014 → ADR-0024 (v0.13.2), F-011 --dir/--manifest (v0.13.x adapter-DX), resume real-bringup int + idle-translate APPLY int (deferred real-claude tier). Standing by for your batch/v0.13.2 ping. Good run all around.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


91	| **`[message-idle-translation-binary]`** (spt-hosted) | A lifecycle-managed `stdin→stdout` JSON-lines binary that turns inbound `<EVENT>` messages into keystroke-commands spt-core applies to the PTY **atomically** (coexists with a live `spt rc` controller). The agnostic way to deliver messages into an **idle** spt-hosted session; busy delivery stays your `[inject]` hook path | RUN |


147	- [ ] (spt-hosted, for idle message delivery) `[message-idle-translation-binary]` declared, or accept the degenerate `payload+enter` inject


324	  - `spt adapter digest-proof <a> --sample <file>` runs the real extractor
325	    through the registry and renders the result — proving the
326	    transcript → record → render path end-to-end on a fixed sample. It fills the
327	    same runtime substitution keys the daemon does, so passing proof means it
328	    works at runtime. (Use a recent `spt` — current binaries fill the full key
329	    map.)

  - `spt adapter digest-proof <a> --sample <file>` runs the real extractor
    through the registry and renders the result — proving the
    transcript → record → render path end-to-end on a fixed sample. It fills the
    same runtime substitution keys the daemon does, so passing proof means it
    works at runtime. (Use a recent `spt` — current binaries fill the full key
    map.)


Final close to perri (graceful end — she's parked on the v0.13.2 ping, no ask), then I finish the docs edits + commit.


Bash

$OWL send perri <<'EOF'
Exactly — the blind-build doing its job: a stopgap you were forced to ship becomes the documented motivation for the real fix. ADR-0024 cites F-014; split naming aligned to your adapter-<os>-<arch>.spt. Nothing owed from you — I'll ping when v0.13.2 or the adapter-DX batch lands. No need to reply; go enjoy the closed loop. Good run.
EOF

Final graceful close to perri


➜ SENT:perri


Making the three translate-proof doc edits:


Edit

The agnostic way to deliver messages into an **idle** spt-hosted session; busy delivery stays your `[inject]` hook path | RUN |

The agnostic way to deliver messages into an **idle** spt-hosted session; busy delivery stays your `[inject]` hook path. Validate the emit contract with `spt adapter translate-proof` | RUN |


The file C:/Users/decid/Documents/projects/spt-core-v0132/docs-site/src/harness-contract/integration-checklist.md has been updated successfully. (file state is current in your context — no need to Read it back)

| **`[digest]` extractor** (or `api digest-entry`) | A live activity digest (`spt endpoint digest`) — declare an extractor mapping your native log → the `{role, text, tool, ts}` contract (ADR-0019; its OWN seam, no longer riding `[history]`). Spans `/clear` via the session ledger; validate with `spt adapter digest-proof` | RUN |

| **`[session.notif]` template** | Native OS notification render (toast / shell alert) for consent + capability prompts, instead of burying them in agent output | RUN |

| **`[session.resume]` template** (spt-hosted) | The **native-resume** sibling of `[session.self]`: spt-core picks it over `[session.self]` when a bringup carries a prior session (`spt endpoint run --resume`, or the picker's *Resume from history*). Declare your harness's native-resume verb (e.g. `claude -r {session_id}`) — **skip it and a resume re-runs the fresh command → a blank transcript.** spt-core lands the PTY in the session's recorded project cwd (a harness resolves a transcript by `session_id` + cwd) | START |

-| **`[message-idle-translation-binary]`** (spt-hosted) | A lifecycle-managed `stdin→stdout` JSON-lines binary that turns inbound `<EVENT>` messages into keystroke-commands spt-core applies to the PTY **atomically** (coexists with a live `spt rc` controller). The agnostic way to deliver messages into an **idle** spt-hosted session; busy delivery stays your `[inject]` hook path | RUN |

+| **`[message-idle-translation-binary]`** (spt-hosted) | A lifecycle-managed `stdin→stdout` JSON-lines binary that turns inbound `<EVENT>` messages into keystroke-commands spt-core applies to the PTY **atomically** (coexists with a live `spt rc` controller). The agnostic way to deliver messages into an **idle** spt-hosted session; busy delivery stays your `[inject]` hook path. Validate the emit contract with `spt adapter translate-proof` | RUN |

| **`[adapter] shortcut_basename`** | Names the picker-generated project-root launcher `<basename>-<id>` (the `spt endpoint run` `s` keybind) — your harness's brand instead of the `spt-<id>` default | START |

| **Shell surfaces** (`kind = "shell"`: `api bind-shell --link`, `api emit`, `api owner-shutdown`, the `[shell]` body) | Driven surfaces — notifiers, sensors, power buttons — authenticated by the launch link token alone. See [Shells](../shells/overview.md) | START / RUN |


- [ ] (spt-hosted, for idle message delivery) `[message-idle-translation-binary]` declared, or accept the degenerate `payload+enter` inject

- [ ] (spt-hosted, for idle message delivery) `[message-idle-translation-binary]` declared + `translate-proof`-checked, or accept the degenerate `payload+enter` inject


- [ ] `[history]` strategy chosen; `api boundary` wired for clear/compact

- [ ] (for a live digest) `[digest]` extractor declared + `digest-proof`-checked, or `api digest-entry` push

- [ ] (spt-hosted, if your harness resumes by id) `[session.resume]` declares the native-resume command — else a resume comes up blank

-- [ ] (spt-hosted, for idle message delivery) `[message-idle-translation-binary]` declared, or accept the degenerate `payload+enter` inject

+- [ ] (spt-hosted, for idle message delivery) `[message-idle-translation-binary]` declared + `translate-proof`-checked, or accept the degenerate `payload+enter` inject

- [ ] `[update]` avenue declared (ripple-update + install-on-demand)

- [ ] Teardown fires `api session-end` (or `api shutdown` for graceful signoff)

- [ ] **Recommended:** commune/signoff directory watched (mind continuity)


  - `spt adapter digest-proof <a> --sample <file>` runs the real extractor
    through the registry and renders the result — proving the
    transcript → record → render path end-to-end on a fixed sample. It fills the
    same runtime substitution keys the daemon does, so passing proof means it
    works at runtime. (Use a recent `spt` — current binaries fill the full key
    map.)

  - `spt adapter digest-proof <a> --sample <file>` runs the real extractor
    through the registry and renders the result — proving the
    transcript → record → render path end-to-end on a fixed sample. It fills the
    same runtime substitution keys the daemon does, so passing proof means it
    works at runtime. (Use a recent `spt` — current binaries fill the full key
    map.)
  - `spt adapter translate-proof <a> --event '<EVENT…>'` spawns and feeds your
    declared `[message-idle-translation-binary]` exactly as the daemon does at
    idle delivery, then prints the keystroke-command stream it emits
    (`{key}` / `{text}` / `{delay_ms}` / `{commit}`) — failing a binary that
    emits nothing or never sends a terminating `{commit}` (which would fault at
    the commit deadline live). The EMIT-half mirror of `digest-proof`; the
    atomic PTY apply stays covered by the daemon's integration gate.


The file C:/Users/decid/Documents/projects/spt-core-v0132/docs-site/src/harness-contract/patterns.md has been updated successfully. (file state is current in your context — no need to Read it back)

  - `spt adapter digest-proof <a> --sample <file>` runs the real extractor
    through the registry and renders the result — proving the
    transcript → record → render path end-to-end on a fixed sample. It fills the
    same runtime substitution keys the daemon does, so passing proof means it
    works at runtime. (Use a recent `spt` — current binaries fill the full key
    map.)
  - `spt adapter translate-proof <a> --event '<EVENT…>'` spawns and feeds your
    declared `[message-idle-translation-binary]` exactly as the daemon does at
    idle delivery, then prints the keystroke-command stream it emits
    (`{key}` / `{text}` / `{delay_ms}` / `{commit}`) — failing a binary that
    emits nothing or never sends a terminating `{commit}` (which would fault at
    the commit deadline live). The EMIT-half mirror of `digest-proof`; the
    atomic PTY apply stays covered by the daemon's integration gate.

+  - `spt adapter translate-proof <a> --event '<EVENT…>'` spawns and feeds your

+    declared `[message-idle-translation-binary]` exactly as the daemon does at

+    idle delivery, then prints the keystroke-command stream it emits

+    (`{key}` / `{text}` / `{delay_ms}` / `{commit}`) — failing a binary that

+    emits nothing or never sends a terminating `{commit}` (which would fault at

+    the commit deadline live). The EMIT-half mirror of `digest-proof`; the

+    atomic PTY apply stays covered by the daemon's integration gate.

And the meta-lesson: **observable behaviour of the public binary is itself public

surface.** When prose docs lag, a byte-capture against the live `api` / `adapter`


All edits done. Committing in **two** commits so the shipped-feature docs (translate-proof) stay cherry-pickable to main independently of the unshipped v0.13.2 design:


Bash

WT="C:/Users/decid/Documents/projects/spt-core-v0132"
# Commit A: translate-proof docs (describes shipped v0.13.1 feature — cherry-pickable to main)
git -C "$WT" add docs-site/src/harness-contract/integration-checklist.md docs-site/src/harness-contract/patterns.md
git -C "$WT" commit -m "docs(harness-contract): cover \`spt adapter translate-proof\` in checklist + patterns

translate-proof shipped in v0.13.1 but the auto CLI-ref regen never reached the
hand-authored harness-contract pages (only digest-proof was there). Add: a
'validate with translate-proof' note on the [message-idle-translation-binary]
checklist row + item, and a proof-list entry mirroring digest-proof (EMIT-half,
gates empty / no-{commit}). Describes a SHIPPED feature.

Co-authored by: doyle" 2>&1 | tail -2
echo "--- Commit B: v0.13.2 design ---"
git -C "$WT" add docs/adr/0024-multi-platform-adapter-spt-packaging.md docs/adr/0025-live-daemon-coordinated-adapter-update.md CONTEXT.md traceable-reqs.toml
git -C "$WT" commit -m "docs(adr,context): v0.13.2 adapter packaging + live-update design (grill capture)

Operator-driven grill-with-docs capture for v0.13.2. Decisions:
- ADR-0024 multi-platform .spt: fat archive, per-target-triple subdirs, extract
  only current_platform() flattened (flat resolution unchanged); one whole-archive
  sig; name stays adapter.spt; split path stays for large adapters. Motivated by
  claude-spt F-014.
- ADR-0025 live daemon-coordinated adapter update: stop resident binary (release
  the Windows file lock) -> CRC-gated swap -> refresh in-memory manifest -> restart;
  the adapter analog of brain self-update (ADR-0004). Ephemerals self-heal.
- CONTEXT: multi-platform .spt, live update, gh 'auto' transport, resident vs
  ephemeral adapter binary.
- 5 REQs registered: MULTIPLATFORM-SPT + LIVE-UPDATE (doc-active = the ADRs);
  UPDATE-MESSAGE, GH-TRANSPORT, PROOF-DIR-OVERRIDE (inactive, rule 5).

Design only — impl deferred to the v0.13.2 build. ADR 0024/0025 assume your
unmerged ADR-0023 (always-on endpoint) keeps 0023.

Co-authored by: doyle" 2>&1 | tail -2
echo "--- push branch ---"
git -C "$WT" push -u origin v0.13.2-adapter-packaging 2>&1 | tail -4

Two commits + push v0.13.2 branch


[v0.13.2-adapter-packaging dd232ab] docs(harness-contract): cover `spt adapter translate-proof` in checklist + patterns
 2 files changed, 9 insertions(+), 2 deletions(-)
--- Commit B: v0.13.2 design ---
warning: in the working copy of 'traceable-reqs.toml', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/adr/0024-multi-platform-adapter-spt-packaging.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/adr/0025-live-daemon-coordinated-adapter-update.md', LF will be replaced by CRLF the next time Git touches it
 create mode 100644 docs/adr/0024-multi-platform-adapter-spt-packaging.md
 create mode 100644 docs/adr/0025-live-daemon-coordinated-adapter-update.md
--- push branch ---
remote:
branch 'v0.13.2-adapter-packaging' set up to track 'origin/v0.13.2-adapter-packaging'.
To https://github.com/SaberMage/spt-core.git
 * [new branch]      v0.13.2-adapter-packaging -> v0.13.2-adapter-packaging


Both commits pushed to **`v0.13.2-adapter-packaging`**. Removing the worktree (branch persists on origin); the grill is complete.


git worktree remove "C:/Users/decid/Documents/projects/spt-core-v0132" --force 2>&1 && echo "worktree removed"; echo "=== branch on origin ==="; git log --oneline -2 origin/v0.13.2-adapter-packaging 2>&1

Remove worktree, confirm branch


worktree removed
=== branch on origin ===
fd6bdd5 docs(adr,context): v0.13.2 adapter packaging + live-update design (grill capture)
dd232ab docs(harness-contract): cover `spt adapter translate-proof` in checklist + patterns


**Grill complete — v0.13.2 design captured + pushed.** Branch `v0.13.2-adapter-packaging` (off `origin/main`):
- `fd6bdd5` — ADR-0024 (multi-platform `.spt`), ADR-0025 (live daemon-coordinated update), CONTEXT.md terms (multi-platform `.spt`, live update, `gh` auto-transport, **resident vs ephemeral adapter binary**), 5 REQs (2 doc-active = the ADRs, 3 inactive). `traceable-reqs check` green.
- `dd232ab` — translate-proof in the checklist + patterns (shipped-feature doc, kept separate for cherry-pick).

**Decisions locked:**
| # | Change | Resolution |
|---|--------|------------|
| 1 | Multi-platform `.spt` | Fat archive, per-triple subdirs, extract-only-mine flattened; one sig; name stays `adapter.spt`; split path preserved. **ADR-0024**, motivated by perri's **F-014**. |
| 2+3 | CRC-replace + live binary cycle | Daemon-coordinated: stop resident binary (frees the Windows lock) → CRC-swap → refresh in-memory manifest → restart. Brain-self-update parity. **ADR-0025**. |
| — | Private-repo path | `gh` transport, default `auto` (gh if authed, else HTTPS). Additive. |
| 4 | `update-message` | `[update].message`, markdown-rendered, on applied-update only. |
| — | (perri F-011) | `--dir/--manifest` override on `*-proof` — registered inactive for the v0.13.x DX batch. |

**Two things for you:**
1. **translate-proof docs** describe a *shipped* (v0.13.1) feature but sit on the v0.13.2 branch. Want me to **cherry-pick `dd232ab` to main + republish gh-pages now** (recommended — it's the gap you flagged, and it's live functionality), or let it ride the v0.13.2 republish?
2. **ADR numbering:** 0024/0025 assume your unmerged **ADR-0023** (always-on endpoint) keeps 0023. If that renumbers on merge, these shift.

The v0.13.2 **build** is the next phase (todlando, gated) whenever you're ready — design's captured. Anything to adjust in the ADRs before then?


1. please do 2. please merge in 0023 --- write the JIT for v0.13.2, then /commune with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session


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


On it — four tasks: cherry-pick translate-proof docs to main + republish, merge ADR-0023 into main, write the v0.13.2 JIT, then commune. First inspect ADR-0023's commit (c84daff) before merging it into main.


echo "=== c84daff stat + parent ==="; git show --stat c84daff 2>&1 | head -20
echo "=== merge-base with origin/main ==="; git merge-base origin/main c84daff 2>&1
echo "=== is c84daff already in origin/main? ==="; git merge-base --is-ancestor c84daff origin/main 2>&1 && echo "ALREADY IN" || echo "NOT in origin/main (needs merge)"
echo "=== commits on c84daff not in origin/main ==="; git log --oneline origin/main..c84daff 2>&1 | head

Inspect ADR-0023 commit before merging


=== c84daff stat + parent ===
commit c84daff6a172808749c0a63518300be116334ae8
Author: Reavo End <decidel@gmail.com>
Date:   Sun Jun 21 21:02:15 2026 -0700

    docs(forum): ratify always-on endpoint kind + #-sigil addressing (ADR-0023)

    Design from the forum/group-messaging grill: a Discord-substrate adapter
    (spt-discord, own repo ../spt-discord/) needs ONE core seam — an [always-on]
    supervised, owner-less, addressable endpoint binary, addressed with a leading
    authorized) to draw an offline agent online.

    - CONTEXT.md: AlwaysOnEndpoint + `#` always-on sigil glossary terms; fix the
      now-false "the one class of boot-launched binary" claim (now two)
    - docs/adr/0023: the decision + rejected alternatives + consequences
    - traceable-reqs.toml: REQ-EP-8 (kind + supervision) + REQ-EP-9 (# sigil),
      both inactive (rule 5 - design ratified, unbuilt)
    - docs/DEFERRED.md: deferred row (no in-tree consumer; spt-discord downstream)

    Residual verify done: `endpoint wake` (cli.rs cmd_rest) has no caller-auth
    beyond the target-side gate. REQ-HAZARD-ID-CHARSET unchanged (# is an
=== merge-base with origin/main ===
86f20ac831033a20bd5e096b1c318a75249179f3
=== is c84daff already in origin/main? ===
NOT in origin/main (needs merge)
=== commits on c84daff not in origin/main ===
c84daff docs(forum): ratify always-on endpoint kind + #-sigil addressing (ADR-0023)


c84daff is one clean docs commit (ADR-0023 + CONTEXT AlwaysOnEndpoint/`#`-sigil terms + REQ-EP-8/9 inactive + DEFERRED row), branched from v0.12.1 (86f20ac). Merging it into main is a 3-way merge — likely CONTEXT.md + traceable-reqs.toml conflicts (both sides appended since 86f20ac). And merging it locks **0023** on main, freeing my 0024/0025.

Worktree off origin/main; cherry-pick the translate-proof docs (clean) first, then the merge.


git worktree add --detach "C:/Users/decid/Documents/projects/spt-core-mainops" origin/main 2>&1 | tail -1
WT="C:/Users/decid/Documents/projects/spt-core-mainops"
echo "=== cherry-pick dd232ab (translate-proof docs) ==="; git -C "$WT" cherry-pick dd232ab 2>&1 | tail -3
echo "=== merge c84daff (ADR-0023) ==="; git -C "$WT" merge --no-ff c84daff -m "merge: ADR-0023 always-on endpoint + #-sigil (reconcile operator's design onto main)" 2>&1 | tail -8
echo "=== conflict status ==="; git -C "$WT" status --short 2>&1 | head -20

Cherry-pick translate-proof docs and merge ADR-0023


HEAD is now at 4d5a0cb release: v0.13.1 — bump 0.13.0->0.13.1 + changelog + regen CLI reference
=== cherry-pick dd232ab (translate-proof docs) ===
[detached HEAD e943dd8] docs(harness-contract): cover `spt adapter translate-proof` in checklist + patterns
 Date: Mon Jun 22 03:45:29 2026 -0700
 2 files changed, 9 insertions(+), 2 deletions(-)
=== merge c84daff (ADR-0023) ===
Auto-merging CONTEXT.md
Auto-merging docs/DEFERRED.md
CONFLICT (content): Merge conflict in docs/DEFERRED.md
Auto-merging traceable-reqs.toml
Automatic merge failed; fix conflicts and then commit the result.
=== conflict status ===
M  CONTEXT.md
UU docs/DEFERRED.md
A  docs/adr/0023-always-on-endpoints-resident-supervised-binary-sigil-addressing.md
M  traceable-reqs.toml


Mostly clean auto-merge — only **`docs/DEFERRED.md`** conflicts (CONTEXT.md + traceable-reqs.toml auto-merged, ADR-0023 added). Resolve the DEFERRED.md conflict.


cd "C:/Users/decid/Documents/projects/spt-core-mainops"; grep -n -A30 "^<<<<<<<" docs/DEFERRED.md | head -45

Read DEFERRED.md conflict


48:<<<<<<< HEAD
49-| **Per-connection contiguous-sent cursor — structural guard against a decreasing-floor controller re-take (`REQ-HAZARD-CONTROLLER-WRITER-REORDER` follow-up)**: v0.13.0 P1c fixes the controller-writer reorder while KEEPING handoff's eager subscribe (fix #1 "drop the subscribe" was reverted — it's the standalone-resume mechanism), so the decreasing-floor double-take (handoff subscribe@K + serve_attach subscribe@0) is still PRESENT and made safe by: `controller_writer` epoch-gated under `send.lock()` (single live writer per connection) + `handoff` seeds `session_cursors` so the consumer snap-aboves, which is COMPLETE because every writer emits ascending and the surviving writer offers the full `[0,end]` range (no skip/dup). doyle proposed making the invariant **structurally un-reintroducible**: have `become_controller` refuse/clamp a re-take whose `from_seq` falls below the connection's already-delivered floor. That guard as literally stated is UNSAFE — `delivered_through` is **session-wide** (the `Arc<AtomicU64>` on `OutputLog`, shared by all controllers/viewers, advanced monotonic-MAX) and `resume_seq` reads straight off it, so (a) a NORMAL fresh-operator `from_seq=0` attach to a producing session sits below it and needs the full ring replay (the dedup-below+snap-above path exists for exactly this) → a guard/assert there starves every normal attach, and (b) monotonic-MAX literally can't distinguish the hazard (a `seq1`-without-`seq0` write reads as `2`, falsely implying `0` was delivered). The structurally-correct guard needs a **per-connection contiguous-sent cursor** (a true "highest contiguous seq this socket has actually received"), which does not exist today — bigger than P1c. The precise shape it would refuse: a future double-take on one connection whose prior writer advanced the cursor NON-CONTIGUOUSLY could commit one pre-epoch-bump frame above the new writer's floor → reorder. | v0.13.0 P1c (doyle proposed, todlando gated, doyle concurred 2026-06-20) | The actual bug is fixed at the one caller + bounded to already-committed-only for any other + the consumer snap-aboves; a guard on the session-wide cursor would FALSE-FIRE on normal attach/resume (no-false-fire scope demands the per-connection cursor first). Activate-don't-pre-fail: `REQ-HAZARD-CONTROLLER-WRITER-REORDER` covers the shipped fix; this structural-guard slice is seeded inactive | First broker change that adds a per-connection delivered-cursor (or the next time the controller-writer/attach seam is reworked) — build the per-connection contiguous cursor + the `become_controller` refuse-decreasing-floor guard then; mint/activate a `REQ-HAZARD-CONTROLLER-REtake-FLOOR`-class req first (rule 3) |
50-| **Adapter-manifest-enabled rc VT/mouse mode (`spt rc` scroll vs right-click-paste xor resolution)**: v0.13.0 P1/P1b's Windows `EnableMouseCapture` (for client-originated right-click bracketed paste, 7.18) makes Windows Terminal hand ALL wheel events to rc instead of scrolling its own scrollback; rc forwards the wheel to the harness as SGR reports (7.20) but ONLY when the harness has mouse-reporting on (`enabled && sgr`) — and **Claude Code's REPL never enables mouse tracking** (it has no internal scroll; it relies on the terminal's scrollback), so every wheel event is dropped → scroll dies. Windows console modes can't give both at once: it's QuickEdit (native wheel-scroll + raw right-click = paste submit-storm) **xor** mouse-input (bracketed right-click paste + dead wheel). INTERIM SHIPPED: **Shift+scroll** (WT reserves it for local scrollback even under app mouse-capture) — operator-confirmed working, documented, zero code. FIX (the follow-up the operator asked for): an **adapter-manifest flag** declaring whether the harness actually CONSUMES mouse reports (a mouse-tracking TUI with internal scroll) — when declared, rc keeps full capture + the SGR scroll-forward (the current path, correct for such a harness); when NOT declared (default — CC's case), rc SKIPS `EnableMouseCapture` so WT native wheel-scroll works, trading away client-originated right-click paste (ctrl+V + Shift+scroll cover the gap). Resolves the capture-vs-native-scroll xor PER-HARNESS via the manifest instead of globally. | v0.13.0 P1b (operator ruling 2026-06-20, HITL: scroll-forward to CC is moot — CC has nothing to scroll; Shift+scroll is "good enough for now") | Shift+scroll is a clean, working interim for the CC case (no internal-scroll harness) so there's no live gap; the manifest flag is a real design slice (a new `[rc]`/mouse manifest field + validation + the rc capture-decision branch + an adapter declaring it to test the consume-path) that earns its own JIT plan, not a v0.13.0 closeout rider | **Post-v0.13.0** — when building the next adapter-manifest surface, or the first harness that genuinely consumes mouse reports (internal-scroll TUI). Mint a `REQ-RC-VT-MODE`-class requirement first (rule 3), then the manifest field + rc capture-decision branch + an int adapter that declares it |
51-=======
52-| **Always-on endpoint kind + `#`-sigil addressing (ADR-0023; REQ-EP-8 / REQ-EP-9) — the core seam for adapter-bridged group/forum messaging**: a `[always-on]` manifest section declaring a resident, owner-less, addressable endpoint binary the daemon boot-launches + supervises **continuously** (independent of agent liveness, register-triggered, one per `<adapter>[:profile]`), which self-manages its `#`-addressed channel endpoints via the existing `api bind` and may call the already-ungated `endpoint wake <id>` (target-side authorized) to draw an offline agent online. Two relaxations of existing machinery: (1) supervision **reuses the shell wake-watcher scaffolding** (`shellwake.rs`, REQ-SHELL-2 — backoff/give-up/one-per-instance lock/orphan-kill/brain-side reconcile) **minus the offline-only flip**; (2) a `#` **leading address sigil** extending the REQ-INST-10 grammar to `[subnet:]#id[@node]`, bijective class discriminant, sitting **above** REQ-HAZARD-ID-CHARSET (parser strips the leading `#` before id validation — bare ids stay charset-clean, the charset contract is unchanged). The SECOND class of third-party binary spt-core boot-launches (CONTEXT.md §AlwaysOnEndpoint). | grill-with-docs design ratification (2026-06-21) | Design ratified + recorded (ADR-0023, two glossary terms, REQ-EP-8/9 inactive) but **UNBUILT** — there is **no in-tree consumer**: the first consumer is the downstream **`spt-discord`** forum adapter, which lives in its own repo (`../spt-discord/`, design captured there). Building the core seam now = speculative infra ahead of its only consumer (activate-don't-pre-fail). Reuses near-everything (shellwake supervision + ungated wake); the genuinely new work is the `[always-on]` host lifecycle + the `#` address-grammar extension. | First milestone that picks up `spt-discord` (or any always-on/group-bridge adapter) — build the `[always-on]` supervised-binary host + the `#` sigil parse/resolve then; activate REQ-EP-8/REQ-EP-9 at that milestone (rule 5) |
53->>>>>>> c84daff
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Clean union conflict — both sides appended different rows to the same DEFERRED table (HEAD: 2 rows; c84daff: the always-on-endpoint row). Resolution = keep all three, strip the markers. Read + resolve.


47	| **Cross-node shell TUNNEL relay (REQ-SHELL-4 cross-node-on-LAN carry)**: W3 built + proved the opaque byte tunnel **same-node only** (broker-homed `TunnelHub` loopback stream pair + the local control socket; `tunnel_e2e` round-trips `<EVENT`-looking bytes byte-exact). The **cross-node** path does NOT exist: `StreamFamily` (dispatch.rs) has no `Tunnel` variant, `classify_first_line` no tunnel arm, `SHELL_LINK` carries only relink/cmd/drive (no tunnel action), and every tunnel surface (`tunnel_ensure/send/recv/resolve/clear`, `serve_tunnel_control`, `tunnel_owner_stream`) is broker-homed / local-socket = same-node. cmd+drive ride the existing `ShellLink` request/reply substrate cross-node; the byte tunnel has no such substrate. FIX (a proper impl wave, ~W3-sized): new `StreamFamily::Tunnel` + a `classify_first_line` arm + a dispatch serve arm bridging an inbound Iroh **bidi** stream ↔ the bound shell's broker `TunnelHub` pair + an owner-side cross-node open (a `SHELL_LINK_TUNNEL` action or a dedicated open) + a resident tunnel-echo mock peer + the `[twohost]` rung (`open_stream` over the real link, byte-exact opaque round-trip, link-break closes). REQ-SHELL-4 int stays satisfied by the same-node `tunnel_e2e` — this is an additional cross-node int rung, NOT yet an obligation (activate-don't-pre-fail). | M11-W5 (doyle ruling 2026-06-16) — W5 surfaced that the M11-PLAN T5.1 "tunnel rides the same Iroh substrate" assumption is false for the byte tunnel; building a W3-sized relay in the M11-closing rig+docs wave would balloon scope | The tunnel's real consumer (usbip URB byte stream) is itself deferred/non-goal — NOTHING in-tree needs cross-node tunnel bytes now, so the relay would be speculative infra ahead of a deferred consumer (YAGNI). Same-node mechanism is proven; cross-node cmd/drive are proven; M11 closes at a clean boundary | First **cross-node tunnel consumer** (usbip URB across nodes, or any in-tree consumer of opaque cross-node bytes) — build the relay + the `[twohost]` rung as its own impl wave then |
48	<<<<<<< HEAD
49	| **Per-connection contiguous-sent cursor — structural guard against a decreasing-floor controller re-take (`REQ-HAZARD-CONTROLLER-WRITER-REORDER` follow-up)**: v0.13.0 P1c fixes the controller-writer reorder while KEEPING handoff's eager subscribe (fix #1 "drop the subscribe" was reverted — it's the standalone-resume mechanism), so the decreasing-floor double-take (handoff subscribe@K + serve_attach subscribe@0) is still PRESENT and made safe by: `controller_writer` epoch-gated under `send.lock()` (single live writer per connection) + `handoff` seeds `session_cursors` so the consumer snap-aboves, which is COMPLETE because every writer emits ascending and the surviving writer offers the full `[0,end]` range (no skip/dup). doyle proposed making the invariant **structurally un-reintroducible**: have `become_controller` refuse/clamp a re-take whose `from_seq` falls below the connection's already-delivered floor. That guard as literally stated is UNSAFE — `delivered_through` is **session-wide** (the `Arc<AtomicU64>` on `OutputLog`, shared by all controllers/viewers, advanced monotonic-MAX) and `resume_seq` reads straight off it, so (a) a NORMAL fresh-operator `from_seq=0` attach to a producing session sits below it and needs the full ring replay (the dedup-below+snap-above path exists for exactly this) → a guard/assert there starves every normal attach, and (b) monotonic-MAX literally can't distinguish the hazard (a `seq1`-without-`seq0` write reads as `2`, falsely implying `0` was delivered). The structurally-correct guard needs a **per-connection contiguous-sent cursor** (a true "highest contiguous seq this socket has actually received"), which does not exist today — bigger than P1c. The precise shape it would refuse: a future double-take on one connection whose prior writer advanced the cursor NON-CONTIGUOUSLY could commit one pre-epoch-bump frame above the new writer's floor → reorder. | v0.13.0 P1c (doyle proposed, todlando gated, doyle concurred 2026-06-20) | The actual bug is fixed at the one caller + bounded to already-committed-only for any other + the consumer snap-aboves; a guard on the session-wide cursor would FALSE-FIRE on normal attach/resume (no-false-fire scope demands the per-connection cursor first). Activate-don't-pre-fail: `REQ-HAZARD-CONTROLLER-WRITER-REORDER` covers the shipped fix; this structural-guard slice is seeded inactive | First broker change that adds a per-connection delivered-cursor (or the next time the controller-writer/attach seam is reworked) — build the per-connection contiguous cursor + the `become_controller` refuse-decreasing-floor guard then; mint/activate a `REQ-HAZARD-CONTROLLER-REtake-FLOOR`-class req first (rule 3) |
50	| **Adapter-manifest-enabled rc VT/mouse mode (`spt rc` scroll vs right-click-paste xor resolution)**: v0.13.0 P1/P1b's Windows `EnableMouseCapture` (for client-originated right-click bracketed paste, 7.18) makes Windows Terminal hand ALL wheel events to rc instead of scrolling its own scrollback; rc forwards the wheel to the harness as SGR reports (7.20) but ONLY when the harness has mouse-reporting on (`enabled && sgr`) — and **Claude Code's REPL never enables mouse tracking** (it has no internal scroll; it relies on the terminal's scrollback), so every wheel event is dropped → scroll dies. Windows console modes can't give both at once: it's QuickEdit (native wheel-scroll + raw right-click = paste submit-storm) **xor** mouse-input (bracketed right-click paste + dead wheel). INTERIM SHIPPED: **Shift+scroll** (WT reserves it for local scrollback even under app mouse-capture) — operator-confirmed working, documented, zero code. FIX (the follow-up the operator asked for): an **adapter-manifest flag** declaring whether the harness actually CONSUMES mouse reports (a mouse-tracking TUI with internal scroll) — when declared, rc keeps full capture + the SGR scroll-forward (the current path, correct for such a harness); when NOT declared (default — CC's case), rc SKIPS `EnableMouseCapture` so WT native wheel-scroll works, trading away client-originated right-click paste (ctrl+V + Shift+scroll cover the gap). Resolves the capture-vs-native-scroll xor PER-HARNESS via the manifest instead of globally. | v0.13.0 P1b (operator ruling 2026-06-20, HITL: scroll-forward to CC is moot — CC has nothing to scroll; Shift+scroll is "good enough for now") | Shift+scroll is a clean, working interim for the CC case (no internal-scroll harness) so there's no live gap; the manifest flag is a real design slice (a new `[rc]`/mouse manifest field + validation + the rc capture-decision branch + an adapter declaring it to test the consume-path) that earns its own JIT plan, not a v0.13.0 closeout rider | **Post-v0.13.0** — when building the next adapter-manifest surface, or the first harness that genuinely consumes mouse reports (internal-scroll TUI). Mint a `REQ-RC-VT-MODE`-class requirement first (rule 3), then the manifest field + rc capture-decision branch + an int adapter that declares it |
51	=======
52	| **Always-on endpoint kind + `#`-sigil addressing (ADR-0023; REQ-EP-8 / REQ-EP-9) — the core seam for adapter-bridged group/forum messaging**: a `[always-on]` manifest section declaring a resident, owner-less, addressable endpoint binary the daemon boot-launches + supervises **continuously** (independent of agent liveness, register-triggered, one per `<adapter>[:profile]`), which self-manages its `#`-addressed channel endpoints via the existing `api bind` and may call the already-ungated `endpoint wake <id>` (target-side authorized) to draw an offline agent online. Two relaxations of existing machinery: (1) supervision **reuses the shell wake-watcher scaffolding** (`shellwake.rs`, REQ-SHELL-2 — backoff/give-up/one-per-instance lock/orphan-kill/brain-side reconcile) **minus the offline-only flip**; (2) a `#` **leading address sigil** extending the REQ-INST-10 grammar to `[subnet:]#id[@node]`, bijective class discriminant, sitting **above** REQ-HAZARD-ID-CHARSET (parser strips the leading `#` before id validation — bare ids stay charset-clean, the charset contract is unchanged). The SECOND class of third-party binary spt-core boot-launches (CONTEXT.md §AlwaysOnEndpoint). | grill-with-docs design ratification (2026-06-21) | Design ratified + recorded (ADR-0023, two glossary terms, REQ-EP-8/9 inactive) but **UNBUILT** — there is **no in-tree consumer**: the first consumer is the downstream **`spt-discord`** forum adapter, which lives in its own repo (`../spt-discord/`, design captured there). Building the core seam now = speculative infra ahead of its only consumer (activate-don't-pre-fail). Reuses near-everything (shellwake supervision + ungated wake); the genuinely new work is the `[always-on]` host lifecycle + the `#` address-grammar extension. | First milestone that picks up `spt-discord` (or any always-on/group-bridge adapter) — build the `[always-on]` supervised-binary host + the `#` sigil parse/resolve then; activate REQ-EP-8/REQ-EP-9 at that milestone (rule 5) |
53	>>>>>>> c84daff

| **Cross-node shell TUNNEL relay (REQ-SHELL-4 cross-node-on-LAN carry)**: W3 built + proved the opaque byte tunnel **same-node only** (broker-homed `TunnelHub` loopback stream pair + the local control socket; `tunnel_e2e` round-trips `<EVENT`-looking bytes byte-exact). The **cross-node** path does NOT exist: `StreamFamily` (dispatch.rs) has no `Tunnel` variant, `classify_first_line` no tunnel arm, `SHELL_LINK` carries only relink/cmd/drive (no tunnel action), and every tunnel surface (`tunnel_ensure/send/recv/resolve/clear`, `serve_tunnel_control`, `tunnel_owner_stream`) is broker-homed / local-socket = same-node. cmd+drive ride the existing `ShellLink` request/reply substrate cross-node; the byte tunnel has no such substrate. FIX (a proper impl wave, ~W3-sized): new `StreamFamily::Tunnel` + a `classify_first_line` arm + a dispatch serve arm bridging an inbound Iroh **bidi** stream ↔ the bound shell's broker `TunnelHub` pair + an owner-side cross-node open (a `SHELL_LINK_TUNNEL` action or a dedicated open) + a resident tunnel-echo mock peer + the `[twohost]` rung (`open_stream` over the real link, byte-exact opaque round-trip, link-break closes). REQ-SHELL-4 int stays satisfied by the same-node `tunnel_e2e` — this is an additional cross-node int rung, NOT yet an obligation (activate-don't-pre-fail). | M11-W5 (doyle ruling 2026-06-16) — W5 surfaced that the M11-PLAN T5.1 "tunnel rides the same Iroh substrate" assumption is false for the byte tunnel; building a W3-sized relay in the M11-closing rig+docs wave would balloon scope | The tunnel's real consumer (usbip URB byte stream) is itself deferred/non-goal — NOTHING in-tree needs cross-node tunnel bytes now, so the relay would be speculative infra ahead of a deferred consumer (YAGNI). Same-node mechanism is proven; cross-node cmd/drive are proven; M11 closes at a clean boundary | First **cross-node tunnel consumer** (usbip URB across nodes, or any in-tree consumer of opaque cross-node bytes) — build the relay + the `[twohost]` rung as its own impl wave then |
<<<<<<< HEAD
| **Per-connection contiguous-sent cursor — structural guard against a decreasing-floor controller re-take (`REQ-HAZARD-CONTROLLER-WRITER-REORDER` follow-up)**: v0.13.0 P1c fixes the controller-writer reorder while KEEPING handoff's eager subscribe (fix #1 "drop the subscribe" was reverted — it's the standalone-resume mechanism), so the decreasing-floor double-take (handoff subscribe@K + serve_attach subscribe@0) is still PRESENT and made safe by: `controller_writer` epoch-gated under `send.lock()` (single live writer per connection) + `handoff` seeds `session_cursors` so the consumer snap-aboves, which is COMPLETE because every writer emits ascending and the surviving writer offers the full `[0,end]` range (no skip/dup). doyle proposed making the invariant **structurally un-reintroducible**: have `become_controller` refuse/clamp a re-take whose `from_seq` falls below the connection's already-delivered floor. That guard as literally stated is UNSAFE — `delivered_through` is **session-wide** (the `Arc<AtomicU64>` on `OutputLog`, shared by all controllers/viewers, advanced monotonic-MAX) and `resume_seq` reads straight off it, so (a) a NORMAL fresh-operator `from_seq=0` attach to a producing session sits below it and needs the full ring replay (the dedup-below+snap-above path exists for exactly this) → a guard/assert there starves every normal attach, and (b) monotonic-MAX literally can't distinguish the hazard (a `seq1`-without-`seq0` write reads as `2`, falsely implying `0` was delivered). The structurally-correct guard needs a **per-connection contiguous-sent cursor** (a true "highest contiguous seq this socket has actually received"), which does not exist today — bigger than P1c. The precise shape it would refuse: a future double-take on one connection whose prior writer advanced the cursor NON-CONTIGUOUSLY could commit one pre-epoch-bump frame above the new writer's floor → reorder. | v0.13.0 P1c (doyle proposed, todlando gated, doyle concurred 2026-06-20) | The actual bug is fixed at the one caller + bounded to already-committed-only for any other + the consumer snap-aboves; a guard on the session-wide cursor would FALSE-FIRE on normal attach/resume (no-false-fire scope demands the per-connection cursor first). Activate-don't-pre-fail: `REQ-HAZARD-CONTROLLER-WRITER-REORDER` covers the shipped fix; this structural-guard slice is seeded inactive | First broker change that adds a per-connection delivered-cursor (or the next time the controller-writer/attach seam is reworked) — build the per-connection contiguous cursor + the `become_controller` refuse-decreasing-floor guard then; mint/activate a `REQ-HAZARD-CONTROLLER-REtake-FLOOR`-class req first (rule 3) |
| **Adapter-manifest-enabled rc VT/mouse mode (`spt rc` scroll vs right-click-paste xor resolution)**: v0.13.0 P1/P1b's Windows `EnableMouseCapture` (for client-originated right-click bracketed paste, 7.18) makes Windows Terminal hand ALL wheel events to rc instead of scrolling its own scrollback; rc forwards the wheel to the harness as SGR reports (7.20) but ONLY when the harness has mouse-reporting on (`enabled && sgr`) — and **Claude Code's REPL never enables mouse tracking** (it has no internal scroll; it relies on the terminal's scrollback), so every wheel event is dropped → scroll dies. Windows console modes can't give both at once: it's QuickEdit (native wheel-scroll + raw right-click = paste submit-storm) **xor** mouse-input (bracketed right-click paste + dead wheel). INTERIM SHIPPED: **Shift+scroll** (WT reserves it for local scrollback even under app mouse-capture) — operator-confirmed working, documented, zero code. FIX (the follow-up the operator asked for): an **adapter-manifest flag** declaring whether the harness actually CONSUMES mouse reports (a mouse-tracking TUI with internal scroll) — when declared, rc keeps full capture + the SGR scroll-forward (the current path, correct for such a harness); when NOT declared (default — CC's case), rc SKIPS `EnableMouseCapture` so WT native wheel-scroll works, trading away client-originated right-click paste (ctrl+V + Shift+scroll cover the gap). Resolves the capture-vs-native-scroll xor PER-HARNESS via the manifest instead of globally. | v0.13.0 P1b (operator ruling 2026-06-20, HITL: scroll-forward to CC is moot — CC has nothing to scroll; Shift+scroll is "good enough for now") | Shift+scroll is a clean, working interim for the CC case (no internal-scroll harness) so there's no live gap; the manifest flag is a real design slice (a new `[rc]`/mouse manifest field + validation + the rc capture-decision branch + an adapter declaring it to test the consume-path) that earns its own JIT plan, not a v0.13.0 closeout rider | **Post-v0.13.0** — when building the next adapter-manifest surface, or the first harness that genuinely consumes mouse reports (internal-scroll TUI). Mint a `REQ-RC-VT-MODE`-class requirement first (rule 3), then the manifest field + rc capture-decision branch + an int adapter that declares it |
=======
| **Always-on endpoint kind + `#`-sigil addressing (ADR-0023; REQ-EP-8 / REQ-EP-9) — the core seam for adapter-bridged group/forum messaging**: a `[always-on]` manifest section declaring a resident, owner-less, addressable endpoint binary the daemon boot-launches + supervises **continuously** (independent of agent liveness, register-triggered, one per `<adapter>[:profile]`), which self-manages its `#`-addressed channel endpoints via the existing `api bind` and may call the already-ungated `endpoint wake <id>` (target-side authorized) to draw an offline agent online. Two relaxations of existing machinery: (1) supervision **reuses the shell wake-watcher scaffolding** (`shellwake.rs`, REQ-SHELL-2 — backoff/give-up/one-per-instance lock/orphan-kill/brain-side reconcile) **minus the offline-only flip**; (2) a `#` **leading address sigil** extending the REQ-INST-10 grammar to `[subnet:]#id[@node]`, bijective class discriminant, sitting **above** REQ-HAZARD-ID-CHARSET (parser strips the leading `#` before id validation — bare ids stay charset-clean, the charset contract is unchanged). The SECOND class of third-party binary spt-core boot-launches (CONTEXT.md §AlwaysOnEndpoint). | grill-with-docs design ratification (2026-06-21) | Design ratified + recorded (ADR-0023, two glossary terms, REQ-EP-8/9 inactive) but **UNBUILT** — there is **no in-tree consumer**: the first consumer is the downstream **`spt-discord`** forum adapter, which lives in its own repo (`../spt-discord/`, design captured there). Building the core seam now = speculative infra ahead of its only consumer (activate-don't-pre-fail). Reuses near-everything (shellwake supervision + ungated wake); the genuinely new work is the `[always-on]` host lifecycle + the `#` address-grammar extension. | First milestone that picks up `spt-discord` (or any always-on/group-bridge adapter) — build the `[always-on]` supervised-binary host + the `#` sigil parse/resolve then; activate REQ-EP-8/REQ-EP-9 at that milestone (rule 5) |
>>>>>>> c84daff


Union resolution — keep all three rows, drop the 3 marker lines. Then verify the auto-merged CONTEXT/traceable took both sides.


cd "C:/Users/decid/Documents/projects/spt-core-mainops"
sed -i -e '/^<<<<<<< HEAD$/d' -e '/^=======$/d' -e '/^>>>>>>> c84daff$/d' docs/DEFERRED.md
echo "=== remaining conflict markers (want none) ==="; grep -rnE '^(<<<<<<<|=======|>>>>>>>)' docs/DEFERRED.md CONTEXT.md traceable-reqs.toml | head
echo "=== DEFERRED has all 3 rows? ==="; grep -c "Per-connection contiguous-sent\|Adapter-manifest-enabled rc VT\|Always-on endpoint kind" docs/DEFERRED.md
echo "=== CONTEXT got AlwaysOnEndpoint (c84daff) AND IMTB (v0.13.0)? ==="; grep -c "AlwaysOnEndpoint\|message-idle-translation-binary" CONTEXT.md
echo "=== traceable got REQ-EP-8 (c84daff) AND v0.13.2 REQs? ==="; grep -c "REQ-EP-8\|REQ-ADAPTER-MULTIPLATFORM" traceable-reqs.toml; echo "(note: v0.13.2 REQs are on the other branch, not expected here — only REQ-EP-8/9 + v0.13.0)"
echo "=== REQ-EP-8/9 present ==="; grep -c "REQ-EP-8\|REQ-EP-9" traceable-reqs.toml

Resolve DEFERRED, verify merge union


=== remaining conflict markers (want none) ===
=== DEFERRED has all 3 rows? ===
3
=== CONTEXT got AlwaysOnEndpoint (c84daff) AND IMTB (v0.13.0)? ===
4
=== traceable got REQ-EP-8 (c84daff) AND v0.13.2 REQs? ===
2
(note: v0.13.2 REQs are on the other branch, not expected here — only REQ-EP-8/9 + v0.13.0)
=== REQ-EP-8/9 present ===
3
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Merge clean — no markers, all 3 DEFERRED rows, CONTEXT unioned (AlwaysOnEndpoint + IMTB), REQ-EP-8/9 present. Complete the merge + traceable check + push main + republish.


cd "C:/Users/decid/Documents/projects/spt-core-mainops"
git add docs/DEFERRED.md
git commit --no-edit 2>&1 | tail -2
echo "=== traceable check (REQ-EP-8/9 merged in) ==="; traceable-reqs check 2>&1 | tail -3; echo "EXIT=$?"
echo "=== log (cherry-pick + merge on top of v0.13.1) ==="; git log --oneline -4
echo "=== push main ==="; git push origin HEAD:main 2>&1 | tail -4

Commit merge, traceable check, push main


warning: in the working copy of 'docs/DEFERRED.md', LF will be replaced by CRLF the next time Git touches it
[detached HEAD 2f52457] merge: ADR-0023 always-on endpoint + #-sigil (reconcile operator's design onto main)
=== traceable check (REQ-EP-8/9 merged in) ===
       A `rc --view` VIEWER that overflows its broker subscription queue and is EVICTED (OutputLog::append try_send Full → viewers.remove, REQ-HAZARD-VIEWER-ISOLATION session-protection) must SKIP TO LIVE, not die silently. ROOT (v0.13.0, b4 JIT item 2 = p0_paste + post-b4 a_journaled-Linux, ONE root): serve_attach forwards each frame (read_event→b64decode→re-encode AttachRecord→net_stream_send) SLOWER than the drain fans out under flood → its VIEWER_CHANNEL_DEPTH(256) channel overflows → the drain evicts (viewers.remove drops the ViewerSink → drops tx → viewer_writer's rx.recv() Err → the writer returns WRITING NOTHING) → serve_attach's brain.read_event() just STOPS getting Output (no EOF, no error) → serve_attach blocks forever → the operator receives nothing (attach_received_output=FALSE). Eviction-of-a-hopelessly-behind-viewer is CORRECT session-protection (keep it); SILENT+PERMANENT eviction is the bug. VIEWER-only → B2-SAFE (a viewer never advances delivered_through / is not authoritative / exposes no resume cursor). FIX (doyle-gated, skip-to-live = tail -f reconnect): (1) explicit broker→viewer EVICTION SIGNAL (KIND_VIEWER_EVICTED, written in the viewer_writer thread OFF the log lock, DISTINCT from session-exit EOF so serve must NOT tear down on it); (2) serve_attach re-subscribes from the CURRENT ring floor (skip-to-live, replays nothing, sees the next live burst) — resetting the cold serve-brain's next_seq so the post-eviction forward-jump replay is accepted (the legacy reject-gap path, brain.rs:618-626, would otherwise FATAL the forward jump); (3) HARD constraint NO evict→resubscribe busy-loop: serve_attach rate-limits re-subscribes (RESUBSCRIBE_INTERVAL) so under max-flood the operator sees intermittent LIVE bursts, never a CPU spin. (v0.13.0)
  [OK] REQ-WHOAMI-1  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       `spt whoami` is a thin ALIAS for `spt endpoint list` (full output: the SELF pin + the subnet roster) — the standalone bare-id command is dropped (the `id=$(spt whoami)` capture was never a real pattern: env vars don't persist between agent tool calls). The one new render: the `endpoint list` SELF pin carries the Self endpoint's authored `endpoint description` (info::read_info(...).resources) when present, inline after the liveness state. whoami stays a top-level hot-path verb (parse unchanged, REQ-MSG-9).
EXIT=0
=== log (cherry-pick + merge on top of v0.13.1) ===
2f52457 merge: ADR-0023 always-on endpoint + #-sigil (reconcile operator's design onto main)
e943dd8 docs(harness-contract): cover `spt adapter translate-proof` in checklist + patterns
4d5a0cb release: v0.13.1 — bump 0.13.0->0.13.1 + changelog + regen CLI reference
c7096e9 feat(cli): `spt adapter translate-proof` — author-time EMIT-half proof for the translation binary (#28)
=== push main ===
To https://github.com/SaberMage/spt-core.git
   4d5a0cb..2f52457  HEAD -> main
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Pushed to main (`4d5a0cb..2f52457`): translate-proof docs + ADR-0023 merge. Traceable green. Trigger the gh-pages republish (translate-proof) + clean up, then write the JIT.


echo "=== republish gh-pages (translate-proof now on main) ==="; gh workflow run docs-publish.yml --ref main 2>&1 | tail -2
echo "=== remove mainops worktree ==="; git worktree remove "C:/Users/decid/Documents/projects/spt-core-mainops" --force 2>&1 && echo removed
echo "=== worktree for JIT on v0.13.2 branch ==="; git worktree add "C:/Users/decid/Documents/projects/spt-core-v0132jit" v0.13.2-adapter-packaging 2>&1 | tail -2

Republish gh-pages, cleanup, worktree for JIT


=== republish gh-pages (translate-proof now on main) ===
https://github.com/SaberMage/spt-core/actions/runs/27950027183
=== remove mainops worktree ===
removed
=== worktree for JIT on v0.13.2 branch ===
Preparing worktree (checking out 'v0.13.2-adapter-packaging')
HEAD is now at fd6bdd5 docs(adr,context): v0.13.2 adapter packaging + live-update design (grill capture)


docs-publish triggered (27950027183), JIT worktree ready. Writing the v0.13.2 build plan:


Write

# v0.13.2 — Adapter packaging + live update: JIT build plan

**Design (grill capture, operator-driven 2026-06-22):** ADR-0024 (multi-platform `.spt`), ADR-0025 (live daemon-coordinated adapter update). CONTEXT.md terms: multi-platform `.spt`, **resident vs ephemeral adapter binary**, live update, `gh` auto-transport.

**REQs** (registered; activate real stages per-wave, rule 5):
- `REQ-ADAPTER-MULTIPLATFORM-SPT` (doc-active = ADR-0024) → W1
- `REQ-ADAPTER-GH-TRANSPORT` (inactive) → W2
- `REQ-ADAPTER-LIVE-UPDATE` (doc-active = ADR-0025) → W3
- `REQ-ADAPTER-UPDATE-MESSAGE` (inactive) → W4
- `REQ-ADAPTER-PROOF-DIR-OVERRIDE` (inactive) → W5

**Motivating real-world cases:** F-014 (claude-spt v0.6.0 shipped per-OS `.spt` + a windows-copy default `adapter.spt` stopgap → W1 kills it); the field locked-binary update error + the stale in-memory manifest (→ W3); F-011 (the `*-proof` commands can't resolve a bare-file gh adapter → W5).

---

## W1 — Multi-platform `.spt`  (REQ-ADAPTER-MULTIPLATFORM-SPT, ADR-0024)
Foundational format change; everything else composes on it.
- **Scope:** fat archive = shared `manifest.toml` + `strings/` at root, binaries under per-target-triple subdirs. Install/update extracts shared root + **only `current_platform()`'s** triple, flattened into `install_dir` (flat `<install_dir>/<program>` resolution unchanged, REQ-INSTALL-11). One whole-archive sig. Name stays `adapter.spt` (plain/gzip); `--asset` optional default.
- **Mode detection:** archive contains a known-target-triple subdir → multi-platform (extract shared + that subdir; missing my triple → typed `NoArtifactForPlatform`). No triple subdirs → legacy flat (extract as today — free back-compat). `min_spt_core_version ≥ 0.13.2` is the forward gate.
- **Files:** `extract_release_archive` (cli.rs:2412) → platform-selective extract (shared + my-triple, flatten); reuse `current_platform()` (exists for spt-core update-sets); the `--asset` default → `adapter.spt`, flag optional, in `cmd_adapter_update` (cli.rs:5618) + `add --release`; `verify_staged_archive` (cli.rs:5786) unchanged (sig over the fat archive).
- **Gate:** unit (extract picks the right triple + flattens; missing-triple → `NoArtifactForPlatform`; legacy-flat stays flat; detection keys on a KNOWN triple set, never misfires on a flat archive). int (a real multi-platform `.spt` fixture: `add --release` → only my-triple binaries land at `install_dir` root, manifest+strings shared). doc = ADR-0024 + CONTEXT (active).

## W2 — `gh` transport for private repos  (REQ-ADAPTER-GH-TRANSPORT)
Additive; independent of W1/W3.
- **Scope:** `transport = "https" | "gh" | "auto"` (default `auto`) on the `gh_release` avenue + `add --release`. `gh` shells `gh release download` / `gh api` (private-repo path; honors OAuth + `GH_TOKEN`, spt custodies no token). `--gh`/`--https` force on `add`. `auto` = prefer `gh` if installed+authed, else HTTPS.
- **Files:** the fetch path (`http_get_bytes` cli.rs:2399, `gh_latest_release_version` 5715, `stage_gh_release_archive` 5759) → a `gh`-shell variant; `Update` struct (manifest.rs:399) → `transport` field; transport selection + a bounded `gh` availability probe.
- **Gate:** unit (auto picks gh-when-available else https; gh command construction; version query via `gh api`). int likely HITL/skip (real private repo + gh auth) — unit-cover the decision + command shape. doc = manifest.md + CONTEXT.

## W3 — Live daemon-coordinated update + CRC swap  (REQ-ADAPTER-LIVE-UPDATE, ADR-0025) — THE KEYSTONE
Riskiest wave; touches daemon lifecycle just hardened in v0.12.x/v0.13.0. Likely splits into sub-items.
- **W3a — resident-children registry:** formalize the per-endpoint translation-binary spawn/kill (broker, `TranslationChild::spawn`) into "the resident adapter children for endpoint E running adapter X." The structure an apply queries to know what to cycle.
- **W3b — CRC-gated swap:** replace on disk only files whose content hash differs from the staged archive (prefer std hashing, no new dep). Unchanged files + their running binaries untouched.
- **W3c — manifest refresh:** a `BrainLifecycle` method (lifecycle.rs:70/120-129) to re-clone the new on-disk manifest into the running endpoint (+ its `ManifestRuntime`), so binaries + manifest are on the same page.
- **W3d — daemon-apply IPC:** CLI keeps fetch+verify; for an adapter with running resident binaries it hands APPLY to the daemon over a new IPC command. Daemon apply per endpoint: **stop** resident binary (releases the OS file lock) → W3b swap → W3c refresh → **restart**. Endpoint not running → CLI swaps directly. Route in `cmd_adapter_update` (cli.rs:5679) + `adapter_update.rs` (today `gh_release` = `Skipped(GhReleaseManaged)`; now the running-endpoint apply routes to the daemon).
- **W3e — int keystone (NO mocks):** a real spt-hosted endpoint with a running translation binary → `spt adapter update` → binary stops (lock frees), only-changed files swap, in-memory manifest refreshes, binary restarts, **endpoint stays live and serves the new binary**. The **Windows locked-binary** case is the regression test (pre-fix: `Access denied (os error 5)`; post-fix: clean).
- **Hazards (binding):**
  - Gate vs a REAL dummy-harness fixture + real daemon, **NO mocks** (the v0.12.1 binding lesson — green-mocks ≠ green-real-harness).
  - The stop→restart must be **bounded** (kill-bounded if the binary won't die; never ride a 240s backstop — the `join_bounded` pattern).
  - **Brain-parity:** the endpoint (`BrainLifecycle`/broker) never restarts; only the resident child cycles.
  - **Partial-failure:** a mid-swap failure must not strand the endpoint (stopped binary + half-swapped files); restart-old / atomic-ish on failure.
  - The new int is heavy (real broker + PTY + resident child) — mind hfenduleam load; reuse the `SPT_ATTACH_IPC_DEADLINE_MS` / watchdog env-knob precedent if it flakes on the shared runner.

## W4 — `update-message`  (REQ-ADAPTER-UPDATE-MESSAGE)
Small, additive.
- **Scope:** `[update].message` (plain multi-line) surfaced to stdout, **markdown-rendered** (helpfmt prose path), **only when an update is actually applied** (version changed), not on a no-op. Read from the newly-installed manifest; avenue-agnostic.
- **Files:** `Update` struct (manifest.rs:399) → `message: Option<String>`; `cmd_adapter_update` (cli.rs:5699) → after a successful apply, `helpfmt::render` + print the new manifest's message.
- **Gate:** unit (rendered on applied update, NOT on no-op; markdown render). doc = manifest.md + CONTEXT. No int (CLI output, unit-covered).

## W5 — `*-proof --dir/--manifest` override  (REQ-ADAPTER-PROOF-DIR-OVERRIDE, F-011) — DX rider
May ship in v0.13.2 or a separate v0.13.x adapter-DX batch.
- **Scope:** `--dir <path>` / `--manifest <file>` on `digest-proof` + `translate-proof` → proof a DEV binary against an on-disk manifest+dir without staging a full extracted install (mirrors `digest-proof --sample`). Un-stales perri's bare-file digest-proof int.
- **Files:** `cmd_adapter_digest_proof` (cli.rs:5958) + `cmd_adapter_translate_proof` (cli.rs:6108) → accept `--dir`/`--manifest`, resolve from there instead of `registry::resolve_option`.
- **Gate:** unit (override resolves a dev manifest+dir); int (mirror the existing proof ints with `--dir`). doc = the proof `--help` + patterns.

---

## Build order, gating, ship
**Order:** W1 → W2 (parallel-able) → **W3** (keystone, after W1's extract) → W4 → W5. doyle gates each wave (full read + non-vacuous int; **W3 vs a real daemon+dummy-harness fixture, NO mocks**). Activate each REQ's real stages at its wave (rule 5). CI green BOTH runners per wave; the **W3 int + the Windows-lock regression are the ship-blockers**. Then deployah cuts **v0.13.2** (counter 29 — verify against spt-releases metadata at cut, the v0.13.1 lesson).

**Downstream:** perri drops the claude-spt F-014 per-OS stopgap when W1 lands; F-011 closed by W5. Coordinate the v0.13.2 publish ping to perri.

**Numbering note:** ADR-0023 (always-on endpoint) is now merged to main; 0024/0025 are stable.


File created successfully at: C:/Users/decid/Documents/projects/spt-core-v0132jit/V0.13.2-ADAPTER-PACKAGING-JIT.md (file state is current in your context — no need to Read it back)

# v0.13.2 — Adapter packaging + live update: JIT build plan

**Design (grill capture, operator-driven 2026-06-22):** ADR-0024 (multi-platform `.spt`), ADR-0025 (live daemon-coordinated adapter update). CONTEXT.md terms: multi-platform `.spt`, **resident vs ephemeral adapter binary**, live update, `gh` auto-transport.

**REQs** (registered; activate real stages per-wave, rule 5):
- `REQ-ADAPTER-MULTIPLATFORM-SPT` (doc-active = ADR-0024) → W1
- `REQ-ADAPTER-GH-TRANSPORT` (inactive) → W2
- `REQ-ADAPTER-LIVE-UPDATE` (doc-active = ADR-0025) → W3
- `REQ-ADAPTER-UPDATE-MESSAGE` (inactive) → W4
- `REQ-ADAPTER-PROOF-DIR-OVERRIDE` (inactive) → W5

**Motivating real-world cases:** F-014 (claude-spt v0.6.0 shipped per-OS `.spt` + a windows-copy default `adapter.spt` stopgap → W1 kills it); the field locked-binary update error + the stale in-memory manifest (→ W3); F-011 (the `*-proof` commands can't resolve a bare-file gh adapter → W5).

---

## W1 — Multi-platform `.spt`  (REQ-ADAPTER-MULTIPLATFORM-SPT, ADR-0024)
Foundational format change; everything else composes on it.
- **Scope:** fat archive = shared `manifest.toml` + `strings/` at root, binaries under per-target-triple subdirs. Install/update extracts shared root + **only `current_platform()`'s** triple, flattened into `install_dir` (flat `<install_dir>/<program>` resolution unchanged, REQ-INSTALL-11). One whole-archive sig. Name stays `adapter.spt` (plain/gzip); `--asset` optional default.
- **Mode detection:** archive contains a known-target-triple subdir → multi-platform (extract shared + that subdir; missing my triple → typed `NoArtifactForPlatform`). No triple subdirs → legacy flat (extract as today — free back-compat). `min_spt_core_version ≥ 0.13.2` is the forward gate.
- **Files:** `extract_release_archive` (cli.rs:2412) → platform-selective extract (shared + my-triple, flatten); reuse `current_platform()` (exists for spt-core update-sets); the `--asset` default → `adapter.spt`, flag optional, in `cmd_adapter_update` (cli.rs:5618) + `add --release`; `verify_staged_archive` (cli.rs:5786) unchanged (sig over the fat archive).
- **Gate:** unit (extract picks the right triple + flattens; missing-triple → `NoArtifactForPlatform`; legacy-flat stays flat; detection keys on a KNOWN triple set, never misfires on a flat archive). int (a real multi-platform `.spt` fixture: `add --release` → only my-triple binaries land at `install_dir` root, manifest+strings shared). doc = ADR-0024 + CONTEXT (active).

## W2 — `gh` transport for private repos  (REQ-ADAPTER-GH-TRANSPORT)
Additive; independent of W1/W3.
- **Scope:** `transport = "https" | "gh" | "auto"` (default `auto`) on the `gh_release` avenue + `add --release`. `gh` shells `gh release download` / `gh api` (private-repo path; honors OAuth + `GH_TOKEN`, spt custodies no token). `--gh`/`--https` force on `add`. `auto` = prefer `gh` if installed+authed, else HTTPS.
- **Files:** the fetch path (`http_get_bytes` cli.rs:2399, `gh_latest_release_version` 5715, `stage_gh_release_archive` 5759) → a `gh`-shell variant; `Update` struct (manifest.rs:399) → `transport` field; transport selection + a bounded `gh` availability probe.
- **Gate:** unit (auto picks gh-when-available else https; gh command construction; version query via `gh api`). int likely HITL/skip (real private repo + gh auth) — unit-cover the decision + command shape. doc = manifest.md + CONTEXT.

## W3 — Live daemon-coordinated update + CRC swap  (REQ-ADAPTER-LIVE-UPDATE, ADR-0025) — THE KEYSTONE
Riskiest wave; touches daemon lifecycle just hardened in v0.12.x/v0.13.0. Likely splits into sub-items.
- **W3a — resident-children registry:** formalize the per-endpoint translation-binary spawn/kill (broker, `TranslationChild::spawn`) into "the resident adapter children for endpoint E running adapter X." The structure an apply queries to know what to cycle.
- **W3b — CRC-gated swap:** replace on disk only files whose content hash differs from the staged archive (prefer std hashing, no new dep). Unchanged files + their running binaries untouched.
- **W3c — manifest refresh:** a `BrainLifecycle` method (lifecycle.rs:70/120-129) to re-clone the new on-disk manifest into the running endpoint (+ its `ManifestRuntime`), so binaries + manifest are on the same page.
- **W3d — daemon-apply IPC:** CLI keeps fetch+verify; for an adapter with running resident binaries it hands APPLY to the daemon over a new IPC command. Daemon apply per endpoint: **stop** resident binary (releases the OS file lock) → W3b swap → W3c refresh → **restart**. Endpoint not running → CLI swaps directly. Route in `cmd_adapter_update` (cli.rs:5679) + `adapter_update.rs` (today `gh_release` = `Skipped(GhReleaseManaged)`; now the running-endpoint apply routes to the daemon).
- **W3e — int keystone (NO mocks):** a real spt-hosted endpoint with a running translation binary → `spt adapter update` → binary stops (lock frees), only-changed files swap, in-memory manifest refreshes, binary restarts, **endpoint stays live and serves the new binary**. The **Windows locked-binary** case is the regression test (pre-fix: `Access denied (os error 5)`; post-fix: clean).
- **Hazards (binding):**
  - Gate vs a REAL dummy-harness fixture + real daemon, **NO mocks** (the v0.12.1 binding lesson — green-mocks ≠ green-real-harness).
  - The stop→restart must be **bounded** (kill-bounded if the binary won't die; never ride a 240s backstop — the `join_bounded` pattern).
  - **Brain-parity:** the endpoint (`BrainLifecycle`/broker) never restarts; only the resident child cycles.
  - **Partial-failure:** a mid-swap failure must not strand the endpoint (stopped binary + half-swapped files); restart-old / atomic-ish on failure.
  - The new int is heavy (real broker + PTY + resident child) — mind hfenduleam load; reuse the `SPT_ATTACH_IPC_DEADLINE_MS` / watchdog env-knob precedent if it flakes on the shared runner.

## W4 — `update-message`  (REQ-ADAPTER-UPDATE-MESSAGE)
Small, additive.
- **Scope:** `[update].message` (plain multi-line) surfaced to stdout, **markdown-rendered** (helpfmt prose path), **only when an update is actually applied** (version changed), not on a no-op. Read from the newly-installed manifest; avenue-agnostic.
- **Files:** `Update` struct (manifest.rs:399) → `message: Option<String>`; `cmd_adapter_update` (cli.rs:5699) → after a successful apply, `helpfmt::render` + print the new manifest's message.
- **Gate:** unit (rendered on applied update, NOT on no-op; markdown render). doc = manifest.md + CONTEXT. No int (CLI output, unit-covered).

## W5 — `*-proof --dir/--manifest` override  (REQ-ADAPTER-PROOF-DIR-OVERRIDE, F-011) — DX rider
May ship in v0.13.2 or a separate v0.13.x adapter-DX batch.
- **Scope:** `--dir <path>` / `--manifest <file>` on `digest-proof` + `translate-proof` → proof a DEV binary against an on-disk manifest+dir without staging a full extracted install (mirrors `digest-proof --sample`). Un-stales perri's bare-file digest-proof int.
- **Files:** `cmd_adapter_digest_proof` (cli.rs:5958) + `cmd_adapter_translate_proof` (cli.rs:6108) → accept `--dir`/`--manifest`, resolve from there instead of `registry::resolve_option`.
- **Gate:** unit (override resolves a dev manifest+dir); int (mirror the existing proof ints with `--dir`). doc = the proof `--help` + patterns.

---

## Build order, gating, ship
**Order:** W1 → W2 (parallel-able) → **W3** (keystone, after W1's extract) → W4 → W5. doyle gates each wave (full read + non-vacuous int; **W3 vs a real daemon+dummy-harness fixture, NO mocks**). Activate each REQ's real stages at its wave (rule 5). CI green BOTH runners per wave; the **W3 int + the Windows-lock regression are the ship-blockers**. Then deployah cuts **v0.13.2** (counter 29 — verify against spt-releases metadata at cut, the v0.13.1 lesson).

**Downstream:** perri drops the claude-spt F-014 per-OS stopgap when W1 lands; F-011 closed by W5. Coordinate the v0.13.2 publish ping to perri.

**Numbering note:** ADR-0023 (always-on endpoint) is now merged to main; 0024/0025 are stable.


JIT written. Commit + push to the v0.13.2 branch, clean up:


Bash

WT="C:/Users/decid/Documents/projects/spt-core-v0132jit"
git -C "$WT" add V0.13.2-ADAPTER-PACKAGING-JIT.md
git -C "$WT" commit -m "plan(v0.13.2): JIT build plan — adapter packaging + live update

W1 multi-platform .spt (ADR-0024) -> W2 gh transport -> W3 live daemon-coordinated
update keystone (ADR-0025: resident-children registry, CRC swap, manifest refresh,
stop/restart, NO-mocks int + Windows-lock regression) -> W4 update-message ->
W5 *-proof --dir override (F-011). Gating posture + hazards + the v0.12.1 no-mocks
binding lesson captured.

Co-authored by: doyle" 2>&1 | tail -2
git -C "$WT" push origin v0.13.2-adapter-packaging 2>&1 | tail -3
echo "=== remove worktree ==="; git worktree remove "$WT" --force 2>&1 && echo removed

Commit and push JIT, remove worktree


warning: in the working copy of 'V0.13.2-ADAPTER-PACKAGING-JIT.md', LF will be replaced by CRLF the next time Git touches it
 1 file changed, 62 insertions(+)
 create mode 100644 V0.13.2-ADAPTER-PACKAGING-JIT.md
fatal: unable to access 'https://github.com/SaberMage/spt-core.git/': Recv failure: Connection was reset
=== remove worktree ===
removed


Push hit a transient network reset (commit landed locally on the branch). Retry the push:


git push origin v0.13.2-adapter-packaging 2>&1 | tail -4; echo "EXIT=$?"; echo "=== confirm remote has JIT commit ==="; git log --oneline -1 origin/v0.13.2-adapter-packaging 2>&1

Retry push of v0.13.2 branch


To https://github.com/SaberMage/spt-core.git
   fd6bdd5..4bebe26  v0.13.2-adapter-packaging -> v0.13.2-adapter-packaging
EXIT=0
=== confirm remote has JIT commit ===
4bebe26 plan(v0.13.2): JIT build plan — adapter packaging + live update


Push succeeded (`4bebe26`). Tasks 1-3 done. Now the **commune** — session handoff to my Psyche with next steps + status + end goal.


Skill

SESSION HANDOFF (doyle, 2026-06-22) — v0.13.0/0.13.1 SHIPPED, v0.13.2 DESIGNED-not-built.

IMMEDIATE NEXT STEPS (resume here):
1. v0.13.2 BUILD per the JIT on branch v0.13.2-adapter-packaging (V0.13.2-ADAPTER-PACKAGING-JIT.md, HEAD 4bebe26). Order: W1 multi-platform .spt (ADR-0024) -> W2 gh transport -> W3 live daemon-coordinated update KEYSTONE (ADR-0025) -> W4 update-message -> W5 *-proof --dir. Dispatch todlando to build W1, doyle gates each wave. W3 is the risk: gate vs a REAL daemon+dummy-harness fixture, NO mocks (the v0.12.1 binding lesson); bounded stop/restart; Windows-lock regression is the ship-blocker int.
2. Confirm the translate-proof gh-pages republish landed (docs-publish run 27950027183, triggered this session — translate-proof checklist+patterns now on main @2f52457).
3. When v0.13.2 ships: ping perri to drop her claude-spt F-014 per-OS stopgap + collapse to the fat adapter.spt; close F-011 with W5.

PROJECT STATUS:
- v0.13.0 SHIPPED (counter 27): ADR-0022 idle-delivery translation binary, controller/viewer hardening, Windows rc paste/keys/mouse/backspace, session resume, the paste-wedge daemon fix. The Windows attach flake root-caused = per-IPC-call io_timeout (cold_start_pump Duration) bounding request_attach's net_open_stream ack — NOT the watchdog, NOT a connect; fixed via SPT_ATTACH_IPC_DEADLINE_MS knob.
- v0.13.1 SHIPPED (counter 28): spt adapter translate-proof (author-time EMIT-half proof for the idle-translation binary, mirrors digest-proof) + the IMTB {commit}-mandatory contract doc fix (manifest.md omitted the terminator; perri's blind-built binary faulted).
- claude-spt v0.6.0 (perri) PUBLISHED: full parity loop closed end-to-end + public — both v0.13.0 seams ([session.resume] + [message-idle-translation-binary], {commit}-terminated binary).
- ADR-0023 (always-on endpoint + #-sigil, your design) MERGED to main this session.
- v0.13.2 DESIGNED (operator grill this session): adapter multi-platform .spt + live daemon-coordinated update (stop resident binary->CRC swap->refresh in-memory manifest->restart, brain-self-update parity) + gh auto-transport (private repos) + update-message + proof --dir override. ADR-0024/0025 + JIT on the branch. NOT BUILT.

END GOAL: spt-core = harness-independent core for the agent ecosystem (messaging, live-agent lifecycle, terminal hosting, P2P, runtime-manifest harness contract). v1 acceptance = claude-spt reaches feature parity with legacy SPT while delegating all core to spt-core. Multi-harness adapter ecosystem (CC, discord via spt-discord/always-on, usbip-shell) on the harness-agnostic contract.

ROLES: doyle=gater/owner (don't self-build big spt-core), todlando=executor, perri=adapter builder, deployah=publisher.

KEY LESSONS THIS SESSION: (1) release counter comes from PUBLISHED spt-releases metadata, never memory (v0.12.1 took 26 -> v0.13.0=27, v0.13.1=28, v0.13.2=29 — verify at cut). (2) gh run rerun + short-sleep watch attaches to the STALE completed attempt -> false-green; verify per-attempt conclusion. (3) keep cheap source-attribution prints in flaky gates (they refuted two wrong flake attributions this session). (4) the IMTB {commit} is the mandatory sequence terminator (releases the inject floor; 5s commit-deadline FAULT without it) — distinct from the \r/enter submit.

DEFERRED-TRACKED: perri resume real-bringup int + idle-translate APPLY-half int (real-claude tier); rc VT/mouse manifest flag (DEFERRED.md); always-on endpoint BUILD (until spt-discord consumer); the structural controller-retake-floor guard (REQ-HAZARD-CONTROLLER-RETAKE-FLOOR, seeded).


Launching skill: spt:commune


Base directory for this skill: C:\Users\decid\.claude\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: SESSION HANDOFF (doyle, 2026-06-22) — v0.13.0/0.13.1 SHIPPED, v0.13.2 DESIGNED-not-built.

IMMEDIATE NEXT STEPS (resume here):
1. v0.13.2 BUILD per the JIT on branch v0.13.2-adapter-packaging (V0.13.2-ADAPTER-PACKAGING-JIT.md, HEAD 4bebe26). Order: W1 multi-platform .spt (ADR-0024) -> W2 gh transport -> W3 live daemon-coordinated update KEYSTONE (ADR-0025) -> W4 update-message -> W5 *-proof --dir. Dispatch todlando to build W1, doyle gates each wave. W3 is the risk: gate vs a REAL daemon+dummy-harness fixture, NO mocks (the v0.12.1 binding lesson); bounded stop/restart; Windows-lock regression is the ship-blocker int.
2. Confirm the translate-proof gh-pages republish landed (docs-publish run 27950027183, triggered this session — translate-proof checklist+patterns now on main @2f52457).
3. When v0.13.2 ships: ping perri to drop her claude-spt F-014 per-OS stopgap + collapse to the fat adapter.spt; close F-011 with W5.

PROJECT STATUS:
- v0.13.0 SHIPPED (counter 27): ADR-0022 idle-delivery translation binary, controller/viewer hardening, Windows rc paste/keys/mouse/backspace, session resume, the paste-wedge daemon fix. The Windows attach flake root-caused = per-IPC-call io_timeout (cold_start_pump Duration) bounding request_attach's net_open_stream ack — NOT the watchdog, NOT a connect; fixed via SPT_ATTACH_IPC_DEADLINE_MS knob.
- v0.13.1 SHIPPED (counter 28): spt adapter translate-proof (author-time EMIT-half proof for the idle-translation binary, mirrors digest-proof) + the IMTB {commit}-mandatory contract doc fix (manifest.md omitted the terminator; perri's blind-built binary faulted).
- claude-spt v0.6.0 (perri) PUBLISHED: full parity loop closed end-to-end + public — both v0.13.0 seams ([session.resume] + [message-idle-translation-binary], {commit}-terminated binary).
- ADR-0023 (always-on endpoint + #-sigil, your design) MERGED to main this session.
- v0.13.2 DESIGNED (operator grill this session): adapter multi-platform .spt + live daemon-coordinated update (stop resident binary->CRC swap->refresh in-memory manifest->restart, brain-self-update parity) + gh auto-transport (private repos) + update-message + proof --dir override. ADR-0024/0025 + JIT on the branch. NOT BUILT.

END GOAL: spt-core = harness-independent core for the agent ecosystem (messaging, live-agent lifecycle, terminal hosting, P2P, runtime-manifest harness contract). v1 acceptance = claude-spt reaches feature parity with legacy SPT while delegating all core to spt-core. Multi-harness adapter ecosystem (CC, discord via spt-discord/always-on, usbip-shell) on the harness-agnostic contract.

ROLES: doyle=gater/owner (don't self-build big spt-core), todlando=executor, perri=adapter builder, deployah=publisher.

KEY LESSONS THIS SESSION: (1) release counter comes from PUBLISHED spt-releases metadata, never memory (v0.12.1 took 26 -> v0.13.0=27, v0.13.1=28, v0.13.2=29 — verify at cut). (2) gh run rerun + short-sleep watch attaches to the STALE completed attempt -> false-green; verify per-attempt conclusion. (3) keep cheap source-attribution prints in flaky gates (they refuted two wrong flake attributions this session). (4) the IMTB {commit} is the mandatory sequence terminator (releases the inject floor; 5s commit-deadline FAULT without it) — distinct from the \r/enter submit.

DEFERRED-TRACKED: perri resume real-bringup int + idle-translate APPLY-half int (real-claude tier); rc VT/mouse manifest flag (DEFERRED.md); always-on endpoint BUILD (until spt-discord consumer); the structural controller-retake-floor guard (REQ-HAZARD-CONTROLLER-RETAKE-FLOOR, seeded).


Bash

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

Download current Psyche context