=== #330 ===
TITLE: fix: peer-propagated updates never carry the docs bundle — a node updated from a peer keeps the PREVIOUS release's docs (SCELTOUIN on 0.72.0 serves the 0.71.0 changelog)
LABELS: state: BACKLOG,type: BUGFIX
BODY:
**Symptom.** SCELTOUIN runs spt 0.72.0 (counter 108, applied) but `http://localhost:5474/sceltouin/docs/changelog.html` tops out at `## [0.71.0]`. HFENDULEAM, which fetched 0.72.0 from GitHub, serves 0.72.0.

**Root cause (code).** Docs ride the update only on the `spt update fetch` GitHub path (`cli.rs` ~11177 fetches `spt-docs.tar.gz`, `cache.stage_docs`). The peer-propagation pull has no docs leg at all: `UpdRecord` (spt-net `net/update.rs`) has `Fetch`/`FetchArtifact` only, and `propagate.rs` ~533 stages just this platform's binary via `stage_update_set`. On apply, `land_staged_docs` (`cli.rs` ~9749) reads the absent staged bundle as "docs-less release" and returns SILENTLY — no `UPDATE_DOCS_SKIPPED`, and no retry ever comes because a propagated node never runs `fetch`. The serving peer can't help either: it `clear_staged_docs()` right after landing (`cli.rs` ~9827), so it holds nothing to serve.

**Contract breached.** REQ-DOCS-RELEASE-ASSET: "Apply lands/refreshes $SPT_HOME/docs (single current copy = docs always match the installed binary)" and "UPDATE_DOCS_SKIPPED loud, retried next fetch". Every node except the GitHub-fetching one is on stale docs after every release.

**Ask.** (1) Keep the verified docs bundle in the release cache after landing so a peer can serve it. (2) Add a docs leg to the pull protocol (`FetchDocs`), requester verifies with `verify_update_set_docs` against the SIGNED set and `stage_docs`. (3) When the signed set declares docs but none is staged, `land_staged_docs` must print `UPDATE_DOCS_SKIPPED`, never return silent. Evidence in comment.

---
Requester: doyle
COMMENTS(1):
--- SaberMage:
Evidence (doyle, 2026-09-24, main @ `11066dce`, spt-core 0.72.0 on both nodes).

**Field, SCELTOUIN (read-only snapshot by fall-a):**
- `spt --version` → 0.72.0; coordinator + broker image 0.72.0; `applied.json` `{"version":108}`, `applied-state.json` `{"phase":"applied","version":108}`.
- `release.json` metadata declares `"docs":{"asset_name":"spt-docs.tar.gz","sha256":"e2aa42c8…"}` — the signed set names the bundle.
- `releases/` holds NO `docs.tar.gz`; `releases/artifacts/x86_64-pc-windows-msvc.bin` rewritten 9/23 11:59 while the two linux `.bin` still date 9/21 5:30 PM → the 0.72.0 stage was a peer pull (one platform), the 0.71.0 stage was a GitHub fetch (all three).
- daemon log: `UPDATE_STAGED:108:BrainOnly (from 14efb80c…)` (= HFENDULEAM) then `BRAIN_UPDATE_RESTART`; **zero** `UPDATE_DOCS_LANDED` / `UPDATE_DOCS_SKIPPED` lines in either log file.
- `$SPT_HOME/docs` last written 9/21 5:30 PM (the 0.71.0 landing); no `docs.new` / `docs.old`; `docs/changelog.md` first heading `## [0.71.0] - 2026-09-18`.
- `http://localhost:5474/sceltouin/docs/changelog.html` (proxied from HFENDULEAM): no `0.72.0` string; `/hfenduleam/docs/changelog.html` has it.

**Control, HFENDULEAM:** same `release.json`, `releases/` also has no `docs.tar.gz` (cleared after landing, `cli.rs` ~9827), `docs/` files dated the 0.72.0 bundle build, changelog tops at 0.72.0. Its daemon log carries no `UPDATE_DOCS_*` either — those lines print on the CLI's stderr, not the daemon's, so the daemon log cannot show a docs landing on any node.

**Code:**
- `crates/spt-net/src/net/update.rs:41` `enum UpdRecord` — `Query / Offer / UpToDate / Fetch / FetchArtifact / StatusQuery / Status / Chunk / Done / Err`; no docs record.
- `crates/spt-daemon/src/propagate.rs:243-274` serve side answers `FetchArtifact` with `cache.load_artifact_for_platform` only; `:524-539` pull side stages `stage_update_set(offer, {current_platform → artifact})` — one binary, no docs.
- `crates/spt/src/cli.rs:11177-11204` — the ONLY `stage_docs` call, inside `spt update fetch` (GitHub transport).
- `crates/spt/src/cli.rs:9748-9751` `land_staged_docs`: `let Some(bundle) = cache.staged_docs() else { return; }` — comment says "docs-less release or docs fetch skipped — nothing to land", but the signed set in hand DOES declare docs; the silent return is the loud-skip contract's hole.
- `crates/spt/src/cli.rs:9827` `cache.clear_staged_docs()` after landing → the source peer keeps no servable copy.

**Discriminator for triage on any node:** `releases/artifacts` holding only this platform's `.bin` = peer pull (docs impossible today); all three = GitHub fetch (docs landed iff `docs/` mtime ≈ bundle build time).

**Fix shape (ask in the issue body):** persist the verified bundle beside the set after landing; add a `FetchDocs` leg mirroring `FetchArtifact` (serve `docs.tar.gz`, requester verifies with `verify_update_set_docs` against the signed set, then `stage_docs`); `land_staged_docs` prints `UPDATE_DOCS_SKIPPED: signed set declares docs but none staged` when `meta.docs.is_some()` and nothing is staged. int gate: peer-pull e2e lands version-matched docs (kin `docs_bundle_e2e`), plus the loud-skip arm.

**Interim workaround per node:** `spt update fetch` (GitHub) then `spt update apply` re-stages and lands the bundle; requires gh access on that node.

*\-doyle@HFENDULEAM*

=== #329 ===
TITLE: fix: adapter updates errors during `spt update`
LABELS: state: BACKLOG,type: BUGFIX
BODY:
if the old (pre-update) SPT version is under the dependency floor for an adapter update, the adapter will show an error and fail to update.

given that the brain immediately runs the new SPT version after the spt-core phase of `spt update`, the proceeding adapter updates should always see the new version of spt-core as what's current, and compare their dependency floor against that value.

field excerpt:
```
Fetching update set from BigscreenVR/spt-bs-releases…
  fetching spt-x86_64-windows.exe (x86_64-pc-windows-msvc)…
  fetching spt-x86_64-linux (x86_64-unknown-linux-gnu)…
  fetching spt-x86_64-linux-musl (x86_64-unknown-linux-musl)…
  fetching spt-docs.tar.gz (docs bundle)…
UPDATE_STAGED:108 (set, 3 platforms, from BigscreenVR/spt-bs-releases) — run `spt update apply` to apply
Updated spt-core to v0.72.0.
Changelog: https://github.com/BigscreenVR/spt-bs-releases/releases
Restart the daemon to finish: daemon-coordinated features keep running the previous version until you restart it (`spt daemon stop` then `spt daemon start`). Run `spt daemon status` to confirm which version is live.
UPDATE_DOCS_LANDED: C:\Users\Admin\AppData\Local\spt-core\docs
ADAPTER_UPDATE:claude-spt: 0.25.31 -> 0.41.3 (fetching adapter.spt from BigscreenVR/claude-spt-bs)
ADAPTER_UPDATE_UNSIGNED:claude-spt: no signing_key — trusting HTTPS + GitHub
ADAPTER_UPDATE_REFUSED:claude-spt: adapter 'claude-spt' requires spt-core 0.68.0 or newer, but this machine runs spt-core 0.53.0. Update spt-core first (`spt update`), then retry.
ADAPTER_UPDATE:omp-spt: 0.3.32 -> 0.9.4 (fetching adapter.spt from BigscreenVR/omp-spt)
ADAPTER_UPDATE_UNSIGNED:omp-spt: no signing_key — trusting HTTPS + GitHub
ADAPTER_UPDATE_REFUSED:omp-spt: adapter 'omp-spt' requires spt-core 0.67.0 or newer, but this machine runs spt-core 0.53.0. Update spt-core first (`spt update`), then retry.
ADAPTER_UPDATE_SUMMARY:claude-spt: FAILED
ADAPTER_UPDATE_SUMMARY:omp-spt: FAILED
```

---
Requester: discord:reavo.
COMMENTS(0):


=== #322 ===
TITLE: add: proliferate adapter update staging
LABELS: state: BACKLOG,type: ADDITION
BODY:
have adapter updates auto-transfer and stage across subnet nodes the same way (i think?) spt-core updates do

maybe the update-on-demand commands from also check and fetch from peers for core-and-adapter update binaries, too. or that's the default behavior, and `--remote` is a flag that signals the use of non-subnet-update-fetching.

---
Requester: discord:reavo.
COMMENTS(0):


=== #278 ===
TITLE: adapter update never prunes strings/: files the new archive dropped stay installed (flat-layout skills beside SKILL.md dirs, a foreign claude-spt.exe)
LABELS: state: BACKLOG,type: CHANGE
BODY:
Found by emphasys (omp-spt 0.5.0 field, 2026-09-06), measured by doyle on this box: adapters/_github/BigscreenVR-omp-spt/strings/skills holds BOTH layouts — archive-shipped <name>/SKILL.md (epoch mtimes = extracted) beside stale flat live.md/ready.md/subnet.md/version.md/force-stop.md/setup.md/list-agents.md + strings/briefs/*.md + a stray claude-spt.exe, all mtime 2026-07-09 (first install). Later archives ship only skills/<name>/SKILL.md; nothing removed them.

Mechanism: crates/spt/src/cli.rs:20770-20776 apply_release_crc_swap is ADDITIVE by design — a file dropped by the new version is left in place, premise: a stale unreferenced file is harmless. That premise holds for binaries (the Windows exe-lock reason the CRC swap exists) and FAILS for strings/: a harness that discovers skills by scanning the dir REFERENCES every stale file, so an old skill keeps firing after the adapter retired it. Public harness contract states no update semantics at all, so an adapter builder cannot know files persist.

Ask: on update, prune dest/strings/ entries absent from the staged archive (data only; binaries stay additive, .old litter rules untouched); state the semantics in the harness-contract update section. Owner ruling + shape in comment.

---
Requester: doyle
COMMENTS(2):
--- SaberMage:
**Owner ruling (doyle, 2026-09-06):** the additive CRC swap stays for BINARIES — that is the Windows exe-lock reason it exists (REQ-ADAPTER-LIVE-UPDATE) and a running translation child must never be yanked mid-update. The premise fails only for `strings/`, which is pure data a harness discovers by scan, so the fix is scoped there.

**Shape:** `spt_daemon::crc_swap::plan_crc_swap` gains a PRUNE row class: every file under `dest/strings/` with no counterpart under `staging/strings/` is removed AFTER the swap commits (data only; nothing under the install root outside `strings/` is ever pruned; `.old`/`.new` litter rules untouched). Both apply paths (`apply_release_crc_swap` cli.rs:20778 and the daemon-hosted `adapter_apply`) run the same plan, so one routing. Rewrite the doc comment at cli.rs:20770-20776 by replacement — it currently states the falsified premise as fact.

**Docs (same commit):** the harness-contract update section states the semantics once: `strings/` mirrors the archive on every update (a file you stop shipping disappears); binaries are additive (a dropped binary stays until the next clean install). Today the contract says nothing, which is the docs gap that let this run for two months.

**Traceability:** mint `REQ-ADAPTER-UPDATE-PRUNES-STRINGS` (doc, impl, unit, int). Unit: plan over a dest with a stale `strings/skills/old.md` + a stale `dest/foo.exe` yields exactly ONE prune row (the .md), never the exe. Int: real `adapter update` from archive v1 (ships `skills/a.md`) to v2 (ships `skills/a/SKILL.md` only) leaves no `a.md`. Mutation proof: remove the prune arm and the int goes red.

**Measurement on this box:** `_github/BigscreenVR-omp-spt/strings/skills` = 5 SKILL.md dirs (epoch mtimes, extracted) + 7 flat .md + 4 briefs + `claude-spt.exe`, all stale files mtime 2026-07-09. Builder: todlando, thin lane after #276/#277; not a v0.67.x rider. Interim for adapter builders: delete the stale files by hand once.

*\-doyle@HFENDULEAM*
--- SaberMage:
Field confirmation (emphasys, omp-spt 0.6.0, 2026-09-06): after deleting the stale flat-layout strings by hand, the 0.6.0 `spt adapter update` did NOT bring them back — the archive is clean; the persistence was the additive swap alone. Mechanism confirmed from the other side.

*\-doyle@HFENDULEAM*

=== #269 ===
TITLE: change: `spt adapter list`
LABELS: state: BACKLOG,type: CHANGE
BODY:
needs to be more helpful. only these line item types:
- `<adapter-name> v<adapter-version>` --> all in cyan
- `:<adapter-profile-name>` --> nested, dimmed white
- `:<custom-adapter-profile-name> (custom)` --> nested, orange. for profiles that don't ship with the parent adapter

and no more spt-internal jargon.

---
Requester: discord:reavo.
COMMENTS(0):


=== #264 ===
TITLE: add: `spt adapter add <adapter-name> --via-subnet|-vs`
LABELS: state: BACKLOG,type: IDEA,flag: NEEDS-OPERATOR
BODY:
- check subnet nodes for the adapter
  - is their adapter list + version-per-adapter published within nodes' peer pump registry entry?
- transfer & install from the first discovered node which has the adapter
- new: complimentary adapter [update] mode which "knows" if the initial installation happened using `--via-subnet`, and continues to use subnet p2p for updates.

### tangential: `spt adapter list` has an "available" section
a second section at the end for all adapters found on other subnet nodes. any of them can be installed with `spt adapter add --via-subnet`
++ color-coded by type
- text at top: `Types: [HARNESS](orange), [GATEWAY](magenta), and [SHELL](cyan)`
grouped in that order, alphabetically per group

### tangential: the potential for spt-core to ship with some inbuilt shells
`fileshare` and `heartbeat` being the two first candidates.

---
Requester: discord:reavo.
COMMENTS(0):


=== #259 ===
TITLE: add: in the endpoint picker, show adapters' version
LABELS: state: BACKLOG,type: ADDITION
BODY:
anywhere an adapter shows up in the picker TUI, follow the `adapter[:profile]` text with ` v#.#.#` (whatever its actual version number is)

---
Requester: discord:reavo.
COMMENTS(0):


=== #4 ===
TITLE: add: "seamless update contract" for adapters
LABELS: state: BACKLOG,type: ADDITION,flag: NEEDS-OPERATOR
BODY:
largely docs with some API helpers

strongly encourage adapter devs to build their apps with seamless live updates as a first-class affordance. should be supported through the manifest-->a number of adapter-provisioned commands for initiating and completing a seamless update with minimal observed interruption to the agents using them

should also provision for in-place updates of any adapter-owned ResidentServices.

---
Requester: discord:reavo.
COMMENTS(0):


=== #2 ===
TITLE: Upgrading a shared-exe service must not require a node-wide stand-down of every instance
LABELS: state: EVAL,type: CHANGE
BODY:
Routed from spt-progress-tool#23 (flynn hand-off; Operator ruled it out of alchemy's next milestone — the constraint lives in spt-core's install path, so the Request mints here).

Today deploying a new release of a service exe (field case: alchemy) requires EVERY instance on the node to be down simultaneously: all instances execute the same installed exe and Windows refuses to overwrite a running one. A routine upgrade therefore needs cross-owner coordination, a window where the service is absent from the node entirely, and — because the only clean way down is `spt shell teardown` — each instance returns having lost its perch: repo binding gone (re-run bind-repo), tag cursor re-baselined at the digest tip so tags written during the gap are never scanned, and a new canonical id.

Operator ruling on record: the brief outage itself is acceptable; the design requirement is that an upgrade must not need the synchronized stand-down at all.

Three sketched shapes, none decided (design ruling owed by doyle):
1. Versioned install dirs — install per-version, point registration at the new dir; running exes keep their own file open, next spawn picks up new bits. Costs disk plus a reaping story.
2. Rename-then-replace — Windows permits renaming a running exe: move the live binary aside, write the new one in its place, delete the orphan on next boot. Cheap; leaves running instances executing an unnamed file.
3. Upgrade-on-relaunch handshake — instances stage the new binary and swap as each relaunches; the node converges with no synchronized window.

Cost coupling noted in the hand-off: the teardown-loses-the-perch penalty above is the same cost that makes the STALE-ONLINE liveness fix worth landing.

Provenance: https://github.com/BigscreenVR/spt-progress-tool/issues/23 — flynn Watches from that side and closes #23 as routed once this ref exists.

Attachments:
- [req-shared-exe-upgrade.txt](https://github.com/BigscreenVR/spt-bs-releases/releases/download/untagged-3ecc0debcb4b3bffe480/shx-1785204617552120000-req-shared-exe-upgrade.txt)

---
Requester: doyle
COMMENTS(1):
--- SaberMage:
DESIGN RULED (doyle, 2026-07-27): shape 2 — rename-then-replace — and not as a new invention: spt-core already ships this exact pattern for its OWN binary in applyhost.rs ("Swap the binary: the current executable steps aside (<exe>.old-<ver> — a running image survives its own rename on both platforms) → land the artifact → roll back on failure"). The service-exe install path adopts the same step-aside discipline, ideally sharing the helper.

Why not the others:
- Shape 1 (versioned install dirs) — registration churn on every upgrade plus a directory-level GC story, and it diverges from the in-house pattern for no gain; the step-aside file IS the version history we need (one file class, self-naming).
- Shape 3 (upgrade-on-relaunch handshake) — a staging state machine to buy a property shape 2 gets for free: with rename-then-replace, each running instance keeps executing its renamed image and picks up new bits at its OWN next relaunch. Node converges with no synchronized window. That is the requirement, satisfied without new coordination state.

Cleanup story: .old-<ver> orphans sweep at service reconcile — attempt delete; while any process still holds the file Windows refuses with a sharing violation and the sweep retries next cycle. Self-healing GC, no boot dependency, mirrors the applyhost rollback vocabulary.

Scope fence: the teardown-loses-the-perch cost named in the hand-off is a SEPARATE defect (perch preservation across relaunch — SHELL-STALE-ONLINE kin). Shape 2 removes the need for upgrade-time teardown, which decouples that cost from upgrades; it does not fix it. It stays its own Request when filed.

Moving to eval; build waits on a milestone slot.

=== #62 ===
TITLE: change: should the extractor force (or loudly refuse) a non-executable declared entry binary?
LABELS: state: EVAL,type: CHANGE
BODY:
Finding by lia (Athenaeum, 2026-07-31/08-01), flagged via perri, filed by doyle so it is not lost. Context: claude-spt shipped its Linux binaries mode 644 (exec bit lost in a Windows-built archive — fixed adapter-side @claude-spt 2aa9768 + REQ-HAZARD-ADAPTER-EXEC-BIT). spt-core extracted faithfully and was NOT at fault; the node was still bricked for that adapter (no hooks, no endpoint hosting) until manual chmod.

QUESTION for the design record: should spt-core's extractor treat an adapter's DECLARED entry binary that arrives non-executable as (a) force exec-bit as defense in depth, (b) force + warn loudly, (c) refuse to install with a diagnostic naming the file and mode, or (d) extract faithfully as today (adapter packer owns correctness)?

TENSION to grill, not pre-decided: forcing silently masks the adapter's packaging defect class that was just caught by red-proven packer validation; extracting faithfully bricks the node for that adapter until a human intervenes. A slip in ANY adapter reproduces this — the question is which side of the seam owns the last line of defense, and whether the failure is loud or silent either way.

---
Requester: lia (via perri; filed by doyle)

---
Requester: doyle
COMMENTS(9):
--- SaberMage:
Incident detail from lia (Athenaeum-Library, spt-core 0.49.0, claude-spt 0.25.30; Reavo approved the filing) — the concrete case behind this question, recorded verbatim in substance:

- spt adapter update claude-spt failed at the post-step with 'failed to spawn session: Permission denied (os error 13)', exit 1. The shipped adapter.spt carried Linux binaries at mode 0644 (only the Windows .exe was 0755 — the one file Linux cannot use); tar built on Windows, no POSIX exec bit. Sibling omp-spt normalizes its tar and was unaffected — that entry-mode diff made the cause unambiguous.
- MISDIRECTION MATTERS: the node operator sees only a downstream EACCES at the post-step, which reads as a LOCAL permissions problem and points AWAY from the upstream packaging cause.
- BLAST RADIUS: wider than a failed update — no hooks, no endpoint hosting, and a fresh spt adapter add on any Linux node lands in the same bricked state. One bit, silently, fleet-wide per adapter.
- SCOPING lia requests deliberately: force exec on the DECLARED entry binary only (the path resolved from the manifest, e.g. [update.post] command = {adapter_dir}/claude-spt), NOT a blanket chmod — perri's own packer fix rejected one-shot --mode=0755 because it would mark manifest.toml and strings/ executable; the same objection applies core-side. Alternative if core stays faithful-to-archive: validate at extraction and fail LOUD naming the real cause ('adapter entry binary extracted non-executable — packaging defect upstream').
- Packaging side is CLOSED for claude-spt (2aa9768, REQ-HAZARD-ADAPTER-EXEC-BIT, red-proven; chmod-then-tar silently records 0644 — mode must be forced at tar time). The open question is core's robustness to the NEXT adapter, from any author — nothing structural prevents a repeat.
- Interim state: manual chmod on Athenaeum, fragile by construction (next bump re-extracts 0644). claude-spt 0.25.31 cut ruled (perri cuts, doyle gates); lia runs the post-cut clean-install proof; lia surveying other Linux nodes for the same bricked state.
--- SaberMage:
lia's measured evidence (Athenaeum-Library; relayed by doyle — lia cannot see this private tracker). The node-side repair vs build-side no-op distinction #62 turns on:

NODE-SIDE (post-extraction) — WORKS: installed binary before -rw-r--r-- (962712 bytes), chmod +x, after -rwxr-xr-x (mode READ BACK, not assumed). Re-ran spt adapter update claude-spt: ADAPTER_UPDATE_UPTODATE 0.25.30, post-step did REAL pending work (CC plugin sptc 0.1.16 -> 0.1.17, stuck behind the failure), exit 0. Proof the post-step genuinely executed rather than being skipped.

BUILD-SIDE — SILENT NO-OP: chmod 0755 then tar still records -rw-r--r-- in the archive (perri, measured). The obvious repair looks applied and ships 644 regardless; mode must be forced at tar time.

WHY THIS DECIDES THE QUESTION'S SHAPE: nothing about the runtime, mount, or node resists the exec bit (Athenaeum mount rw,noatime,discard, no noexec) — the brick is ENTIRELY a function of what mode extraction leaves behind. Forcing or validating the declared entry binary's exec bit at extraction time is sufficient to prevent the class, and is not papering over a deeper runtime problem.

FAILURE RECORD (diagnosis-quality argument for the fail-loud shape): 'ADAPTER_UPDATE_POST_FAIL:claude-spt: post-step did not run: failed to spawn session: Permission denied (os error 13)' — reads as a local permissions fault, points away from the upstream packaging cause.

PENDING: lia surveys other Linux subnet nodes for the same bricked state (measured modes, no repairs), and on 0.25.31 publish runs clean spt adapter add --release on Athenaeum from the current 644 state, no chmod anywhere — extracted modes reported as measured = the end-to-end proof.
--- SaberMage:
claude-spt v0.25.31 PUBLISHED with the packaging fix (2026-08-01). adapter.spt sha256 403913cbf29d…35a1a; Linux triples 0755 in the archive forced at tar time, manifest honestly 0644, owner/mtime normalized — verified by perri on the PUBLISHED artifact, not the packer's claim; all three triples rebuilt fresh with the cache deliberately distrusted. doyle's delta gate ran post-hoc (pre-tag gate instruction lost in transit — noted for releases#61's delivery-reliability record): PASS, five commits, single-fix scope held. Remaining proof legs: lia's clean spt adapter add --release on Athenaeum from the 644 state (no chmod), MUSL triple exercised deliberately, extracted modes reported as measured; plus the Linux-subnet bricked-state survey. This issue (#62, core-side extractor question) stays open r
=== #64 ===
TITLE: change: surface an active release-keys.json trust-anchor override in status output
LABELS: state: BACKLOG,type: CHANGE
BODY:
Observer: lia (discovery by comparison — a healthy node has NO identity/release-keys.json; the genuine anchor is compiled in and the file only overrides it). Relayed by perri (claude-spt F-029 @1a03b9a); filed by doyle.

THE INCIDENT SHAPE: a box carrying debug-rollout residue (release-keys.json with an expired debug key + fabricated version floor 9001) silently narrowed its trust anchor — rejected every genuine release as a rollback while reporting healthy version status. Nothing on any CLI surface distinguishes that box from a clean one; the state is discoverable only by an action that fails, or by comparing against a known-good node.

SCOPE RULING (doyle): the public adapter surface does NOT need the debug mechanism documented — DEBUG-ROLLOUT.md owns it and it is maintainer-facing by design. The durable fix for discovery-by-comparison is a STATUS SIGNAL: spt update status (and/or the doctor surface) states loudly when the trust anchor is overridden by identity/release-keys.json, e.g. 'trust anchor OVERRIDDEN (identity/release-keys.json, key <id>, channel <ch>)'. An override in effect must never be silent; cleanup guidance (delete the file to restore the builtin anchor) rides the message.

---
Requester: lia (via perri F-029; filed by doyle)

---
Requester: doyle
COMMENTS(1):
--- SaberMage:
Incident timeline detail (lia, relayed by doyle) sharpening the case: the override was chosen DELIBERATELY (debug bootstrap) — the failure is that it OUTLIVED its purpose invisibly. The debug key expired 2026-06-13 and the box then silently rejected every genuine release for SEVEN WEEKS while spt --version reported a plausible 0.33.0. Nothing on the sick box said its trust anchor had narrowed to a single expired key; the state was findable only by comparing against a healthy node (which has no such file at all). Signal design implication: the status line should surface the override's key EXPIRY alongside its existence — an expired override anchor is a box that can never update again, and that deserves the loudest form of the message.

=== #244 ===
TITLE: Move current_exe_hash() off the daemon ready path — a ~10s synchronous hash per Linux boot sits inside the ONLINE window (ADR-0018 Q7 behaviour change)
LABELS: state: BACKLOG,type: CHANGE
BODY:
The v0.66.0/#242 golden RCA (RCA-242-R2-LINUX.md, BRAIN_PHASE breadcrumbs + harvest-loop repro) measured current_exe_hash() on the READY PATH at ~10.1s per boot on kitsubito under leg load (~1.5s on Windows). Every daemon boot pays it before readiness, inside the same ONLINE budget that bring-up tests and KNOWN-HAZARDS 5.13 guard — under load it manufactures deadline-burn bring-up failures (register IR-17's kitsubito family member is CLOSED under this mechanism at the 2026-08-30 sweep).

Ask: compute the exe hash OFF the ready path (lazy/async/deferred-with-memo — mechanics to the lane), so readiness stops carrying a filesystem-scale hash cost. This is a behaviour change to the ADR-0018 Q7 ratified readiness semantic, so it needs operator triage, not a drive-by: the hash currently has a position in the readiness contract and moving it changes what a ready daemon has proven.

Origin: todlando-flagged during the RCA; figures doyle-measured, RCA-cited. Filed at the v0.66.0 close sweep per the register composition.
Requester: doyle

---
Requester: doyle
COMMENTS(0):


=== #305 ===
TITLE: fix: webserver code seems to live in the broker
LABELS: state: BACKLOG,type: CHANGE
BODY:
any changes to SPT's webserver/file serving code should be usable and evident upon `spt node refresh`. they currently do not follow a refresh, and instead need a full daemon flip to take.

---
Requester: discord:reavo.
COMMENTS(0):



[exited with code 0]
