---
name: v032-platform-safe-update
description: "v0.3.2 REQ-UPD-8 platform-safe `spt update fetch` (update-set) + apply platform-guard + product_version message; the real cross-platform verify is POST-publish"
metadata: 
  node_type: memory
  type: project
  originSessionId: 15c8a092-3f9c-407b-b294-fb2581f00ac5
---

v0.3.2 fixes the v0.3.1 cross-OS brick (Linux ELF applied as `spt.exe`). Minted **REQ-UPD-8**, implemented + all gates green, on **main** head `cabdc61` (commits `a65b508` fix + `cabdc61` release-prep), workspace bumped 0.3.1→0.3.2. **CI GREEN both runners** (kitsubito Linux + hfenduleam Windows) + traceability (one socket-bind flake on first kitsubito run, clean on rerun). **todlando pinged 2026-06-09 to sign** (release ceremony = tag v0.3.2 → `release-publish --version 6`; counter v0.3.1=5 → v0.3.2=6). Signing delegated to todlando; NOT me.

**What shipped:**
- `spt update fetch` now downloads the signed multi-platform `update-set.json` + every platform artifact it names, verifies each, and stages the SET (never a platform-blind single). No single-fetch fallback — a missing `update-set.json` points at the installer.
- `apply_staged` Single path REFUSES unless the cache carries a `platform.json` stamp == `current_platform()`. Unstamped (every P2P-pulled single, all pre-v0.3.2 stages) or wrong-OS → fail-safe refuse. This guard alone stops the brick. `propagate.rs` left UNCHANGED — the apply guard is the backstop.
- `relcache`: `stage_single_with_platform()` writes the stamp; bare `stage()`/`stage_update_set()` clear it.
- additive `product_version: String` (`#[serde(default)]`) on ReleaseMetadata + UpdateSetMetadata → friendly apply message `Updated spt-core to vX.Y.Z.` + changelog URL (counter fallback when empty). Stamped at publish from the tag.
- `xtask release-publish` now builds+signs+verifies+uploads `update-set.json` over both platforms.

**Plan-premise CORRECTION (the commune/V032-PLAN said "release-publish already builds a SignedUpdateSet at main.rs:799 — just not uploaded"). FALSE.** main.rs:799 was inside `debug_rollout` (debug channel). Production `release_publish` only signed per-platform `SignedRelease`s; it had NO update-set at all. v0.3.2 ADDED set construction+upload to publish (`build_signed_update_set`).

**PUBLISHED + RIG-VERIFIED 2026-06-09.** todlando signed v0.3.2 (counter 6, key rel-primary-2026); live at github.com/SaberMage/spt-releases/releases/tag/v0.3.2 with `update-set.json` (both platforms). Cross-OS rig verify PASSED end-to-end: kitsubito (Linux, 0.3.2) served the v6 SET; hfenduleam (Windows) — which still held the v5 **Linux-ELF single landmine** (the literal brick artifact, sha 61b5aff2…, magic 7f454c46) — pulled the SET over P2P, selected ONLY its Windows artifact (magic 4d5a/MZ, sha 0c96b99b… = published PE), applied → `spt 0.3.2`. No brick. **Fleet: kitsubito=0.3.2, hfenduleam=0.3.2; ENLYZEAM (3rd SPT_DEV node) still unknown — re-roll same way.** Hard rule LIFTED.

**Transitional cache-clear gotcha (hit live on both nodes):** `cmd_update_fetch`'s rollback floor = `staged_version` on the pinned channel. A node that staged the v6 SINGLE (via 0.3.1's old fetch) can't re-fetch the v6 SET — `Rollback{6,6}` (same version, only the shape differs single→set). Also the P2P pump's offer floor = staged_version. FIX both: stop daemon → `rm -rf $SPT_HOME/releases` (pure cache; installed binary untouched) → restart → fetch/pull stages the set clean. Linux $SPT_HOME default = `~/.spt-core` (NOT XDG `~/.local/share/spt`); Windows default = `%LOCALAPPDATA%\spt-core`. NOT a shipping bug (only bites the one migration hop; future releases are vN+1). Friendly apply message is 0.3.2-CLI behavior — a 0.3.0→0.3.2 apply prints the old `APPLIED:6` (the applying binary is still 0.3.0); unit-tested, not observable on the upgrade hop.

**Apply ≠ running new code (by design, applyhost docs):** `spt update apply` swaps the on-disk binary + does the brain-only handoff, but the resident DAEMON PROCESS keeps running old logic until its next restart (`ensure_running` spawns the now-new binary at next restart/logon — routine lifecycle, never an endpoint cycle). So after apply: binary on disk = new, `spt --version` (a fresh exec) = new, but the long-lived daemon pid stays old-in-memory until bounced. Manual/at-logon-managed daemons need an EXPLICIT restart to run the new version (hfenduleam: hand-restarted → new pid 18112; enlyzeam: pid 68580 stayed 0.3.0-in-memory post-apply until bounce). Deferring to next logon is SAFE — the binary is correct; only the running process lags. Bounce sooner only to get 0.3.2 runtime behavior (boot-race self-heal etc.) immediately.

All-3-nodes verified 2026-06-09: enlyzeam (Windows, gerald ready_agent) AUTO-pulled the v6 set over P2P with zero intervention (no cache-clear needed — its floor<6) → staged Windows PE only → applied → 0.3.2.

Related: [[owl-send-not-legacy-spt-send]] (use `$OWL send todlando`, NOT `spt send` → NO_PERCH; `${PIPESTATUS[0]}` not `$?` through a pipe), [[daemon-service-detection-gotcha]] (push→CI BEFORE tagging).
