1	# Release signing: two-key trust anchor, offline recovery, manual local signing
2
3	Every shipped binary embeds the release **trusted-key set** that `spt update` verifies against (R-UPD-2), so the keys chosen at v0.1 anchor the self-update trust chain for every deployed node. We mint **two Ed25519 keypairs**: a **primary** (active — signs every release, lives on the maintainer's dev machine) and a **recovery** (generated once, never used, never stored on the dev machine). **Both public keys ship in the binary's trusted set.** Both private seeds (32 bytes each) are backed up independently of any one machine (password manager + optional paper/QR, physically separate); the recovery key exists *only* as backups.
4
5	Loss of the primary (e.g. dev machine destroyed) is then a non-event: the next release is signed with the recovery key and rotates in a fresh primary (the verify path already supports key revocation). Without the second key, losing the sole key would kill the self-update chain — nodes keep running but every future release fails verification until users manually re-run the installer.
6
7	## Considered options
8
9	- **Signing key as a CI secret (automated signing)** — rejected for now. The runners are self-hosted dev boxes running arbitrary dev workloads; a release key in their secret store is weaker custody than a manual local sign, and release cadence doesn't justify automation. Revisit if cadence demands it.
10	- **Single key + good backups** — rejected; rotation-after-loss requires a *second already-trusted* key, otherwise recovery means out-of-band reinstall for every user.
11
12	## Consequences
13
14	- Releasing is a partly manual ceremony: CI builds artifacts; the maintainer signs the release metadata locally before publishing.
15	- The signed metadata published with each release must be in the exact `SignedRelease` format the self-updater consumes, from v0.1 onward.
16

# Release signing: two-key trust anchor, offline recovery, manual local signing

Every shipped binary embeds the release **trusted-key set** that `spt update` verifies against (R-UPD-2), so the keys chosen at v0.1 anchor the self-update trust chain for every deployed node. We mint **two Ed25519 keypairs**: a **primary** (active — signs every release, lives on the maintainer's dev machine) and a **recovery** (generated once, never used, never stored on the dev machine). **Both public keys ship in the binary's trusted set.** Both private seeds (32 bytes each) are backed up independently of any one machine (password manager + optional paper/QR, physically separate); the recovery key exists *only* as backups.

Loss of the primary (e.g. dev machine destroyed) is then a non-event: the next release is signed with the recovery key and rotates in a fresh primary (the verify path already supports key revocation). Without the second key, losing the sole key would kill the self-update chain — nodes keep running but every future release fails verification until users manually re-run the installer.

## Considered options

- **Signing key as a CI secret (automated signing)** — rejected for now. The runners are self-hosted dev boxes running arbitrary dev workloads; a release key in their secret store is weaker custody than a manual local sign, and release cadence doesn't justify automation. Revisit if cadence demands it.
- **Single key + good backups** — rejected; rotation-after-loss requires a *second already-trusted* key, otherwise recovery means out-of-band reinstall for every user.

## Consequences

- Releasing is a partly manual ceremony: CI builds artifacts; the maintainer signs the release metadata locally before publishing.
- The signed metadata published with each release must be in the exact `SignedRelease` format the self-updater consumes, from v0.1 onward.


Grep

Project infrastructure|spt-releases|What is SPT|freeware|Installation


538:## Installation
559:## Project infrastructure
561:**spt-releases** (publish-target repo, `SaberMage/spt-releases`):
562:[Omitted long matching line]


530
531	**Scope:** the gated *capabilities* (remote-exec, instantiate-anywhere) are deferred, so the *full* consent UX lands with them. **v1 ships the framework seam** — the grant-store shape + the deliver-consent-to-user mechanism — so the deferred capabilities drop into a coherent gate without a rewrite.
532
533	### Messaging substrate
534
535	**file-transfer progress**:
536	Every file transfer over the substrate is addressable and **progress-queryable mid-flight** by both the agent and the SPT binary (for the GUI) — at any point during the transfer. A substrate-wide requirement, not shell-specific (the Shell text+file channel inherits it).
537
538	## Installation
539
540	spt-core is per-machine and harness-independent, so it installs *before* and *independent of* any adapter.
541
542	**Two install paths, (b) is the primitive:**
543	- **(a) harness-bootstrapped** — a harness plugin installs spt-core as its bootstrap step (today's cplugs model: `/plugin` pulls the plugin, which fetches the spt-core binary). The plugin's role shrinks to this bootstrap; spt-core self-updates thereafter.
544	- **(b) standalone** — the user installs spt-core directly with no harness, for a Pi node, a Shell-only node, or a headless server. Path (a) calls into (b).
545
546	**Installer form:** a one-line script (`curl | sh` / `irm | iex`) that fetches the **signed** binary for the platform, verifies it, places it, registers the binary dir on system-wide PATH (so adapters call `spt api …` cross-OS), and registers the daemon as an OS service. **v0.1 phasing:** the scripts (hosted at the canonical Pages URL, ADR-0014) download the latest release binary, **sha256-check** it (full ed25519 verification is `spt update`'s job thereafter; first-fetch trust = HTTPS + GitHub), place it, and register the *user* PATH — **OS-service registration is deferred** (daemon auto-start on `spt` invocation covers dev-stage use; gap: node unreachable after reboot until something invokes `spt`). **The install path must be non-interactive** (it doubles as every adapter's pack-in on-demand install — no second mechanism); if the one-liner ever grows interactive elements, a flagged non-interactive mode is mandatory. First-run identity gen + daemon start are already unattended; pairing stays a separate explicit step. Chosen for the dev-tool audience, cross-platform reach, and because the binary self-updates after. **Marketplace-repackaging-friendly:** nodes are foreseen on novel platforms (Android, medium-power Linux handhelds), so the install must be easy to repackage for platform marketplaces (e.g. PortMaster for handhelds, F-Droid-style for Android) — a relocatable binary + minimal, non-OS-entangled install logic.
547
548	**Daemon lifecycle:** registered as a systemd user service (Linux) / Windows service or scheduled task for the always-on guarantee, with `spt api`-triggered auto-start as the fallback (above). The first network bind triggers the OS firewall prompt here.
549
550	**What "installed" comprises:** the `spt` binary + the broker + the `$SPT_HOME` root (holds `node.key`, trust store, registry, spools, in-memory-seed fallback state). **First run is idempotent + interactive-optional:** generates the node identity and starts the daemon unattended; pairing (and subnet naming) is a separate explicit step.
551
552	**Legacy migration at install:** standalone install **auto-detects an existing `claude_skill_owl` (modern SPT) install and offers migration** (the migration commitment above) — identity, agents, tracked Psyche context.
553
554	**adapter registration (`spt adapter add`)**:
555	How a node comes to *know* an adapter — harness or shell. An explicit **`spt adapter add <path>`** (or **`--github <user/repo>`**) validates the manifest against the published JSON Schema and writes a registration record under `{SPT_HOME}/…/adapters/` — a **copy** of the files for `file_pull`-update adapters (spt-core owns what it later swaps) or a **pointer** for `delegated`-update adapters (the plugin owns + updates its own files). One command + one dir for both `kind="harness"` and `kind="shell"`; the `kind` field differentiates. The `--github` form **fetches the manifest first** (readable-before-install — same rule as the `min_spt_core_version` readable-before-update gate), checks compatibility, then completes the install via the manifest's own `[update]` avenue: **install is the first update** (one fetch/swap mechanism, not two). Harness-bootstrapped install (path a) calls it from the plugin's bootstrap; standalone install (path b) calls it for shell-only / Pi nodes. Registration is **node-local** — it means *"this node can drive/launch this adapter,"* distinct from advertising an endpoint into a subnet. The **registered-adapter set** the self-updater ripple-updates (see Self-update) is exactly this record set.
556
557	**Removal (`spt adapter remove`) is soft-deregister:** the adapter is **hidden from new-creation / picker lists immediately**, but existing and live instances **keep running** under it (lazy — nothing is force-torn-down). An optional manifest **`uninstall` command template** (the inverse of the install/`[update]` avenue — e.g. `claude plugin uninstall spt`) cleans the adapter's own artifacts; it runs **once no instance is live** under the adapter (or immediately with `--force`, which cascades teardown). Once actually uninstalled, an endpoint whose synced **adapter history** still references it renders as **"needs install"** in the resume picker and offers `adapter add` (the `--github` path closes the loop).
558
559	## Project infrastructure
560
561	**spt-releases** (publish-target repo, `SaberMage/spt-releases`):
562	The public face of spt-core. The source repo (`SaberMage/spt-core`) stays **private long-term**; everything dev-facing publishes here: the README, GitHub Releases (binaries + dev assets), and the rendered dev docs (GitHub Pages at `https://sabermage.github.io/spt-releases`). Pure publish target — doc *truth* and CI doc-generation stay in the source repo; a publish pipeline pushes rendered artifacts. Everything in it is authored public-safe from day one (created private, flips public later). **The Pages URL is the permanent canonical URL** baked into docs, `llms.txt`, the install one-liner, and adapter bootstrap code — no custom domain on Pages (lapse-proof; ADR-0014). `spt-core.decidel.com` may exist only as a human-facing redirect. **Licensing splits by artifact:** install scripts / `mock-adapter` / docs-site content = MIT (devs copy these by design); the `spt` binary = short proprietary freeware license (free to run + redistribute unmodified, no warranty) with an explicit clause that **building adapters/shells against the public contract is unrestricted and royalty-free**.
563	_Avoid_: treating it as a second source repo; authoring doc truth there.
564
565	**issue / feature tracking**:
566	GitHub for v1 (mature API + `gh` CLI already wired; de-risks any agent-files-issues automation). This is *project infrastructure*, not a runtime user dependency — it does not compromise the product's "no central operator" promise the way the removed runtime gh-sync did. **tangled.org** (git on AT Protocol, self-hostable knots) is documented as the principled migration target, aligned with spt-core's decentralized ethos — especially if agents ever file issues over spt's own P2P layer into a self-hosted tracker. v2+ story.
567
568	### Frontend (day-one)
569
570	The day-one headed frontend is a launcher/manager UX, not just a raw `attach`. The end user cares about *reaching a target agent*, not about PTY mechanics. The frontend must:
571
572	- **List** all running and historic endpoints.
573	- **Launch a historic endpoint** that isn't currently running, reusing the same manifest that originally launched it.
574	- **Tap into** (attach to) a running endpoint.
575	- **Init a new endpoint** using a known adapter (e.g. claude-code / spt-plugin).
576
577	This is the realization of the "guided resume" (resume by project or agent, XMB-style filtered selection) and "management GUI" (per-endpoint panes: latest session output, live/project context, psyche log) sketches. `attach` is one operation under this frontend, not the whole frontend.
578
579	**guided-resume picker (`spt resume`, no-arg)**:
580	The CLI sibling of the frontend's guided resume — one command that lists endpoints **grouped by locality, most-recently-used within each group**: `on-node / current-project → on-node / other-project → off-node`, mirroring the *resolution policy*'s local-first preference. Selection **chains conditionally**: a **running** instance → attach/tap-in (no adapter step — already live under one); a **non-running** endpoint → into the **adapter selector** (*adapter selection*: history head = default → prior adapters → "choose a different adapter") → *home subnet* / other creation prompts as needed → launch; a **"+ new endpoint"** entry → the full creation flow. Off-node picks respect the reach + consent gates (remote-drive of your own running instance is ungated; a cold off-node launch is *instantiate-anywhere*, gated/deferred). It unifies the no-id `spawn-session` picker, the resume adapter-history UX, and locality resolution into one pipeline; the day-one frontend renders the same selection logic graphically.
581

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

### Messaging substrate

**file-transfer progress**:
Every file transfer over the substrate is addressable and **progress-queryable mid-flight** by both the agent and the SPT binary (for the GUI) — at any point during the transfer. A substrate-wide requirement, not shell-specific (the Shell text+file channel inherits it).

## Installation

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

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

**Installer form:** a one-line script (`curl | sh` / `irm | iex`) that fetches the **signed** binary for the platform, verifies it, places it, registers the binary dir on system-wide PATH (so adapters call `spt api …` cross-OS), and registers the daemon as an OS service. **v0.1 phasing:** the scripts (hosted at the canonical Pages URL, ADR-0014) download the latest release binary, **sha256-check** it (full ed25519 verification is `spt update`'s job thereafter; first-fetch trust = HTTPS + GitHub), place it, and register the *user* PATH — **OS-service registration is deferred** (daemon auto-start on `spt` invocation covers dev-stage use; gap: node unreachable after reboot until something invokes `spt`). **The install path must be non-interactive** (it doubles as every adapter's pack-in on-demand install — no second mechanism); if the one-liner ever grows interactive elements, a flagged non-interactive mode is mandatory. First-run identity gen + daemon start are already unattended; pairing stays a separate explicit step. Chosen for the dev-tool audience, cross-platform reach, and because the binary self-updates after. **Marketplace-repackaging-friendly:** nodes are foreseen on novel platforms (Android, medium-power Linux handhelds), so the install must be easy to repackage for platform marketplaces (e.g. PortMaster for handhelds, F-Droid-style for Android) — a relocatable binary + minimal, non-OS-entangled install logic.

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

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

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

**adapter registration (`spt adapter add`)**:
How a node comes to *know* an adapter — harness or shell. An explicit **`spt adapter add <path>`** (or **`--github <user/repo>`**) validates the manifest against the published JSON Schema and writes a registration record under `{SPT_HOME}/…/adapters/` — a **copy** of the files for `file_pull`-update adapters (spt-core owns what it later swaps) or a **pointer** for `delegated`-update adapters (the plugin owns + updates its own files). One command + one dir for both `kind="harness"` and `kind="shell"`; the `kind` field differentiates. The `--github` form **fetches the manifest first** (readable-before-install — same rule as the `min_spt_core_version` readable-before-update gate), checks compatibility, then completes the install via the manifest's own `[update]` avenue: **install is the first update** (one fetch/swap mechanism, not two). Harness-bootstrapped install (path a) calls it from the plugin's bootstrap; standalone install (path b) calls it for shell-only / Pi nodes. Registration is **node-local** — it means *"this node can drive/launch this adapter,"* distinct from advertising an endpoint into a subnet. The **registered-adapter set** the self-updater ripple-updates (see Self-update) is exactly this record set.

**Removal (`spt adapter remove`) is soft-deregister:** the adapter is **hidden from new-creation / picker lists immediately**, but existing and live instances **keep running** under it (lazy — nothing is force-torn-down). An optional manifest **`uninstall` command template** (the inverse of the install/`[update]` avenue — e.g. `claude plugin uninstall spt`) cleans the adapter's own artifacts; it runs **once no instance is live** under the adapter (or immediately with `--force`, which cascades teardown). Once actually uninstalled, an endpoint whose synced **adapter history** still references it renders as **"needs install"** in the resume picker and offers `adapter add` (the `--github` path closes the loop).

## Project infrastructure

**spt-releases** (publish-target repo, `SaberMage/spt-releases`):
The public face of spt-core. The source repo (`SaberMage/spt-core`) stays **private long-term**; everything dev-facing publishes here: the README, GitHub Releases (binaries + dev assets), and the rendered dev docs (GitHub Pages at `https://sabermage.github.io/spt-releases`). Pure publish target — doc *truth* and CI doc-generation stay in the source repo; a publish pipeline pushes rendered artifacts. Everything in it is authored public-safe from day one (created private, flips public later). **The Pages URL is the permanent canonical URL** baked into docs, `llms.txt`, the install one-liner, and adapter bootstrap code — no custom domain on Pages (lapse-proof; ADR-0014). `spt-core.decidel.com` may exist only as a human-facing redirect. **Licensing splits by artifact:** install scripts / `mock-adapter` / docs-site content = MIT (devs copy these by design); the `spt` binary = short proprietary freeware license (free to run + redistribute unmodified, no warranty) with an explicit clause that **building adapters/shells against the public contract is unrestricted and royalty-free**.
_Avoid_: treating it as a second source repo; authoring doc truth there.

**issue / feature tracking**:
GitHub for v1 (mature API + `gh` CLI already wired; de-risks any agent-files-issues automation). This is *project infrastructure*, not a runtime user dependency — it does not compromise the product's "no central operator" promise the way the removed runtime gh-sync did. **tangled.org** (git on AT Protocol, self-hostable knots) is documented as the principled migration target, aligned with spt-core's decentralized ethos — especially if agents ever file issues over spt's own P2P layer into a self-hosted tracker. v2+ story.

### Frontend (day-one)

The day-one headed frontend is a launcher/manager UX, not just a raw `attach`. The end user cares about *reaching a target agent*, not about PTY mechanics. The frontend must:

- **List** all running and historic endpoints.
- **Launch a historic endpoint** that isn't currently running, reusing the same manifest that originally launched it.
- **Tap into** (attach to) a running endpoint.
- **Init a new endpoint** using a known adapter (e.g. claude-code / spt-plugin).

This is the realization of the "guided resume" (resume by project or agent, XMB-style filtered selection) and "management GUI" (per-endpoint panes: latest session output, live/project context, psyche log) sketches. `attach` is one operation under this frontend, not the whole frontend.

**guided-resume picker (`spt resume`, no-arg)**:
The CLI sibling of the frontend's guided resume — one command that lists endpoints **grouped by locality, most-recently-used within each group**: `on-node / current-project → on-node / other-project → off-node`, mirroring the *resolution policy*'s local-first preference. Selection **chains conditionally**: a **running** instance → attach/tap-in (no adapter step — already live under one); a **non-running** endpoint → into the **adapter selector** (*adapter selection*: history head = default → prior adapters → "choose a different adapter") → *home subnet* / other creation prompts as needed → launch; a **"+ new endpoint"** entry → the full creation flow. Off-node picks respect the reach + consent gates (remote-drive of your own running instance is ungated; a cold off-node launch is *instantiate-anywhere*, gated/deferred). It unifies the no-id `spawn-session` picker, the resume adapter-history UX, and locality resolution into one pipeline; the day-one frontend renders the same selection logic graphically.


C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-46-   look, not its toolchain). Raw `.md` published alongside HTML. Authored in a new
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-47-   top-level **`docs-site/`** (never from internal `docs/` — that corpus is
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-48-   private and sister-project-laden).
M6-PLAN.md:49:7. **README:** `README.md`, `# SPT` brand; "What is SPT?" tagline verbatim; honest
M6-PLAN.md:50:   ABC paragraph (**Pi = the pi coding agent, disambiguated**; harness adapters
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-51-   phrased as in-development); **Status table** (works-today vs next); two install
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-52-   one-liners; two dev getting-started sections; docs + `llms.txt` pointers.
M6-PLAN.md-53-8. **Install-on-demand docs:** generic contract + per-OS check-and-install snippet


No matches found


PowerShell

git show 1922f4a --stat 2>$null; git show 1922fa4 --stat

Show grill commit file list


commit 1922fa4f52b19d25ef7059d3032ef5eb36c28191
Author: Reavo End <decidel@gmail.com>
Date:   Fri Jun 5 04:05:02 2026 -0700

    docs(grill): M6 stage-setting decisions — spt-releases topology, docs scope, signing

    Grill session outcomes for the preliminary phase before the v1 acceptance
    proof: ADR-0014 (publish-target repo + lapse-proof github.io canonical URL),
    ADR-0015 (two-key release-signing trust anchor, offline recovery, manual
    local signing). CONTEXT.md: spt-releases entry + licensing split, v0.1
    installer phasing (minimal, sha256-only, non-interactive contract), SDK
    crates first-party-only while source is private, Pi disambiguation, shell
    concrete-types staleness fix. DOCS-STRATEGY.md: v0.1 Tier 1/Tier 2
    deployment scope + mdBook (Starlight-styled) site generator.

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

 CONTEXT.md                                               | 14 +++++++++++---
 docs/DOCS-STRATEGY.md                                    |  8 ++++++++
 ...-publish-target-repo-and-lapse-proof-canonical-url.md | 16 ++++++++++++++++
 docs/adr/0015-release-signing-two-key-trust-anchor.md    | 15 +++++++++++++++
 4 files changed, 50 insertions(+), 3 deletions(-)


CONTEXT.md:52:spt-core is harness-independent: it does not know about Claude Code, Codex, Cursor, or any other agent runtime. All harness-specific surfaces (how to invoke an agent session, fetch conversation history for an echo commune, detect activity/idleness, etc.) are abstracted behind a runtime layer that consumers supply.
CONTEXT.md:61:A declarative configuration file (TOML/YAML — schema TBD) that tells spt-core how to drive a specific harness. Declares: how to invoke an agent session, how to look up conversation history for an echo commune, how to spawn/resume a Psyche-equivalent, which binary or command implements each harness-side operation, and which endpoint types this harness supports. spt-core is the actor for each of these; the manifest tells it what to do.
CONTEXT.md:83:[Omitted long matching line]
CONTEXT.md:540:spt-core is per-machine and harness-independent, so it installs *before* and *independent of* any adapter.
docs\TRACEABILITY.md:45:3. **Agent-facing rule.** The repo `CLAUDE.md` instructs every dev-agent: tag `[<stage>-><REQ-ID>]` in the same change as the evidence, and run `traceable-reqs check` before declaring done. Since spt-core is built *with* agents, the contract must be machine-followable — agent discipline + CI as the net.
ROADMAP.md:12:- **The hard freeze on `claude_skill_owl` is not a clock.** It works today and delivers value as-is while spt-core is built — no schedule pressure from it.
PRD.md:10:spt-core is a **harness-independent core for an agent ecosystem**: inter-agent messaging, live-agent lifecycle, terminal hosting, seamless self-update, and zero-config cross-machine networking — shipped as both a Rust library workspace and a single canonical binary (`spt` / `spt.exe`).
PRD.md:37:- **A model/billing opinion** — SPT is not a harness; model choice is always the adapter's.
PRD.md:67:The umbrella surface an adapter binds to. Two halves; `SPT is not a harness` — model/billing/harness-env/harness-context are always the adapter's, expressed in its own command templates.
docs\adr\0001-clean-room-fork-from-claude-skill-owl.md:9:[Omitted long matching line]
docs\adr\0002-bake-networking-into-core-collapse-spt-node.md:19:spt-core is a ground-up, harness-independent rebuild. The primary reason `spt-node` was separate — decoupling networking from a CC-tied binary — no longer exists, because spt-core is already decoupled from any harness. Meanwhile the product goal for networking sharpened: WAN cross-machine messaging should be **first-class and zero-config — "just works."**
docs\MANIFEST.md:7:**SPT is not a harness.** The manifest declares only what *varies per harness*. Command templates are **opaque strings** — spt-core never parses out a model, tool list, or flag; the adapter writes the full command line and spt-core just runs it with substitution keys filled. Anything spt-core owns (sentinels, spool/registry/perch schemas, the event-block vocabulary, the version-trampoline mechanism, config knobs) is **not** in the manifest.


C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-44-   verticals' tutorials (overview + reference stubs only).
M6-PLAN.md-45-6. **Site:** mdBook + custom theme CSS (styling north star: Astro Starlight — its
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-46-   look, not its toolchain). Raw `.md` published alongside HTML. Authored in a new
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-47-   top-level **`docs-site/`** (never from internal `docs/` — that corpus is
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-48-   private and sister-project-laden).
M6-PLAN.md:49:7. **README:** `README.md`, `# SPT` brand; "What is SPT?" tagline verbatim; honest
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-50-   ABC paragraph (**Pi = the pi coding agent, disambiguated**; harness adapters
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-51-   phrased as in-development); **Status table** (works-today vs next); two install
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-52-   one-liners; two dev getting-started sections; docs + `llms.txt` pointers.
M6-PLAN.md-53-8. **Install-on-demand docs:** generic contract + per-OS check-and-install snippet
C:\Users\decid\Documents\projects\spt-core\M6-PLAN.md-54-   (real Pages URL, no placeholders); Windows PATH-refresh gotcha (absolute path


1	# Documentation strategy
2
3	> Draft (2026-05-31). A **designed-in commitment**, not an afterthought — grounded in research on critically-acclaimed developer docs (Stripe, Twilio, the Rust Book, FastAPI, Cloudflare, Anthropic, Diátaxis, `llms.txt`, Google dev style guide). Governs the **shipped product docs** (authored as code lands); the planning docs (CONTEXT/PRD/ADRs/design docs) are internal and separate.
4
5	## The defining constraint: a dual audience
6
7	spt-core's docs serve **two readers at once** — human developers *and* the **AI dev-agents that build integrations with it**. This is unusual and it is the design constraint: every artifact is authored **once in clean markdown** and served in **two depths** (human-rendered + agent-export). The agent layer is **first-class, not optional** — the strategic signal (Tailwind's docs traffic fell ~40% because assistants answer for it) is, for spt-core, *the goal*: make the agent layer so good that dev-agents integrate correctly on the first try. There is recursive fit here — the same kind of agent that spt-core *hosts* is the one that *reads its docs*.
8
9	## Principles (top techniques, prioritized for spt-core)
10
11	1. **Sub-10-minute killer quickstart** — runnable, deterministic, whole-working-thing-first, no placeholders. TTFHW ("time to first hello world") is the single most-cited conversion lever (~68% abandon trials over setup time).
12	2. **Diátaxis four-mode separation** — tutorial / how-to / reference / explanation, never mixed (mixing is the most-cited cause of confusing docs).
13	3. **Deterministic, real, copy-pasteable examples everywhere** — no `<YOUR_VALUE_HERE>` placeholders; real values that run. Serves humans *and* agents.
14	4. **Dual-depth agent exports** — `llms.txt` (slim curated index) + `llms-full.txt` (full concatenated export), auto-generated in CI, plus markdown content negotiation (`.md` suffix / `Accept: text/markdown`) which cuts agent token use ~90% vs HTML.
15	5. **One canonical way to do X** — explicitly mark deprecated/alternate paths. Non-determinism is fatal for agents.
16	6. **Complete reference, auto-generated, all error variants** — rustdoc for the public SDK crates (`spt-proto`, `spt-runtime`, `spt-msg`) + a machine-readable manifest/contract schema. Generic placeholders in reference are a failure mode.
17	7. **Consistent conversational voice** — adopt the Google developer style guide: second person ("you"), active voice, knowledgeable-friend tone.
18	8. **Explain *why*, not just *what*** — conceptual docs + diagrams for the messaging model, the agent lifecycle states, instances/dormancy, and P2P topology.
19	9. **Stable, never-renamed anchors/URLs** — agents cache links.
20	10. **Docs-as-product, gated in CI** — generation (rustdoc, `llms.txt`, schema, CLI help) is part of the build so docs can never drift from code. Drift is the #1 most-cited docs failure; this kills it structurally.
21
22	## Information architecture — by capability vertical
23
24	Organize by capability, each vertical carrying the same four Diátaxis modes internally (the Cloudflare pattern):
25
26	**Messaging · Live-agent lifecycle · Terminal hosting · Networking (P2P + pairing) · Harness contract (manifest + `api`) · Instances · Shells · Self-update.**
27
28	## Killer quickstart targets (two, one per audience)
29
30	- **Human dev:** two agents exchange a message end-to-end in **one command + minimal config**, < 10 min — the core primitive, whole-thing-first, then decomposed.
31	- **Dev-agent integrator:** a minimal **adapter** that satisfies the manifest + `api` contract and drives one lifecycle operation — the "build a harness for spt-core" hello-world.
32
33	## Agent-consumable docs (spt-core specifics)
34
35	- **`llms.txt` / `llms-full.txt`** — auto-emitted in CI; the slim index lets a tool answer quick questions, the full export feeds deep ingestion.
36	- **Markdown content negotiation** + deterministic include/exclude tags so agent exports carry the canonical path and drop human-only narrative.
37	- **Machine-readable manifest/contract schema** (JSON Schema) at a stable, discoverable path — agents validate their adapter manifests against it; the schema *is* documentation.
38	- **MCP doc/resource server** — the standout fit, given the harness contract is the core integration surface. Expose the contract + reference as **deterministic, side-effect-free MCP resources** (same request → same result), giving integrating agents structured knowledge without RAG fuzziness. Highest-leverage agent-docs move for spt-core.
39	- **CLI help as first-class agent docs** — agents primarily shell `spt api …`, so `spt <cmd> --help` must be complete, exampled, and machine-readable (a `--json`/structured help mode). The CLI surface is a documentation surface.
40
41	## v0.1 deployment scope (grilled 2026-06-05)
42
43	Deployed to GitHub Pages on `spt-releases` (ADR-0014; truth + CI generation stay in the source repo). **Tier 1 (ships with v0.1):** mental model/overview · human quickstart (install → two agents message) · adapter quickstart (via `mock-adapter`) · **harness contract vertical complete** (public manifest reference + full `spt api` reference + install-on-demand bootstrap pattern) · shell adapter getting-started (`spt-shell-notify` as the live example) · CLI reference generated from clap, CI-gated · **manifest JSON Schema (new code: schemars on the manifest types)** · root `llms.txt` + `llms-full.txt` CI-generated · rustdoc → Pages (no docs.rs — SDK crates are first-party-only while source is private).
44
45	**Site generator: mdBook + custom theme CSS** (Rust-toolchain-coherent — one `cargo install mdbook` on the existing runners; the Rust Book IA this strategy copies *is* mdBook). Styling north star: **Astro Starlight** — copy its look/feel in the theme CSS, not its toolchain. Raw `.md` published alongside rendered pages (`/x.html` ↔ `/x.md`) for the agent-export convention; `llms.txt` / `llms-full.txt` / `manifest.schema.json` as static assets at site root.
46
47	**Tier 2 (deferred, tracked):** remaining verticals' tutorials/how-tos (overview + reference stubs only in v0.1) · MCP doc server · `--help --json` mode (low marginal value over the generated CLI reference) · capstone tutorial · per-vertical `llms.txt` fan-out (root links pages directly until then).
48
49	## Anti-patterns to design against (most-cited failures)
50
51	Doc/code drift (#1 — solved by CI gating); *what* without *why*; too much setup before first success; generic placeholders in reference; mixed Diátaxis modes; poor search/navigation; multiple non-canonical ways to do X (fatal for agents).
52
53	## CI commitments
54
55	rustdoc, the manifest JSON Schema, `llms.txt`/`llms-full.txt`, and CLI help exports are **generated and checked in CI** — a doc-drift gate. Doc quality lives on the same footing as tests.
56
57	---
58
59	## Concrete templates (from firsthand artifacts)
60
61	Extracted by fetching the *actual* artifacts (Cloudflare/Anthropic/Mintlify `llms.txt`, FastAPI/Stripe/Supabase quickstarts, the Rust Book TOC, Django/Diátaxis, Tokio/clap rustdoc, gh/Stripe CLI). Copy the *shape*.
62
63	### `llms.txt` — two-level (Cloudflare pattern, not Anthropic's flat dump)
64
65	Root `llms.txt` is a **curated index that fans out to a per-vertical `llms.txt`**; `llms-full.txt` is CI-concatenated page bodies (Anthropic's overflowed a 10 MB fetch — generation-only, never hand-authored).
66
67	```markdown
68	# spt-core
69	A harness-independent core for an agent ecosystem: messaging, live-agent
70	lifecycle, terminal hosting, P2P networking, a runtime-manifest harness contract.
71
72	> Each capability links to its own llms.txt. Full concatenated docs: /llms-full.txt.
73	> Machine-readable contract: /manifest.schema.json. Append `.md` to any doc URL
74	> for raw markdown (~90% fewer tokens than HTML).
75
76	## Start here
77	- [Quickstart: two agents exchange a message](…/quickstart/messaging.md): one command, <10 min.
78	- [Quickstart: build an adapter](…/quickstart/adapter.md): minimal manifest + `api` contract.
79	- [Mental model](…/concepts/overview.md): daemon, endpoints, instances, subnet.
80
81	## Capabilities      (one bullet per vertical, each → its own llms.txt)
82	- [Messaging](…/messaging/llms.txt): typed ops + file blobs; send / ring / ready.
83	- [Live-agent lifecycle](…/lifecycle/llms.txt): skeleton → live → dormant → suspended.
84	- … Terminal · Networking · Harness contract · Instances · Shells · Self-update
85
86	## Reference (auto-generated)
87	- [spt-proto](https://docs.rs/spt-proto) · [spt-runtime](https://docs.rs/spt-runtime) · [spt-msg](https://docs.rs/spt-msg)
88	- [CLI reference](…/cli/llms.txt) · [Manifest JSON Schema](…/manifest.schema.json)
89	```
90
91	### Quickstart — whole-thing → run → verify → dissect (FastAPI), with a 2-path fork (Supabase)
92
93	Zero placeholders; every value runs; complete working thing shown *before* explanation; audience fork at the top (spt-core has two readers); end with the Diátaxis trident.
94
95	```markdown
96	# Quickstart — two agents exchange a message, end to end, <10 min.
97	> Pick your path: **developer** (here) · **building an adapter / dev-agent** → /quickstart/adapter.md
98
99	## 1. Install (one line)        curl -fsSL https://spt-core.dev/install.sh | sh
100	   Verify:                      spt --version      # spt 0.1.0 (proto v1)
101	## 2. The whole working thing   spt ready alice
102	                                spt ready bob
103	                                spt send bob --from alice "hello"
104	## 3. Run it                    spt poll --as bob
105	                                # → from=alice op=message body="hello"
106	## 4. What just happened        (dissect each line; note the daemon auto-started)
107	## Next                         how-to · concept · reference  (Diátaxis trident)
108	```
109
110	### Information architecture — two axes
111
112	**Global sequence (Rust Book logic):** early runnable project, dependency-ordered concepts, capstone last.
113	```
114	0 Getting started → 1 Quickstart (the early runnable project) → 2 Mental model
115	→ 3 Capability verticals (build-order) → 4 Capstone: build a non-CC adapter end-to-end
116	```
117	**Vertical ordering (dependency-sequenced):**
118	`Messaging → Lifecycle → Terminal → Networking → Harness contract → Instances → Shells → Self-update`
119	(messaging is the primitive the quickstart needs; self-update last because its no-termination invariant depends on the terminal/broker model.)
120	**Per-vertical internal template (Django labels × Cloudflare ordering):**
121	`Overview (why+diagram) · Quickstart/Tutorial · How-to guides · Reference (rustdoc+CLI+schema) · llms.txt`
122
123	### rustdoc — Tokio opener + out-document clap on errors
124
125	Crate doc: **value-prop → "A tour of" → runnable example near top → feature flags → proto-compat note.** Every public error enum documents **per-variant cause → fix**; every fallible fn gets an `# Errors` section.
126
127	```rust
128	//! # spt-runtime — one-sentence value prop.
129	//! ## A tour of spt-runtime   - [`Manifest`] · [`ApiCommand`] · [`Lifecycle`]
130	//! ## Example                 (runnable, real values, near the top)
131	//! ## Feature flags           `net` (default) · `unstable`
132	//! ## Wire-protocol compatibility   interop by proto version, never crate version.
133	```
134	```rust
135	/// The manifest's `min_spt_core_version` exceeds this binary.
136	/// **Fix:** update spt-core, or pin the adapter to a compatible release.
137	VersionTooOld { required: Version, found: Version },
138	```
139
140	### CLI-as-docs — gh buckets + `--json` help mode
141
142	Top-level `--help` groups by audience (`AGENT COMMANDS` send/ring/ready/poll · `HARNESS COMMANDS` api … · `NODE COMMANDS` pair/subnet/update). Per-command: **USAGE · FLAGS · EXAMPLES (real values) · LEARN MORE**. Beyond gh: a **`spt <cmd> --help --json`** structured-help mode so dev-agents parse the contract without scraping, plus gh's uniform **`--json <fields>` / `--jq` / `--template`** trio on any list command. The `--json` help export is generated + CI-gated alongside rustdoc and the schema.
143

# Documentation strategy

> Draft (2026-05-31). A **designed-in commitment**, not an afterthought — grounded in research on critically-acclaimed developer docs (Stripe, Twilio, the Rust Book, FastAPI, Cloudflare, Anthropic, Diátaxis, `llms.txt`, Google dev style guide). Governs the **shipped product docs** (authored as code lands); the planning docs (CONTEXT/PRD/ADRs/design docs) are internal and separate.

## The defining constraint: a dual audience

spt-core's docs serve **two readers at once** — human developers *and* the **AI dev-agents that build integrations with it**. This is unusual and it is the design constraint: every artifact is authored **once in clean markdown** and served in **two depths** (human-rendered + agent-export). The agent layer is **first-class, not optional** — the strategic signal (Tailwind's docs traffic fell ~40% because assistants answer for it) is, for spt-core, *the goal*: make the agent layer so good that dev-agents integrate correctly on the first try. There is recursive fit here — the same kind of agent that spt-core *hosts* is the one that *reads its docs*.

## Principles (top techniques, prioritized for spt-core)

1. **Sub-10-minute killer quickstart** — runnable, deterministic, whole-working-thing-first, no placeholders. TTFHW ("time to first hello world") is the single most-cited conversion lever (~68% abandon trials over setup time).
2. **Diátaxis four-mode separation** — tutorial / how-to / reference / explanation, never mixed (mixing is the most-cited cause of confusing docs).
3. **Deterministic, real, copy-pasteable examples everywhere** — no `<YOUR_VALUE_HERE>` placeholders; real values that run. Serves humans *and* agents.
4. **Dual-depth agent exports** — `llms.txt` (slim curated index) + `llms-full.txt` (full concatenated export), auto-generated in CI, plus markdown content negotiation (`.md` suffix / `Accept: text/markdown`) which cuts agent token use ~90% vs HTML.
5. **One canonical way to do X** — explicitly mark deprecated/alternate paths. Non-determinism is fatal for agents.
6. **Complete reference, auto-generated, all error variants** — rustdoc for the public SDK crates (`spt-proto`, `spt-runtime`, `spt-msg`) + a machine-readable manifest/contract schema. Generic placeholders in reference are a failure mode.
7. **Consistent conversational voice** — adopt the Google developer style guide: second person ("you"), active voice, knowledgeable-friend tone.
8. **Explain *why*, not just *what*** — conceptual docs + diagrams for the messaging model, the agent lifecycle states, instances/dormancy, and P2P topology.
9. **Stable, never-renamed anchors/URLs** — agents cache links.
10. **Docs-as-product, gated in CI** — generation (rustdoc, `llms.txt`, schema, CLI help) is part of the build so docs can never drift from code. Drift is the #1 most-cited docs failure; this kills it structurally.

## Information architecture — by capability vertical

Organize by capability, each vertical carrying the same four Diátaxis modes internally (the Cloudflare pattern):

**Messaging · Live-agent lifecycle · Terminal hosting · Networking (P2P + pairing) · Harness contract (manifest + `api`) · Instances · Shells · Self-update.**

## Killer quickstart targets (two, one per audience)

- **Human dev:** two agents exchange a message end-to-end in **one command + minimal config**, < 10 min — the core primitive, whole-thing-first, then decomposed.
- **Dev-agent integrator:** a minimal **adapter** that satisfies the manifest + `api` contract and drives one lifecycle operation — the "build a harness for spt-core" hello-world.

## Agent-consumable docs (spt-core specifics)

- **`llms.txt` / `llms-full.txt`** — auto-emitted in CI; the slim index lets a tool answer quick questions, the full export feeds deep ingestion.
- **Markdown content negotiation** + deterministic include/exclude tags so agent exports carry the canonical path and drop human-only narrative.
- **Machine-readable manifest/contract schema** (JSON Schema) at a stable, discoverable path — agents validate their adapter manifests against it; the schema *is* documentation.
- **MCP doc/resource server** — the standout fit, given the harness contract is the core integration surface. Expose the contract + reference as **deterministic, side-effect-free MCP resources** (same request → same result), giving integrating agents structured knowledge without RAG fuzziness. Highest-leverage agent-docs move for spt-core.
- **CLI help as first-class agent docs** — agents primarily shell `spt api …`, so `spt <cmd> --help` must be complete, exampled, and machine-readable (a `--json`/structured help mode). The CLI surface is a documentation surface.

## v0.1 deployment scope (grilled 2026-06-05)

Deployed to GitHub Pages on `spt-releases` (ADR-0014; truth + CI generation stay in the source repo). **Tier 1 (ships with v0.1):** mental model/overview · human quickstart (install → two agents message) · adapter quickstart (via `mock-adapter`) · **harness contract vertical complete** (public manifest reference + full `spt api` reference + install-on-demand bootstrap pattern) · shell adapter getting-started (`spt-shell-notify` as the live example) · CLI reference generated from clap, CI-gated · **manifest JSON Schema (new code: schemars on the manifest types)** · root `llms.txt` + `llms-full.txt` CI-generated · rustdoc → Pages (no docs.rs — SDK crates are first-party-only while source is private).

**Site generator: mdBook + custom theme CSS** (Rust-toolchain-coherent — one `cargo install mdbook` on the existing runners; the Rust Book IA this strategy copies *is* mdBook). Styling north star: **Astro Starlight** — copy its look/feel in the theme CSS, not its toolchain. Raw `.md` published alongside rendered pages (`/x.html` ↔ `/x.md`) for the agent-export convention; `llms.txt` / `llms-full.txt` / `manifest.schema.json` as static assets at site root.

**Tier 2 (deferred, tracked):** remaining verticals' tutorials/how-tos (overview + reference stubs only in v0.1) · MCP doc server · `--help --json` mode (low marginal value over the generated CLI reference) · capstone tutorial · per-vertical `llms.txt` fan-out (root links pages directly until then).

## Anti-patterns to design against (most-cited failures)

Doc/code drift (#1 — solved by CI gating); *what* without *why*; too much setup before first success; generic placeholders in reference; mixed Diátaxis modes; poor search/navigation; multiple non-canonical ways to do X (fatal for agents).

## CI commitments

rustdoc, the manifest JSON Schema, `llms.txt`/`llms-full.txt`, and CLI help exports are **generated and checked in CI** — a doc-drift gate. Doc quality lives on the same footing as tests.

---

## Concrete templates (from firsthand artifacts)

Extracted by fetching the *actual* artifacts (Cloudflare/Anthropic/Mintlify `llms.txt`, FastAPI/Stripe/Supabase quickstarts, the Rust Book TOC, Django/Diátaxis, Tokio/clap rustdoc, gh/Stripe CLI). Copy the *shape*.

### `llms.txt` — two-level (Cloudflare pattern, not Anthropic's flat dump)

Root `llms.txt` is a **curated index that fans out to a per-vertical `llms.txt`**; `llms-full.txt` is CI-concatenated page bodies (Anthropic's overflowed a 10 MB fetch — generation-only, never hand-authored).

```markdown
# spt-core
A harness-independent core for an agent ecosystem: messaging, live-agent
lifecycle, terminal hosting, P2P networking, a runtime-manifest harness contract.

> Each capability links to its own llms.txt. Full concatenated docs: /llms-full.txt.
> Machine-readable contract: /manifest.schema.json. Append `.md` to any doc URL
> for raw markdown (~90% fewer tokens than HTML).

## Start here
- [Quickstart: two agents exchange a message](…/quickstart/messaging.md): one command, <10 min.
- [Quickstart: build an adapter](…/quickstart/adapter.md): minimal manifest + `api` contract.
- [Mental model](…/concepts/overview.md): daemon, endpoints, instances, subnet.

## Capabilities      (one bullet per vertical, each → its own llms.txt)
- [Messaging](…/messaging/llms.txt): typed ops + file blobs; send / ring / ready.
- [Live-agent lifecycle](…/lifecycle/llms.txt): skeleton → live → dormant → suspended.
- … Terminal · Networking · Harness contract · Instances · Shells · Self-update

## Reference (auto-generated)
- [spt-proto](https://docs.rs/spt-proto) · [spt-runtime](https://docs.rs/spt-runtime) · [spt-msg](https://docs.rs/spt-msg)
- [CLI reference](…/cli/llms.txt) · [Manifest JSON Schema](…/manifest.schema.json)
```

### Quickstart — whole-thing → run → verify → dissect (FastAPI), with a 2-path fork (Supabase)

Zero placeholders; every value runs; complete working thing shown *before* explanation; audience fork at the top (spt-core has two readers); end with the Diátaxis trident.

```markdown
# Quickstart — two agents exchange a message, end to end, <10 min.
> Pick your path: **developer** (here) · **building an adapter / dev-agent** → /quickstart/adapter.md

## 1. Install (one line)        curl -fsSL https://spt-core.dev/install.sh | sh
   Verify:                      spt --version      # spt 0.1.0 (proto v1)
## 2. The whole working thing   spt ready alice
                                spt ready bob
                                spt send bob --from alice "hello"
## 3. Run it                    spt poll --as bob
                                # → from=alice op=message body="hello"
## 4. What just happened        (dissect each line; note the daemon auto-started)
## Next                         how-to · concept · reference  (Diátaxis trident)
```

### Information architecture — two axes

**Global sequence (Rust Book logic):** early runnable project, dependency-ordered concepts, capstone last.
```
0 Getting started → 1 Quickstart (the early runnable project) → 2 Mental model
→ 3 Capability verticals (build-order) → 4 Capstone: build a non-CC adapter end-to-end
```
**Vertical ordering (dependency-sequenced):**
`Messaging → Lifecycle → Terminal → Networking → Harness contract → Instances → Shells → Self-update`
(messaging is the primitive the quickstart needs; self-update last because its no-termination invariant depends on the terminal/broker model.)
**Per-vertical internal template (Django labels × Cloudflare ordering):**
`Overview (why+diagram) · Quickstart/Tutorial · How-to guides · Reference (rustdoc+CLI+schema) · llms.txt`

### rustdoc — Tokio opener + out-document clap on errors

Crate doc: **value-prop → "A tour of" → runnable example near top → feature flags → proto-compat note.** Every public error enum documents **per-variant cause → fix**; every fallible fn gets an `# Errors` section.

```rust
//! # spt-runtime — one-sentence value prop.
//! ## A tour of spt-runtime   - [`Manifest`] · [`ApiCommand`] · [`Lifecycle`]
//! ## Example                 (runnable, real values, near the top)
//! ## Feature flags           `net` (default) · `unstable`
//! ## Wire-protocol compatibility   interop by proto version, never crate version.
```
```rust
/// The manifest's `min_spt_core_version` exceeds this binary.
/// **Fix:** update spt-core, or pin the adapter to a compatible release.
VersionTooOld { required: Version, found: Version },
```

### CLI-as-docs — gh buckets + `--json` help mode

Top-level `--help` groups by audience (`AGENT COMMANDS` send/ring/ready/poll · `HARNESS COMMANDS` api … · `NODE COMMANDS` pair/subnet/update). Per-command: **USAGE · FLAGS · EXAMPLES (real values) · LEARN MORE**. Beyond gh: a **`spt <cmd> --help --json`** structured-help mode so dev-agents parse the contract without scraping, plus gh's uniform **`--json <fields>` / `--jq` / `--template`** trio on any list command. The `--json` help export is generated + CI-gated alongside rustdoc and the schema.


23-[Omitted long context line]
24-
25:**Pi** (disambiguation — two meanings, never conflate):
26:(1) **Pi, the coding agent/harness** (`badlogic/pi-mono`) — a harness example alongside Claude Code and Codex; this is the meaning in user-facing harness lists. (2) **Pi-class node** — Raspberry-Pi-class low-power hardware hosting a Shell-only or headless SPT node; an incidental hardware descriptor, never an explicit product example. Public-facing docs must disambiguate or avoid the bare word.
27:_Avoid_: bare "Pi node" when the harness is meant.
28-
29-**spt-daemon** (per-machine supervisor):
--
111-- **continue-existing:** resume an existing harness session under the adapter (its native resume).
112-
113:**capability declaration** — which endpoint types a harness/node can host (a Pi node might host only Shells, never a LiveAgent). Static manifest list, consumed by the subnet registry so a node advertises its hostable types. Exact shape is design-open.
114-
115-**adapter-update seam** — file-pull or delegated-command (see Self-update). Locked.
--
390-A resting instance retains its files + last context and is re-activatable in place — a lightweight **wake** (state's already there), distinct from instantiate-anywhere's fresh spawn. Resting instances remain addressable by node (`ling@desktop`). Registry status: **active / dormant / suspended / offline** (offline = node unreachable).
391-
392:[Omitted long matching line]
393-
394-[Omitted long context line]
--
542-**Two install paths, (b) is the primitive:**
543-- **(a) harness-bootstrapped** — a harness plugin installs spt-core as its bootstrap step (today's cplugs model: `/plugin` pulls the plugin, which fetches the spt-core binary). The plugin's role shrinks to this bootstrap; spt-core self-updates thereafter.
544:- **(b) standalone** — the user installs spt-core directly with no harness, for a Pi node, a Shell-only node, or a headless server. Path (a) calls into (b).
545-
546-[Omitted long context line]
--
553-
554-**adapter registration (`spt adapter add`)**:
555:[Omitted long matching line]
556-
557-[Omitted long context line]


1	# spt-core
2
3	**Platform scope:** Windows + Linux for v1. macOS is out (no test machine available) but kept structurally easy — `portable-pty` and Iroh both support it, so macOS is a later test/CI-budget decision, not a re-architecture.
4
5	**Legacy migration:** it should be possible — ideally *automatic* — for a user to migrate an existing `claude_skill_owl` (modern SPT) install to spt-core (identity, agents, tracked Psyche context). Exact mechanism deferred to design; the commitment is that migration is a first-class supported path, not a manual rebuild.
6
7	Harness-independent core for the SPT ecosystem. Provides inter-agent messaging, live-agent lifecycle, terminal wrapping, self-update, and networking primitives — as both a Rust library workspace and a canonical reference binary. Designed so any agent runtime (Claude Code, Codex, Cursor, headless, future harnesses) can interface with the SPT ecosystem either by shelling out to the binary or by linking the crates directly.
8
9	Successor to `claude_skill_owl` (today's "modern SPT"), which is being rebuilt as `spt-core` to untether the system from Claude Code and lift it to a general-purpose agent-ecosystem core.
10
11	## Language
12
13	**spt-core**:
14	The system. Canonical name. The Rust workspace and the umbrella project.
15
16	**spt.exe / spt** (canonical binary):
17	The reference binary built from the workspace. Replaces today's `owl.exe`. Most external integrations (plugins, hooks, scripts in other harnesses) interact with spt-core *only* through this binary — fire-and-forget subcommands, long-running listeners under a parent harness's process supervisor, etc. Unix builds use the same name without `.exe`.
18
19	**library workspace**:
20	The set of Rust crates that compose spt-core. Consumers that want a deeper integration than shelling out to `spt.exe` link these crates directly. The reference binary is itself a consumer of the workspace. The expected non-binary consumers are future first-party services that link Rust directly.
21
22	**spt plugin** (separate downstream project — NOT an spt-core deliverable):
23	A rebuilt version of today's Claude Code `spt` plugin. It is the **first consumer** built *atop* spt-core and the **acceptance proof** of spt-core v1 (it reaches feature parity with modern SPT while delegating all core functionality to spt-core, primarily via `spt.exe`, with deeper hooks where useful) — but it **lives and builds in its own repository, outside spt-core**. It is a Claude-Code-specific *adapter*: it holds the Claude Code conventions (hooks, slash-commands, skill/plugin layout, `claude` session-invocation). **spt-core itself contains zero Claude Code conventions** — only the harness-agnostic contract the plugin binds to. The only adapter-shaped artifact ever in this repo is a generic mock/test adapter exercising the manifest + `api` contract (PRD R-DOCS-2). The M2 milestone delivers that contract + the lifecycle primitives; building the plugin is downstream work, not M2.
24
25	**Pi** (disambiguation — two meanings, never conflate):
26	(1) **Pi, the coding agent/harness** (`badlogic/pi-mono`) — a harness example alongside Claude Code and Codex; this is the meaning in user-facing harness lists. (2) **Pi-class node** — Raspberry-Pi-class low-power hardware hosting a Shell-only or headless SPT node; an incidental hardware descriptor, never an explicit product example. Public-facing docs must disambiguate or avoid the bare word.
27	_Avoid_: bare "Pi node" when the harness is meant.
28
29	**spt-daemon** (per-machine supervisor):
30	The single always-on, one-per-machine logical supervisor. Owns the PTYs for all hosted sessions, the node's network identity + WAN endpoint, the subnet registry, all spools, **all poll-listener logic, and all Psyche/pulse loops** — everything is consolidated here (no separate poll-listener or Psyche-wrapper processes; listeners already touch sessions directly under capsule/idle, and Psyche wrappers already invoke harness binaries directly, so they belong in the one supervisor). Collapses what the sister project planned as a separate `spt-node` daemon into one process — see Networking. The `spt-node` separate-deliverable concept is retired.
31
32	Internally the logical daemon is split into two implementation layers for seamless self-update (see Self-update):
33	- **broker** (stable "kernel") — holds *only* the un-transferable, must-not-die resources: PTY master fds, the spawned harness child processes, and listening network sockets. Minimal, dumb, versioned local IPC. Almost never updates.
34	- **daemon brain** ("userspace") — all logic (routing, registry, pulse/psyche loops, manifest parsing, update orchestration). Restarts freely on update; rehydrates from disk state and re-attaches to the broker's held handles.
35
36	Logical addressing is unchanged — still one per-machine `spt-daemon`; the broker is an internal layer, not separately addressable.
37
38	**in-session relay**:
39	A thin, stateless `spt.exe` task that exists only in **harness-hosted** sessions (where the agent harness is the parent process and spt cannot reach into its process tree — today's Monitor model). It streams the daemon brain's events into the session's stdout. All *stateful* listener logic lives in the daemon; the relay is a dumb pipe, freely killable and respawnable. **spt-hosted** sessions need no relay — the daemon injects events straight into the PTY.
40
41	### Deliverable shape
42
43	spt-core ships **both** a library workspace and a canonical binary:
44
45	- **Library crates** — the deeper integration path. Used by future first-party services that link Rust directly.
46	- **`spt.exe` / `spt`** — the canonical binary, built from the workspace. The primary integration path for harness plugins and external tooling, which mostly fire it as a subprocess at various surfaces (one-shot commands, poll listeners under a Monitor-tool-equivalent, hook tap-ins).
47
48	Both surfaces are first-class. Wire-protocol parity between them is a versioning concern from day one (a non-Rust client speaking to `spt.exe` and a Rust client linking the crates must see the same observable behavior).
49
50	## Runtime model

# spt-core

**Platform scope:** Windows + Linux for v1. macOS is out (no test machine available) but kept structurally easy — `portable-pty` and Iroh both support it, so macOS is a later test/CI-budget decision, not a re-architecture.

**Legacy migration:** it should be possible — ideally *automatic* — for a user to migrate an existing `claude_skill_owl` (modern SPT) install to spt-core (identity, agents, tracked Psyche context). Exact mechanism deferred to design; the commitment is that migration is a first-class supported path, not a manual rebuild.

Harness-independent core for the SPT ecosystem. Provides inter-agent messaging, live-agent lifecycle, terminal wrapping, self-update, and networking primitives — as both a Rust library workspace and a canonical reference binary. Designed so any agent runtime (Claude Code, Codex, Cursor, headless, future harnesses) can interface with the SPT ecosystem either by shelling out to the binary or by linking the crates directly.

Successor to `claude_skill_owl` (today's "modern SPT"), which is being rebuilt as `spt-core` to untether the system from Claude Code and lift it to a general-purpose agent-ecosystem core.

## Language

**spt-core**:
The system. Canonical name. The Rust workspace and the umbrella project.

**spt.exe / spt** (canonical binary):
The reference binary built from the workspace. Replaces today's `owl.exe`. Most external integrations (plugins, hooks, scripts in other harnesses) interact with spt-core *only* through this binary — fire-and-forget subcommands, long-running listeners under a parent harness's process supervisor, etc. Unix builds use the same name without `.exe`.

**library workspace**:
The set of Rust crates that compose spt-core. Consumers that want a deeper integration than shelling out to `spt.exe` link these crates directly. The reference binary is itself a consumer of the workspace. The expected non-binary consumers are future first-party services that link Rust directly.

**spt plugin** (separate downstream project — NOT an spt-core deliverable):
A rebuilt version of today's Claude Code `spt` plugin. It is the **first consumer** built *atop* spt-core and the **acceptance proof** of spt-core v1 (it reaches feature parity with modern SPT while delegating all core functionality to spt-core, primarily via `spt.exe`, with deeper hooks where useful) — but it **lives and builds in its own repository, outside spt-core**. It is a Claude-Code-specific *adapter*: it holds the Claude Code conventions (hooks, slash-commands, skill/plugin layout, `claude` session-invocation). **spt-core itself contains zero Claude Code conventions** — only the harness-agnostic contract the plugin binds to. The only adapter-shaped artifact ever in this repo is a generic mock/test adapter exercising the manifest + `api` contract (PRD R-DOCS-2). The M2 milestone delivers that contract + the lifecycle primitives; building the plugin is downstream work, not M2.

**Pi** (disambiguation — two meanings, never conflate):
(1) **Pi, the coding agent/harness** (`badlogic/pi-mono`) — a harness example alongside Claude Code and Codex; this is the meaning in user-facing harness lists. (2) **Pi-class node** — Raspberry-Pi-class low-power hardware hosting a Shell-only or headless SPT node; an incidental hardware descriptor, never an explicit product example. Public-facing docs must disambiguate or avoid the bare word.
_Avoid_: bare "Pi node" when the harness is meant.

**spt-daemon** (per-machine supervisor):
The single always-on, one-per-machine logical supervisor. Owns the PTYs for all hosted sessions, the node's network identity + WAN endpoint, the subnet registry, all spools, **all poll-listener logic, and all Psyche/pulse loops** — everything is consolidated here (no separate poll-listener or Psyche-wrapper processes; listeners already touch sessions directly under capsule/idle, and Psyche wrappers already invoke harness binaries directly, so they belong in the one supervisor). Collapses what the sister project planned as a separate `spt-node` daemon into one process — see Networking. The `spt-node` separate-deliverable concept is retired.

Internally the logical daemon is split into two implementation layers for seamless self-update (see Self-update):
- **broker** (stable "kernel") — holds *only* the un-transferable, must-not-die resources: PTY master fds, the spawned harness child processes, and listening network sockets. Minimal, dumb, versioned local IPC. Almost never updates.
- **daemon brain** ("userspace") — all logic (routing, registry, pulse/psyche loops, manifest parsing, update orchestration). Restarts freely on update; rehydrates from disk state and re-attaches to the broker's held handles.

Logical addressing is unchanged — still one per-machine `spt-daemon`; the broker is an internal layer, not separately addressable.

**in-session relay**:
A thin, stateless `spt.exe` task that exists only in **harness-hosted** sessions (where the agent harness is the parent process and spt cannot reach into its process tree — today's Monitor model). It streams the daemon brain's events into the session's stdout. All *stateful* listener logic lives in the daemon; the relay is a dumb pipe, freely killable and respawnable. **spt-hosted** sessions need no relay — the daemon injects events straight into the PTY.

### Deliverable shape

spt-core ships **both** a library workspace and a canonical binary:

- **Library crates** — the deeper integration path. Used by future first-party services that link Rust directly.
- **`spt.exe` / `spt`** — the canonical binary, built from the workspace. The primary integration path for harness plugins and external tooling, which mostly fire it as a subprocess at various surfaces (one-shot commands, poll listeners under a Monitor-tool-equivalent, hook tap-ins).

Both surfaces are first-class. Wire-protocol parity between them is a versioning concern from day one (a non-Rust client speaking to `spt.exe` and a Rust client linking the crates must see the same observable behavior).

## Runtime model


Full picture. D1 execution. Setting up task tracking + creating repo.


ToolSearch

Question: select:TaskCreate,TaskUpdate


Question: select:TaskCreate,TaskUpdate


PowerShell

Check local sibling dir free


False


TaskCreate

M6-D1: spt-releases repo + public face (README, licenses)

Create SaberMage/spt-releases private via gh; author README.md per grill decision 7; LICENSE-MIT + LICENSE-BINARY; Pages config deferred to D7 flip. Activate REQ-REL-1 doc stage.

Creating spt-releases repo + public face


Task #1 created successfully: M6-D1: spt-releases repo + public face (README, licenses)


M6-D2: installer scripts (install.sh / install.ps1)

Non-interactive: latest-release lookup → platform asset fetch → sha256 verify → per-OS install root → user PATH → print absolute path. Idempotent. CI rung against staged artifact + spt --version. Activate REQ-INSTALL-1/2/3/5.


Task #2 created successfully: M6-D2: installer scripts (install.sh / install.ps1)


M6-D3: manifest JSON Schema (schemars) + drift gate

schemars derives on spt-runtime manifest types; emit manifest.schema.json at stable site-root path + release asset; CI regenerate+diff gate. Activate REQ-DOCS-4 (partial), REQ-DOCS-5.


Task #3 created successfully: M6-D3: manifest JSON Schema (schemars) + drift gate


M6-D4: docs-site Tier 1 content (mdBook)

mdBook skeleton + Starlight-inspired theme; mental model; human quickstart; adapter quickstart (mock-adapter); harness-contract vertical complete; shell getting-started (spt-shell-notify); stubs for remaining verticals. Activate REQ-DOCS-1/2/3.


Task #4 created successfully: M6-D4: docs-site Tier 1 content (mdBook)


M6-D5: agent-export + generation plumbing

llms.txt + llms-full.txt; raw .md alongside HTML; CLI ref from clap; rustdoc → Pages; source-repo CI publish job → spt-releases Pages branch; drift gates. REQ-REL-1 impl, REQ-DOCS-4/5 complete.


Task #5 created successfully: M6-D5: agent-export + generation plumbing


M6-D6: release pipeline + signing ceremony

Workspace → 0.1.0; tag-triggered build matrix both runners; cross-repo release via fine-grained token; key ceremony per ADR-0015 (primary + recovery, embed both pubkeys in VerifyPolicy trusted set, seed backups); manual SignedRelease signing. Activate REQ-REL-2/3.


Task #6 created successfully: M6-D6: release pipeline + signing ceremony


M6-D7: v0.1.0 ship + flip public + acceptance run

Tag v0.1.0; ceremony; publish release; flip spt-releases public; Pages live; acceptance proof per M6-PLAN (clean-machine install, quickstarts, llms.txt + schema fetch, staged v0.1.1 verify, recovery-key verify).


Task #7 created successfully: M6-D7: v0.1.0 ship + flip public + acceptance run


Updated task #1 status


PowerShell

gh repo create SaberMage/spt-releases --private --description "SPT — releases, install scripts, and developer docs for spt-core" 2>&1

Create private spt-releases repo on GitHub


git clone https://github.com/SaberMage/spt-releases C:\Users\decid\Documents\projects\spt-releases 2>&1

Clone empty spt-releases locally


Cloning into 'C:\Users\decid\Documents\projects\spt-releases'...
warning: You appear to have cloned an empty repository.


Write

# SPT

## What is SPT?

spt-core is a **harness-independent core for an agent ecosystem**: inter-agent messaging, live-agent lifecycle, terminal hosting, seamless self-update, and zero-config cross-machine networking — shipped as both a Rust library workspace and a single canonical binary (`spt` / `spt.exe`).

SPT lets coding agents talk to each other — across sessions, across projects, and across machines — without a central server. An agent running under one harness can message an agent under another, keep a persistent identity and memory across context resets, and be reached (or revived) from any paired machine. Three pieces make that up: **(a)** the `spt` binary, the per-machine core that does the work — messaging, lifecycle, terminal hosting, P2P networking — and is what this repository distributes; **(b)** *harness adapters*, thin declarative manifests that teach spt-core how to drive a specific agent harness (an adapter for Claude Code is in development; harnesses like Codex and Pi — the [pi coding agent](https://github.com/badlogic/pi-mono) — are the kind of target the contract is designed for); and **(c)** *shell adapters*, lightweight non-agent endpoints (notifiers, sensors, automations) that join the same network. The adapter contract is public, documented, and royalty-free to build against — today's surface for third parties is the binary plus that contract.

## Status

| Capability | State |
|---|---|
| Inter-agent messaging (send / ring / ready / poll), same-machine and cross-machine | ✅ works today |
| Live-agent lifecycle (persistent identity, context communes, dormancy/wake) | ✅ works today |
| Terminal hosting + remote attach (drive a session on another node) | ✅ works today |
| Zero-config P2P networking (QUIC) + TOTP-seeded machine pairing, private subnets | ✅ works today |
| Harness contract: manifest + `spt api` (build your own adapter) | ✅ works today |
| Shell adapters (e.g. desktop-notification shell) + `spt adapter add` | ✅ works today |
| Signed, peer-propagated self-update | ✅ works today |
| Claude Code harness adapter (`spt-claude-code`) | 🚧 in development |
| OS-service registration at install (always-on after reboot) | 🔜 next |
| macOS builds | 🔜 next |
| SDK crates on crates.io | 🔜 later |

Platforms today: **Windows + Linux**.

## Install

One line, non-interactive. Downloads the latest release, sha256-verifies it, places the binary, and registers it on your user PATH.

**Linux:**

```sh
curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh
```

**Windows (PowerShell):**

```powershell
irm https://sabermage.github.io/spt-releases/install.ps1 | iex
```

Then verify:

```
spt --version
```

> **Windows note:** the PATH registration takes effect in *new* terminals. The installer prints the absolute path to the binary so you can use it immediately in the current one.

## Getting started — developers

Two agents exchanging a message, end to end, in under 10 minutes:

- **[Human quickstart](https://sabermage.github.io/spt-releases/quickstart/messaging.html)** — install, `spt ready` two agents, send and poll a message, then see what actually happened.

## Getting started — adapter developers & dev-agents

Build a working harness or shell adapter from the published contract alone:

- **[Adapter quickstart](https://sabermage.github.io/spt-releases/quickstart/adapter.html)** — a minimal adapter that satisfies the manifest + `spt api` contract, walked through via the `mock-adapter` reference (source ships with every release).
- **[Manifest JSON Schema](https://sabermage.github.io/spt-releases/manifest.schema.json)** — validate your adapter manifest mechanically.

## Documentation

- **Docs site:** <https://sabermage.github.io/spt-releases> — mental model, quickstarts, the complete harness-contract reference, CLI reference, rustdoc.
- **For AI agents:** [`llms.txt`](https://sabermage.github.io/spt-releases/llms.txt) (curated index) and [`llms-full.txt`](https://sabermage.github.io/spt-releases/llms-full.txt) (full export). Append `.md` to any doc URL for raw markdown.

## License

Split by artifact:

- **Install scripts, `mock-adapter`, and documentation content** — [MIT](./LICENSE-MIT). Copy them; that's what they're for.
- **The `spt` binary** — a short [proprietary freeware license](./LICENSE-BINARY): free to run and redistribute unmodified, no warranty. Building adapters, shells, or integrations against the public contract is explicitly **unrestricted and royalty-free**.


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

# SPT

## What is SPT?

spt-core is a **harness-independent core for an agent ecosystem**: inter-agent messaging, live-agent lifecycle, terminal hosting, seamless self-update, and zero-config cross-machine networking — shipped as both a Rust library workspace and a single canonical binary (`spt` / `spt.exe`).

SPT lets coding agents talk to each other — across sessions, across projects, and across machines — without a central server. An agent running under one harness can message an agent under another, keep a persistent identity and memory across context resets, and be reached (or revived) from any paired machine. Three pieces make that up: **(a)** the `spt` binary, the per-machine core that does the work — messaging, lifecycle, terminal hosting, P2P networking — and is what this repository distributes; **(b)** *harness adapters*, thin declarative manifests that teach spt-core how to drive a specific agent harness (an adapter for Claude Code is in development; harnesses like Codex and Pi — the [pi coding agent](https://github.com/badlogic/pi-mono) — are the kind of target the contract is designed for); and **(c)** *shell adapters*, lightweight non-agent endpoints (notifiers, sensors, automations) that join the same network. The adapter contract is public, documented, and royalty-free to build against — today's surface for third parties is the binary plus that contract.

## Status

| Capability | State |
|---|---|
| Inter-agent messaging (send / ring / ready / poll), same-machine and cross-machine | ✅ works today |
| Live-agent lifecycle (persistent identity, context communes, dormancy/wake) | ✅ works today |
| Terminal hosting + remote attach (drive a session on another node) | ✅ works today |
| Zero-config P2P networking (QUIC) + TOTP-seeded machine pairing, private subnets | ✅ works today |
| Harness contract: manifest + `spt api` (build your own adapter) | ✅ works today |
| Shell adapters (e.g. desktop-notification shell) + `spt adapter add` | ✅ works today |
| Signed, peer-propagated self-update | ✅ works today |
| Claude Code harness adapter (`spt-claude-code`) | 🚧 in development |
| OS-service registration at install (always-on after reboot) | 🔜 next |
| macOS builds | 🔜 next |
| SDK crates on crates.io | 🔜 later |

Platforms today: **Windows + Linux**.

## Install

One line, non-interactive. Downloads the latest release, sha256-verifies it, places the binary, and registers it on your user PATH.

**Linux:**

```sh
curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh
```

**Windows (PowerShell):**

```powershell
irm https://sabermage.github.io/spt-releases/install.ps1 | iex
```

Then verify:

```
spt --version
```

> **Windows note:** the PATH registration takes effect in *new* terminals. The installer prints the absolute path to the binary so you can use it immediately in the current one.

## Getting started — developers

Two agents exchanging a message, end to end, in under 10 minutes:

- **[Human quickstart](https://sabermage.github.io/spt-releases/quickstart/messaging.html)** — install, `spt ready` two agents, send and poll a message, then see what actually happened.

## Getting started — adapter developers & dev-agents

Build a working harness or shell adapter from the published contract alone:

- **[Adapter quickstart](https://sabermage.github.io/spt-releases/quickstart/adapter.html)** — a minimal adapter that satisfies the manifest + `spt api` contract, walked through via the `mock-adapter` reference (source ships with every release).
- **[Manifest JSON Schema](https://sabermage.github.io/spt-releases/manifest.schema.json)** — validate your adapter manifest mechanically.

## Documentation

- **Docs site:** <https://sabermage.github.io/spt-releases> — mental model, quickstarts, the complete harness-contract reference, CLI reference, rustdoc.
- **For AI agents:** [`llms.txt`](https://sabermage.github.io/spt-releases/llms.txt) (curated index) and [`llms-full.txt`](https://sabermage.github.io/spt-releases/llms-full.txt) (full export). Append `.md` to any doc URL for raw markdown.

## License

Split by artifact:

- **Install scripts, `mock-adapter`, and documentation content** — [MIT](./LICENSE-MIT). Copy them; that's what they're for.
- **The `spt` binary** — a short [proprietary freeware license](./LICENSE-BINARY): free to run and redistribute unmodified, no warranty. Building adapters, shells, or integrations against the public contract is explicitly **unrestricted and royalty-free**.


MIT License

Copyright (c) 2026 SaberMage

Applies to: the install scripts (install.sh, install.ps1), the mock-adapter
source, and the documentation content published in this repository and on its
GitHub Pages site. The `spt` binary is licensed separately — see LICENSE-BINARY.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


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

MIT License

Copyright (c) 2026 SaberMage

Applies to: the install scripts (install.sh, install.ps1), the mock-adapter
source, and the documentation content published in this repository and on its
GitHub Pages site. The `spt` binary is licensed separately — see LICENSE-BINARY.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


SPT Binary License (freeware)

Copyright (c) 2026 SaberMage

This license applies to the `spt` / `spt.exe` binary distributed via this
repository's releases ("the Software"). The install scripts, the mock-adapter,
and the documentation are separately licensed under LICENSE-MIT.

1. GRANT. You may download, install, and run the Software, on any number of
   machines, for any purpose, commercial or non-commercial, free of charge.

2. REDISTRIBUTION. You may redistribute unmodified copies of the official
   Software binaries free of charge, provided this license accompanies them.
   You may not sell the Software itself, modify it, or distribute modified
   versions of it.

3. ADAPTERS AND INTEGRATIONS. Developing adapters, shells, or any other
   software that interoperates with the Software through its published public
   contract (the manifest format and JSON Schema, the `spt api` surface, and
   the documented CLI) is UNRESTRICTED AND ROYALTY-FREE. No fee, royalty, or
   separate license is required for any such adapter, shell, or integration,
   regardless of how it is licensed, distributed, or commercialized.

4. NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
   KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.

5. LIMITATION OF LIABILITY. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
   AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OF OR INABILITY TO USE THE
   SOFTWARE.

6. TERMINATION. This license terminates automatically if you breach it.
   Sections 4 and 5 survive termination.


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

SPT Binary License (freeware)

Copyright (c) 2026 SaberMage

This license applies to the `spt` / `spt.exe` binary distributed via this
repository's releases ("the Software"). The install scripts, the mock-adapter,
and the documentation are separately licensed under LICENSE-MIT.

1. GRANT. You may download, install, and run the Software, on any number of
   machines, for any purpose, commercial or non-commercial, free of charge.

2. REDISTRIBUTION. You may redistribute unmodified copies of the official
   Software binaries free of charge, provided this license accompanies them.
   You may not sell the Software itself, modify it, or distribute modified
   versions of it.

3. ADAPTERS AND INTEGRATIONS. Developing adapters, shells, or any other
   software that interoperates with the Software through its published public
   contract (the manifest format and JSON Schema, the `spt api` surface, and
   the documented CLI) is UNRESTRICTED AND ROYALTY-FREE. No fee, royalty, or
   separate license is required for any such adapter, shell, or integration,
   regardless of how it is licensed, distributed, or commercialized.

4. NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
   KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.

5. LIMITATION OF LIABILITY. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
   AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OF OR INABILITY TO USE THE
   SOFTWARE.

6. TERMINATION. This license terminates automatically if you breach it.
   Sections 4 and 5 survive termination.


PowerShell

cd C:\Users\decid\Documents\projects\spt-releases; git checkout -b main 2>&1; git add README.md LICENSE-MIT LICENSE-BINARY; git commit -m @'
Public face: README, MIT/freeware license split

README per the M6 grill spec: verbatim "What is SPT?" tagline, honest
three-part ecosystem paragraph (Pi disambiguated to the pi coding agent,
harness adapters phrased as in-development), works-today/next status table,
the two canonical non-interactive install one-liners (lapse-proof Pages
URLs), per-audience getting-started sections, docs + llms.txt pointers.

LICENSE-MIT covers install scripts / mock-adapter / docs content;
LICENSE-BINARY is the short proprietary freeware license with the explicit
royalty-free adapter/shell development clause.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
'@; git push -u origin main 2>&1

Commit and push spt-releases public face


Switched to a new branch 'main'
warning: in the working copy of 'LICENSE-BINARY', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'LICENSE-MIT', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'README.md', LF will be replaced by CRLF the next time Git touches it
[main (root-commit) 2debda8] Public face: README, MIT/freeware license split
 3 files changed, 134 insertions(+)
 create mode 100644 LICENSE-BINARY
 create mode 100644 LICENSE-MIT
 create mode 100644 README.md
branch 'main' set up to track 'origin/main'.
To https://github.com/SaberMage/spt-releases
 * [new branch]      main -> main
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-core


Edit

id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = []  # activates at M6 start (D1/D5)

id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = ["doc"]  # doc activated M6-D1: SaberMage/spt-releases created (private, flips at D7) + README per grill decision 7 (verbatim tagline, honest ecosystem paragraph w/ Pi disambiguation, status table, canonical-URL one-liners, per-audience getting-started, llms.txt pointers) + LICENSE-MIT/LICENSE-BINARY split w/ royalty-free adapter clause (spt-releases@2debda8); doc tag on ADR-0014 (the topology decision). impl (publish pipeline) activates at D5


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


# Publish-target repo + lapse-proof canonical URL

# Publish-target repo + lapse-proof canonical URL

<!-- [doc->REQ-REL-1] delivered M6-D1: SaberMage/spt-releases created (private), README + LICENSE-MIT/LICENSE-BINARY pushed (2debda8); Pages goes live at the D7 flip -->


The file C:\Users\decid\Documents\projects\spt-core\docs\adr\0014-publish-target-repo-and-lapse-proof-canonical-url.md has been updated successfully. (file state is current in your context — no need to Read it back)

# Publish-target repo + lapse-proof canonical URL

<!-- [doc->REQ-REL-1] delivered M6-D1: SaberMage/spt-releases created (private), README + LICENSE-MIT/LICENSE-BINARY pushed (2debda8); Pages goes live at the D7 flip -->

# Publish-target repo + lapse-proof canonical URL

The `spt-core` source repo stays **private long-term** (clean-room rebuild; internal planning corpus). All dev-facing artifacts publish to a separate repo, **`SaberMage/spt-releases`** — README, GitHub Releases (binaries + dev assets), and the rendered dev docs via GitHub Pages. It is a pure publish target: doc truth and CI doc-generation live in the source repo; a pipeline pushes rendered output. Created private; flips public anytime (Pages goes live at the flip — no Pro plan).

**Canonical URL = `https://sabermage.github.io/spt-releases`, with no custom domain configured on Pages.** This URL is what gets baked everywhere: docs cross-links, `llms.txt`, the install one-liner in the README, and every adapter's pack-in install logic.

## Considered options

- **Custom domain (`spt-core.decidel.com`) as Pages canonical** — rejected. Configuring a Pages custom domain makes GitHub redirect all github.io URLs to it, so the domain becomes canonical for *everything*, including the install one-liner baked into third-party adapters. A lapsed/transferred domain then hands arbitrary code execution (`curl | sh`) to whoever registers it next. The github.io URL is account-bound, free, and cannot lapse.
- `spt-core.decidel.com` survives only as an optional **human-facing redirect → Pages** (marketing/vanity). It never appears in install commands, adapter code, or doc cross-links.

## Consequences

- Install scripts are served from the Pages URL (not `raw.githubusercontent.com`), keeping hosting swappable behind the stable URL.
- DOCS-STRATEGY principle 9 (never-renamed URLs; agents cache links) is anchored to the github.io origin permanently.
- Everything authored in `spt-releases` must be public-safe from day one.

+<!-- [doc->REQ-REL-1] delivered M6-D1: SaberMage/spt-releases created (private), README + LICENSE-MIT/LICENSE-BINARY pushed (2debda8); Pages goes live at the D7 flip -->


383	[[requirements]]
384	id = "REQ-INSTALL-5"
385	title = "Non-interactive install path: the canonical one-liner doubles as every adapter's pack-in on-demand install (no second mechanism); sha256-verified fetch; user-PATH registration"
386	required_stages = []  # activates at M6 start (D2)
387	[[requirements]]
388	id = "REQ-REL-1"
389	title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
390	required_stages = []  # activates at M6 start (D1/D5)
391	[[requirements]]
392	id = "REQ-REL-2"
393	title = "Release asset set consumable by the self-updater: platform binaries, SHA256SUMS, SignedRelease metadata, manifest schema, mock-adapter zip; tag-triggered cross-repo pipeline"
394	required_stages = []  # activates at M6 start (D6/D7)
395	[[requirements]]
396	id = "REQ-REL-3"
397	title = "Two-key release-signing trust anchor: primary + offline never-used recovery, both pubkeys embedded in the binary's trusted set, manual local signing (ADR-0015)"
398	required_stages = []  # activates at M6 start (D6)
399
400	# ───────────────────────────── Documentation ────────────────────────────────
401	[[requirements]]
402	id = "REQ-DOCS-1"

[[requirements]]
id = "REQ-INSTALL-5"
title = "Non-interactive install path: the canonical one-liner doubles as every adapter's pack-in on-demand install (no second mechanism); sha256-verified fetch; user-PATH registration"
required_stages = []  # activates at M6 start (D2)
[[requirements]]
id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = []  # activates at M6 start (D1/D5)
[[requirements]]
id = "REQ-REL-2"
title = "Release asset set consumable by the self-updater: platform binaries, SHA256SUMS, SignedRelease metadata, manifest schema, mock-adapter zip; tag-triggered cross-repo pipeline"
required_stages = []  # activates at M6 start (D6/D7)
[[requirements]]
id = "REQ-REL-3"
title = "Two-key release-signing trust anchor: primary + offline never-used recovery, both pubkeys embedded in the binary's trusted set, manual local signing (ADR-0015)"
required_stages = []  # activates at M6 start (D6)

# ───────────────────────────── Documentation ────────────────────────────────
[[requirements]]
id = "REQ-DOCS-1"


id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = []  # activates at M6 start (D1/D5)

id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = ["doc"]  # doc activated M6-D1: SaberMage/spt-releases created (private, flips at D7) + README per grill decision 7 (verbatim tagline, honest ecosystem paragraph w/ Pi disambiguation, status table, canonical-URL one-liners, per-audience getting-started, llms.txt pointers) + LICENSE-MIT/LICENSE-BINARY split w/ royalty-free adapter clause (spt-releases@2debda8); doc tag on ADR-0014 (the topology decision). impl (publish pipeline) activates at D5


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

id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = []  # activates at M6 start (D1/D5)

id = "REQ-REL-1"
title = "spt-releases publish-target repo: README public face, licensing split, Pages docs at the permanent lapse-proof canonical URL (ADR-0014)"
required_stages = ["doc"]  # doc activated M6-D1: SaberMage/spt-releases created (private, flips at D7) + README per grill decision 7 (verbatim tagline, honest ecosystem paragraph w/ Pi disambiguation, status table, canonical-URL one-liners, per-audience getting-started, llms.txt pointers) + LICENSE-MIT/LICENSE-BINARY split w/ royalty-free adapter clause (spt-releases@2debda8); doc tag on ADR-0014 (the topology decision). impl (publish pipeline) activates at D5

-required_stages = []  # activates at M6 start (D1/D5)

+required_stages = ["doc"]  # doc activated M6-D1: SaberMage/spt-releases created (private, flips at D7) + README per grill decision 7 (verbatim tagline, honest ecosystem paragraph w/ Pi disambiguation, status table, canonical-URL one-liners, per-audience getting-started, llms.txt pointers) + LICENSE-MIT/LICENSE-BINARY split w/ royalty-free adapter clause (spt-releases@2debda8); doc tag on ADR-0014 (the topology decision). impl (publish pipeline) activates at D5

title = "Release asset set consumable by the self-updater: platform binaries, SHA256SUMS, SignedRelease metadata, manifest schema, mock-adapter zip; tag-triggered cross-repo pipeline"


Create SaberMage/spt-releases private via gh; author README.md per grill decision 7; LICENSE-MIT + LICENSE-BINARY; Pages config deferred to D7 flip. Activate REQ-REL-1 doc stage.

Creating spt-releases repo + public face

Non-interactive: latest-release lookup → platform asset fetch → sha256 verify → per-OS install root → user PATH → print absolute path. Idempotent. CI rung against staged artifact + spt --version. Activate REQ-INSTALL-1/2/3/5.

schemars derives on spt-runtime manifest types; emit manifest.schema.json at stable site-root path + release asset; CI regenerate+diff gate. Activate REQ-DOCS-4 (partial), REQ-DOCS-5.

mdBook skeleton + Starlight-inspired theme; mental model; human quickstart; adapter quickstart (mock-adapter); harness-contract vertical complete; shell getting-started (spt-shell-notify); stubs for remaining verticals. Activate REQ-DOCS-1/2/3.

llms.txt + llms-full.txt; raw .md alongside HTML; CLI ref from clap; rustdoc → Pages; source-repo CI publish job → spt-releases Pages branch; drift gates. REQ-REL-1 impl, REQ-DOCS-4/5 complete.

Workspace → 0.1.0; tag-triggered build matrix both runners; cross-repo release via fine-grained token; key ceremony per ADR-0015 (primary + recovery, embed both pubkeys in VerifyPolicy trusted set, seed backups); manual SignedRelease signing. Activate REQ-REL-2/3.

Tag v0.1.0; ceremony; publish release; flip spt-releases public; Pages live; acceptance proof per M6-PLAN (clean-machine install, quickstarts, llms.txt + schema fetch, staged v0.1.1 verify, recovery-key verify).


PowerShell

traceable-reqs check 2>&1 | Select-Object -Last 8

Run traceable-reqs coverage check


  [OK] REQ-UPD-2  required: [impl, unit]  stages: -doc +impl +unit -int
       All binaries signature-verified before handoff
  [OK] REQ-UPD-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       No endpoint process terminates/suspends during self-update
  [OK] REQ-UPD-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Update gated on user confirmation by default; opt-in full-auto
  [OK] REQ-UPD-5  required: [impl, unit]  stages: -doc +impl +unit -int
       spt-core ripple-updates registered adapters


traceable-reqs check 2>&1 | Select-String -Pattern "FAIL|MISSING|REQ-REL-1|^\d+/|passed|failed" | Select-Object -First 10

Check overall result + REQ-REL-1 row


       A surfaced concurrent context pair is durably preserved (both versions, tracked artifacts) until a strictly dominating write
clears it; no reconcile failure path discards an unmerged version (6.6, ADR-0013)
       Concurrent SQLite openers (registry/spool) must not fail with 'database is locked' (4.7)
       Stale registry entries degrade to fallback, never hard-fail (4.3)
  [OK] REQ-REL-1  required: [doc]  stages: +doc -impl -unit -int


traceable-reqs check > $null 2>&1; "exit=$LASTEXITCODE"; traceable-reqs check 2>&1 | Select-Object -First 4

Get exit code + summary header


exit=0
139 requirements: 139 complete, 0 incomplete, 0 findings
  [OK] REQ-API-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       api prefix and adapter_name on every machinery invocation
  [OK] REQ-API-2  required: [impl, unit, int]  stages: -doc +impl +unit +int