# W17 — Distribution: adapter self-update + the first real release

**Scope.** Alchemy has never shipped. It is registered on this node in dev
copy-mode from a stable directory, which means there is no way for any other node
to get it and no way for this one to move forward except by hand. W17 closes that:
declare the `[update]` avenue in the manifest, then publish an actual GitHub
release whose asset *is* the installable adapter.

**Not in scope.** Signing (`signing_key`) — see the open question below. CI
automation of the release; W17 publishes by hand from the runbook so the steps are
proven before they are scripted. Linux binaries.

## Design decisions

1. **Avenue = `gh_release`.** Alchemy already lives in a GitHub repo, releases are
   how everything else in this ecosystem ships, and the fetch rides the node's
   authenticated `gh` CLI — which spt-core's own bootstrap provisions, so there is
   no new credential to manage and a private repo works unchanged.
2. **`repo = "BigscreenVR/spt-progress-tool"`** — alchemy's own code home, not a
   separate distribution repo. Same repo the Requests Hub uses; one place.
3. **Multi-platform layout even for one platform.** The binary ships under
   `x86_64-pc-windows-msvc/` rather than flat at the archive root, so a Linux node
   is refused with spt-core's clear `NoArtifactForPlatform` instead of installing a
   Windows exe it cannot run. Costs nothing now, and adding a second triple later
   is a directory rather than a re-layout.
4. **Bare-name spawn stays bare.** `spawn = "alchemy …"` is left alone: a command
   template's program token resolves against the adapter's install dir before PATH,
   so the shipped binary is found without PATH placement. This is also how the
   release deploys code — a spawned shell runs the *installed* binary, so shipping
   is the deploy path.
5. **Version = 0.2.0**, tag `v0.2.0`. 0.1.0 was never published, and the manifest
   version and the release tag move together.

## Open question

- **Signing.** `gh_release` treats an unsigned asset as trusted on HTTPS + GitHub
  (the same trust as first acquisition); a declared `signing_key` makes updates
  fail-closed against a detached `.sig` sibling asset. Deliberately unsigned for
  0.2.0: there is no key ceremony for this project yet, and a key that lives
  nowhere in particular is worse than no key. Revisit before the adapter is
  installed anywhere the Operator does not control — the answer needs a decision
  about where the private key lives, not just a manifest line.

## Tasks

1. `REQ-ADAPTER-UPDATE` in the registry (`doc` + `impl`), and add `manifest.toml`
   to `[scan].roots` — impl evidence for this requirement lives in the manifest,
   and an unscanned root makes evidence silently vanish.
2. `[update]` section in `manifest.toml` + version bump.
3. Fill `docs/RELEASE-RUNBOOK.md`: it is still the unfilled template, with a
   placeholder requirement tag. Real packing command, real publish steps, real
   trust model.
4. `CHANGELOG.md` — does not exist; the runbook makes the tagged version's section
   the release body, so the release cannot ship without it.
5. Build, pack `adapter.spt`, push, tag, publish the release with checksums.
6. Prove the loop actually closes: `spt adapter update alchemy` moves this node to
   the published 0.2.0 and re-registers it as a pointer install.

## Gate

`cargo test` green · `traceable-reqs check` exit 0 · the release exists with its
asset · `spt adapter list` shows alchemy at the published version.

## Outcome (2026-07-25)

Shipped. `v0.2.0` is published on `BigscreenVR/spt-progress-tool` with `adapter.spt`
+ `SHA256SUMS`; `spt adapter add --release` installed it on this node (triple
extracted correctly), `spt adapter version alchemy` reports 0.2.0, and
`spt adapter update alchemy` reports up-to-date — so the update path is live.

**Task 6 (prove the loop closes) came back half-green, and that is the finding.**
Acquisition and update work; *launching* an instance from a release install does
not. spt-core fills neither install-dir program-token resolution nor `{adapter_dir}`
for `[shell].spawn`, so a shell adapter cannot ship its own binary — both probes
refused (cleanly, with rollback) and there is no portable third option. Filed with
spt-core; the shipped manifest keeps the bare token because that is the form which
becomes correct when the fix lands. Interim on this node: a local manifest-only
registration whose `spawn` names the *released* binary by absolute path, with
`[update]` removed so an update cannot re-register the unspawnable manifest. Net
effect is still a deploy — shells and the Hub Daemon now run released code, which is
how the tag-scanner fix reached the node.

The signing open question stands unanswered by design; nothing in this wave changed
what it depends on.
