# MILESTONE TRIAGE — MUSL-TIER

**Operator GO 2026-07-08** ("first-class self-updating musl tier, including centralization of the target triple"). doyle triage; todlando builds; doyle gates; deployah publishes. **QUEUED** — REQs minted inactive (`required_stages = []`); activate per-wave at work-start ([[traceable-per-wave-activation]]) when the operator sets it running.

## Why
Deploy targets on Linux with **GLIBC < 2.39** can't run the `x86_64-unknown-linux-gnu` artifact (kitsubito builds against 2.39 → symbol-version fail). A **static `x86_64-unknown-linux-musl`** binary has zero glibc dependency and runs on any Linux kernel.

## Proven feasibility (doyle /diagnose 2026-07-08 — findings-backlog seed #14)
- **Builds + runs static musl with ZERO source changes** — `cargo build --release --target x86_64-unknown-linux-musl -p spt` EXIT 0 on kitsubito (`musl-tools` + `cmake` + `rustup target add` only); `file` = `static-pie linked`, `--version` runs. `aws-lc-sys` + bundled `rusqlite` compiled clean under `musl-gcc`; TLS = rustls (no OpenSSL). PTY = openpty (musl-provided).
- **DNS/HTTPS works statically** — a real `spt update fetch` on the static binary resolved DNS, reached GitHub, and downloaded the update-set + artifacts. The NSS/`getaddrinfo` static-musl fear is **refuted** (only NSS plugins break, not plain DNS). No reqwest/resolver change needed.
- **The ONLY gap is the release model** — that fetch ended `UPDATE_FETCH_REJECTED:NoArtifactForPlatform("unknown")`: `current_platform()` returns `"unknown"` for `target_env="musl"`, and no musl artifact is published. That is exactly what this milestone fixes.

## Site census (the target-triple surface)
`current_platform()` (spt-daemon `release.rs:196-211`) is already the canonical accessor — most callers route through it. Hardcoded/assumption sites:
- `release.rs:196-211` `current_platform()` — cfg arms for msvc + gnu only; musl → `"unknown"` fallback.
- `release.rs:190-191` `KNOWN_TARGET_TRIPLES` — the 2-triple allow-list (adapter multi-platform archive detection).
- `applyhost.rs:740-743` — the cross-platform "other" binary logic **assumes exactly 2 platforms** (`if me==windows { linux } else { windows }`) — the N-platform blocker.
- `xtask/src/main.rs:598-599` — asset-name ↔ triple map (`spt-x86_64-linux` → gnu).
- `.github/workflows/release.yml` — build matrix (2 entries) + assemble job hardcodes `spt-x86_64-linux spt-x86_64-windows.exe` in SHA256SUMS (line ~81) and the release upload (lines ~120-121).
- `propagate.rs` (412/419/492), `applyhost.rs` (184/216/597/633/655) — already route through `current_platform()`; correct once the registry gains musl. Tests: `release.rs`, `update.rs`, `net/update.rs`, `cli.rs:16922` literals.

## Waves

### W1 — Target-triple CENTRALIZATION (refactor, behaviour-NEUTRAL) — `REQ-PLATFORM-REGISTRY`
One authoritative platform registry; `current_platform()`, `KNOWN_TARGET_TRIPLES`, the applyhost "other" logic, and the asset↔triple map all derive from it. gnu + windows behaviour **byte-identical** — the existing release/update/apply/propagate test suite stays green (that IS the gate).
- **DESIGN FORK (doyle to rule before dispatch):** (a) registry shape — `enum SupportedPlatform { WindowsMsvc, LinuxGnu, LinuxMusl }` with triple/asset/cfg methods, vs a `SUPPORTED_PLATFORMS: &[PlatformSpec]` const table. Lean: const table of `{ triple, asset_name }` — data-driven, one row per platform, no match-arm sprawl. (b) `applyhost` "other platform" — generalize the binary if/else to "every registered platform except `current_platform()`" (a filter over the registry). (c) `current_platform()` stays a cfg→triple map (build-time truth) but its output must be a registry member; keep the `"unknown"` fallback loud.
- **Gate:** clippy --workspace, full nextest both legs (kitsubito Linux), traceable; diff review = no behaviour change for gnu/windows (release/update/apply/propagate suites unchanged & green). No new platform yet.

### W2 — Register the musl platform (self-IDENTIFY) — `REQ-PLATFORM-MUSL`
Add `x86_64-unknown-linux-musl` to the W1 registry: a `current_platform()` cfg arm (`target_arch=x86_64, target_os=linux, target_env=musl` → the musl triple, **no more `"unknown"`**), asset name `spt-x86_64-linux-musl`, triple map entry. DNS/crypto/build already proven — no dependency changes.
- **Gate:** a musl-built `spt` self-reports `x86_64-unknown-linux-musl` (not `"unknown"`); `KNOWN_TARGET_TRIPLES`/registry contains it; unit over the new cfg arm (or a registry-membership unit, since the cfg arm can't be unit-tested cross-target). Build the musl target in the gate (toolchain on kitsubito) and assert `current_platform`.

### W3 — CI build + signed release + update-set publish + self-update E2E — `REQ-RELEASE-MUSL-ARTIFACT`
release.yml gains a musl matrix entry (build on kitsubito; install `musl-tools`+`cmake`+target in-job, `CC_x86_64_unknown_linux_musl=musl-gcc`); the assemble job includes `spt-x86_64-linux-musl` in SHA256SUMS + the release upload; `release-publish` (xtask) signs the musl artifact; the update-set carries its artifact entry.
- **Gate (release-pipeline touch → real E2E):** cut a draft/test release with the musl artifact; a **static musl binary on a sub-2.39-glibc box** runs `spt update fetch` → gets the musl artifact (no `NoArtifactForPlatform`), applies, and self-updates. Verify SHA256 + signature over the musl bytes. Bump/CHANGELOG ride the release PR ([[release-standard-bump-in-pr]]); deployah publishes.

## Notes / rulings pre-banked
- **No source-dependency changes** — feasibility proved the current tree links static musl as-is. Do NOT swap the crypto provider (aws-lc-sys builds clean on musl here); keep the tree identical.
- **musl is ADDITIVE** — gnu stays the default Linux artifact (glibc boxes unaffected). musl is a third platform, not a replacement.
- **Roll-forward for old-glibc boxes:** once W3 ships, those nodes install the musl artifact and self-update on the musl channel like any platform.
