WEBSERVE W0 — the serving substrate: node-prefixed URLs, the serving registry, the WEB surface

Fixes BigscreenVR/spt-bs-releases#248

Delivers #248 and the LOCAL half of #249 for milestone #272 (v0.68.0). #249 and #272 stay open: #249's cross-node half is W1, #272 closes with the milestone.

## What lands

- **ADR-0056 node-prefixed URLs** — the loopback server answers `/<node>/…` for every facet; bare `/` redirects (302) to `/<node>/`; `f/`, `docs/`, `a/`, `m/`, `bin/` + `install` are reserved at the router (404 by facet name, never a registry lookup); `/<node>/` renders the index page with a `?json` twin; a known `/<other-node>/…` answers 502 until W1. The docs server's bare paths remain a byte-true compatibility alias.
- **ADR-0057 serving registry** — `$SPT_HOME/serve/registry` (spt-store format + read path, daemon single writer). Served-name rule clause by clause; **Amendment 2** (ruled from the W0 smoke): history records name → last (path, kind), so a freed name is re-taken only by the path it last named, and adapter aliases survive deactivate/activate and `adapter update`. Verbs `spt serve add|rm|list`. Manifest `[adapter].web_short_path` registers a core-owned `adapters/<adapter>/web/` served root at activation.
- **ADR-0060 WEB surface** — the `WEB` row joins the surface vocabulary (default-on within the subnet, rule-addressable, non-attributable); nothing dials it in W0.
- **W0 rider — unknown manifest keys are named** — manifest structs keep no `deny_unknown_fields` by design, so a misspelled optional key was visible only by absence. `Manifest::unknown_keys` walks the parsed TOML against the schemars schema; `adapter add` prints `manifest: unknown key [<table>].<key> (ignored)` per key and proceeds.
- **Gate driver** `.github/ci/ws272-w0.py` — IR-76 three-arm pre-flight, disk-floor instants with free space logged (IR-59 rider), exit files per leg, and a worktree-level lane lock (same-lane pool serialization is the invariant).
- Docs: `docs-site/src/serving/overview.md` (new, in SUMMARY and `llms.txt`), manifest reference (+ Unknown keys), `reference.md` regen, STORAGE.md, MANIFEST.md, ADR-0057 Amendment 2 text, CONTEXT.md served-name entry re-stated to Amendment 2 (gater-approved: a ruled amendment falsified the sentence, corrected by replacement), CHANGELOG.

Requirements activated: REQ-WEB-URL-NODE-PREFIX, REQ-WEB-SERVING-REGISTRY, REQ-WEB-ACCESS-SURFACE, REQ-MANIFEST-UNKNOWN-KEY-WARN (`traceable-reqs check` exit 0 on both hosts).

## Commits

- `2c5d3187` chore(treqs): mint the WEBSERVE W0 requirements
- `9b3caa92` ci: W0 gate driver with the IR-76 three-arm pre-flight and a lane lock
- `02adfbc1` feat(webserve): W0 serving substrate — node-prefixed URLs, serving registry, WEB surface

Product tree at `02adfbc1` differs from the Linux-tested `0b9554b3` by the driver file and one `llms.txt` line only (no Rust, no docs-site page bodies).

## Evidence map

**Linux — kitsubito `~/spt-w1/.worktrees/ws272-w0`, SOLO battery at `0b9554b3`, exit files under `.spt/gate-head2/`** (chain log: `procs-before: 0`, lane lock held):

| leg | exit |
|---|---|
| treqs | 0 |
| pool claim | 0 |
| prebuild (workspace bins) | 0 |
| xtask check (docs drift, spacerun, internal-codes scan) | 0 |
| clippy (`--all-targets -D warnings`) | 0 |
| nextest (`--no-fail-fast`, spt-store / spt-runtime / spt-daemon / spt) | 0 — `Summary [1053.275s] 2776 tests run: 2776 passed (1 slow, 2 leaky), 0 skipped` |
| mdbook build | 0 |
| driver | 0 |
| Amendment 2 field smoke (`.spt/gate/w0-smoke-am2.py`, fresh home, own port) | 0 — 20 CLI ops, 20 HTTP probes, 0 failures (`.spt/gate-head2/smoke.raw`; exit file `.spt/gate-am2/smoke.exit`) |

At `20427963` (`.spt/gate-head3/`, product tree identical to `02adfbc1` minus the llms.txt line): driver `--self-test` 0 (lane lock refuses a second driver naming the holder, frees on exit), treqs 0, `--probe-lock` 0. The pre-rider tree (`2b209369`, `.spt/gate-am2/`) was also fully green there: nextest 2771/2771, mdbook 0.

**Windows — hfenduleam `.worktrees/ws272-w0`** (`.spt/gate-am2/`, pre-rider tree): treqs 0, claim 0, prebuild 0, xtask check 0, clippy 0; nextest stopped for the operator's box hold. Full battery at `02adfbc1` into `.spt/gate-head/` runs on release, before this PR opens: _(to be filled: exit files + Summary)_.

**Rig-defect note (voided window, kept as proof):** kitsubito `.spt/gate-head/` + `.spt/gate-head-driver.log` hold an interleaved run — a stopped local ssh left its remote chain alive and a second driver ran beside the intended one (one `Starting 2776 tests`, two `Summary` lines). The gater voided that window in full, including its isolated reruns and smokes; nothing from it is cited above. The lane lock in `9b3caa92` is the guard.

## Gate notes

- Every commit carries `Co-authored by: todlando` (grepped on the raw body).
- CHANGELOG `[Unreleased]` entry present; visible text carries no internal codes (measured with the scan's token classes; the REQ tags are HTML comments, the file's existing convention).
- Field acceptance per `GATE-W0-272-CHECKLIST.md` is the gater's.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_015SQWGdNjq4sUbTzPQMLEQA
