# W0 (#248) gate checklist — doyle, pre-staged 2026-09-06 17:35Z while todlando is held

Source of truth: `WEBSERVE-272-JIT.md` §W0 (incl. ADR-0057 Am.2 ruling 16:35Z) + ADR-0056/0057/0058
Amendment 1 AS ON MAIN (`a26ca59a` ADR-0058 Am.1, `ef9171a6` ADR-0056 Am.1). Gate reads go to
SOURCE, never the PR body.

## 0. Pre-flight (before any command)
- [x] Operator CPU hold on hfenduleam 18:02Z → RELEASED by the operator ("i'm done") 22:08Z.
- [x] Open GATE-TEST-INDEX.md (memory) — first touch of a gate.
- [x] `git fetch` then assert origin/main == GitHub main (`gh api …/branches/main`).
- [x] `git merge-base --is-ancestor origin/main <tip>` — if false, rebase BEFORE gating.
- [x] List every other open PR with CI running; land the expensive-rerun sibling first.
- [x] IR-76 three-arm golden check; record free GB beside the floor line (`xtask disk-floor`).
- [x] Worktree `.worktrees/gate-w0-<sha>` detached at the tip; own pool; claim FROM inside.

## 1. Mechanical legs (exit FILES read, never the harness notification)
- [x] ONE battery per box: my legs start only after todlando's Windows battery exit files are in
      and no CI job is running on this runner (sub-index: dispatch-window legs name the box).
- [x] Sweep leaked exes before every cargo; `xtask disk-floor` read before firing (snapshot, not
      a promise); build xtask FROM the gate worktree (a prebuilt one bakes its build tree's root).
- [x] `traceable-reqs check` (exit 2 = did not parse — read the code).
- [x] workspace-bins prebuild → `xtask check` (docs drift + spacerun + internal-codes scan).
      Cold pool: prebuild fixture bins as WHOLE packages (`-p mock-adapter --bins`, `-p spt
      --bins` etc.) — `--bins` test harnesses never emit fixture exes.
- [x] Every nextest leg `--no-fail-fast`; read the RUN COUNT against the expected population and
      `grep -c Summary` == 1 before reading any FAIL line; count `panicked at`, not FAIL lines.
- [x] clippy strict; `nextest --no-fail-fast` filtered: spt-store, spt-daemon, spt (serveverb),
      spt-runtime (manifest web_short_path), + `webserve_e2e` + any e2e bin touched.
- [x] Same legs on kitsubito: builder's Linux solo battery 2776/2776 at 0b9554b3 ACCEPTED; product
      delta 0b9554b3→c33dc521 = ONE docs-site/llms.txt line (measured `git diff --stat -- . ':!.github'`),
      02adfbc1→c33dc521 = zero product files; CI Linux unit GREEN at c33dc521 itself (34067180842).
- [ ] Mutation (pre-registered 23:58Z, runs AFTER battery #3 in the same gate pool, never beside it):
      rule 2.5 = ADR-0056:78, arm webserve.rs:298 `bare_node && docs_root.join(first).is_file()`.
      Mutate that predicate to `false`; cell webserve_e2e.rs:253-263 MUST red at :256 (`left: 302,
      right: 200` — the peer named `llms.txt` steals the root leaf) while :259's 502 stays green
      (one arm, siblings intact); revert the one file by `git checkout --`, re-run = green. The cell
      is not a rider in WEBSERVE-272-JIT.md (grep: no match) — it is the ledger's own §1 line.

### Battery #1 at 02adfbc1 (Windows, todlando, 22:09Z): 2803 run / 4 failed, env-leaked launcher.
Pre-registered reads for battery #2 (scrubbed env, .spt/gate-head-b/):
- (1) attach_wedge :315 + (2) daemon_stop_convoy :153 `DAEMON_STOP_REFUSED ($SPT_ENDPOINT_ID set)`
  → pass scrubbed = launcher's, closed; red scrubbed = scrub incomplete (trio is OWL_SESSION_ID /
  SPT_AGENT_ID / SPT_ENDPOINT_ID), not product.
- (3) job_escape :608 `daemon_survived=true reachable=false` (25 s poll) — NOT ledgered. W0's
  reconcile_at sits in the docs-server block AFTER the broker exists (daemon.rs:291; same block
  spawns the attach observer on the broker Arc), so IPC readiness is not on W0's path. Pass
  scrubbed = load/env sighting, one ledger line; red scrubbed = hertz lane, read that rig's daemon
  stderr for SERVE_RECONCILE_FAIL/DOCS_SERVER_* timing before any product claim.
  **hertz 22:47Z:** `reachable` = `spt daemon stop`'s EXIT STATUS (job_escape_e2e.rs:519,
  stderr `Stdio::null`, env inherited) — refusal and unreachable are the same false by
  construction, so (3) is most likely the SAME env leak as (1)/(2). Pass scrubbed = launcher's,
  closed. Test defect regardless: probe must capture stderr into the DIAGNOSTIC line or assert
  the specific exit code — hertz thin lane after W0 lands (not on W0's path).
- (4) resident_service :664 teardown LEAK = FLAKE-LEDGER L42 row (3 golden occurrences, OPEN,
  IR-34 kin, hertz's cluster). Red scrubbed = 4th sighting ON THE ROW; pass = note under env leak.
- Battery #1 ate ~48 GB of target; floor before #2 = 82.8 GiB. My legs need a floor read AFTER #2.
  echo-276 target reaped 22:46Z (+42.7 GiB → 125.9 free).
### Battery #2 = gate-head-d (att3, LAUNCH PROVEN by psutil read-back, mine too). Outcomes so far:
- (1)(2) not seen → launcher's, CLOSED. (3) not seen → same leak, CLOSED (probe defect → hertz later).
- (4) resident_service :664 RED scrubbed → L42 row 4th sighting, hertz records; not W0.
- NEW io_events :87 `explicit daemon start failed: ` EMPTY stderr 0.872 s → UNCLASSIFIED by rig
  construction (exit code discarded, TempDir logs gone); W0 exonerated by signature (a child
  failure prints DAEMON_START_FAIL); hertz instrument lane + ledger row. 1 of 2 at this sha.

### Battery #2 FINAL: 2803 run / 2801 passed / 2 failed (the two above), all other legs 0, mdbook 0.
### PR #195 open 23:11Z at tip 1d8f6f2e (driver hardening only vs 02adfbc1), Fixes #248 only ✓.
### Source reads at 1d8f6f2e DONE 23:23Z — PASS on every line below; ONE finding:
- **Finding #1 (driver, batched):** cold pool → "Cannot run prebuilt xtask" → DISK_FLOOR:REFUSE; fix =
  DISK_FLOOR:BOOTSTRAP direct read at the same floor (approved shape), respin ONCE after this CI.
- Router webserve.rs:262-345 = ADR-0056 Am.1 order · Am.2 allocator serving.rs:240-262 · alias
  refused manifest.rs:1195 (front door, before writes) · WEB_NODE_RESERVED warn+continue
  daemon.rs:298, pairhost.rs:864/891 · unknown-key warn-then-proceed registry.rs:327 · ttl/audience
  Option<> · WEB row access.rs:218 default_on=true attributable=false, XFER row kept :213 · no
  product path deletes a web dir (remove hits are in cfg(test) regions) · trailers 3/3 · wip 0 ·
  api.md untouched · CHANGELOG hits = 4 HTML doc tags · SUMMARY + llms carry serving/overview.md.
- ⚠ My own collision 23:17-23:19Z: a backtick in a double-quoted message body ran a 2-min cold
  compile beside CI's Windows unit job. Named candidate for any red in that window.

### Respin c33dc521 (todlando, pushed 23:32Z) — gate finding #1 closed; battery #3 = gate-doyle2 (mine)
- Chain `eb38b71a → 2c5d3187 mint → 8d7b8289 driver → c33dc521`, merge-base == main (ff-landable);
  diff vs 1d8f6f2e = `.github/ci/ws272-w0.py` ONLY (+35/−9), product tree byte-identical, so every
  §2 verdict at 1d8f6f2e carries to c33dc521 unchanged; trailers 3/3 (raw body read).
- Respin read: cold pool → `bootstrap_floor_reading` takes ONE direct `shutil.disk_usage` reading
  against the SAME `FLOOR_BYTES = 32 << 30` the meter enforces, names the absent xtask in the raw,
  and `floor_verdict` renders it through the same parser (PASS/REFUSE, never a third outcome);
  warm pool path unchanged. Self-test grew a cold-pool cell. `python ws272-w0.py --self-test` in my
  worktree: 9/9 PASS incl. `cold-pool bootstrap … rendered PASS` (exit 0, 23:49Z). ⚠ My battery
  runs on a WARM pool (xtask present), so the cold arm's only executions are the self-test + CI.
- CI 34067180842 at c33dc521: GREEN 5/5 (Windows unit 23:47:18Z on this box, Linux 23:40Z, lint 23:42Z).
- Battery #3 launched 23:48Z AFTER the Windows unit job concluded, `procs-before: 0`, LAUNCH PROVEN
  (read-back NONE; dropped SPT_ENDPOINT_ID SPT_SESSION_NAME SPT_INJECT_VERIFY_ECHO). Pre-flight:
  treqs 0 · GOLDEN_PREFLIGHT:CLEAR · DISK_FLOOR:PASS 90.43 GiB / floor 32 · claim 0 · prebuild 0.
  hertz's broker.rs instrument compile finished BEFORE launch (his 23:31Z note); broker bounce for
  that instrument HELD by hertz until I say "gate done" (a daemon restart mid-battery forges reds).

## 2. Source reads (one per REQ; note file:line in the verdict)
Verdicts read at 1d8f6f2e (23:11-23:29Z) and carried to c33dc521 (product tree byte-identical, §1):
router order webserve.rs:262-345 (reserved facets :290 before registry, other-node 502, docs surface
untouched :282) · facet 404 bodies `FACET_NOT_FOUND: <f>` :342 / `FACET_UNAVAILABLE: <m|bin|install>` :341
vs registry miss `NOT_FOUND: served resource <name>` :190 · allocator + Am.2 history serving.rs:240-262 ·
ttl_ms/audience `Option` serving.rs:40-43, no reaper · reserved alias refused at activation manifest.rs:1195 ·
WEB_NODE_RESERVED warn+continue · unknown key registry.rs:327-328 walk manifest.rs:1516 (unit :1647 pins
`[adapter].web_short_pth`, known-key-silent :1649) · WEB row access.rs:218 default_on=true attributable=false,
XFER :213 kept · STORAGE.md:118-152 carries the name-history shape · ADR-0057 Amendment 2 at :78 · CHANGELOG
[Unreleased] 3 tagged user-facing entries, no internal codes · trailers 3/3 raw · PR body Fixes #248 only.
- [x] REQ-WEB-URL-NODE-PREFIX: bare `/` → 302 `/<node>/`; reserved `f/ docs/ a/ m/ bin/ install`
      404-with-facet-name BEFORE any registry lookup; `/<other-node>/…` → 502-with-node; docs
      bare paths byte-true (REQ-DOCS-LOCAL-SERVER surface untouched); `?json` index twin.
- [x] REQ-WEB-SERVING-REGISTRY: `$SPT_HOME/serve/registry`, spt-store owns format, daemon single
      writer (CLI goes through the control client, no direct writer); entry fields per JIT;
      `<stem>~<n>.<ext>` per `(stem, ext)`, counter persisted; **Am.2: history = name → (last
      abs path, kind); same path re-takes its name; different path takes next suffix**;
      `--as` under the same rule; reference-served resolves at request time (deleted → 404).
- [x] ADR-0057 Am.1: served root `$SPT_HOME/adapters/<adapter>/web/`, created empty at
      activation, entry removed at deactivation, DIR + bytes retained; alias = same entry;
      reserved alias refused at ACTIVATION (not just HTTP).
- [x] ADR-0058 Am.1: `ttl_ms` + `audience` optional on every kind; no reaping/enforcement in W0.
- [x] REQ-WEB-ACCESS-SURFACE: `WEB` row `default_on: true`, non-attributable; `--help` Control
      surfaces section grows by table composition; nothing dials it in W0; XFER row still present.
- [x] Startup warning for reserved OS hostnames: warns, never refuses boot/join.
- [x] REQ-MANIFEST-UNKNOWN-KEY-WARN (rider 18:15Z): unknown `[adapter]` key → ONE stderr line
      naming table+key, registration PROCEEDS (never refuse; no `deny_unknown_fields` added
      anywhere); unit cell for the typo case + the known-key-silent case; manifest.md paragraph.
- [x] Docs: `docs-site/src/serving/overview.md` + SUMMARY + llms.txt; `reference.md` regen;
      `api.md` untouched; MANIFEST.md + manifest.md carry `web_short_path` under `[adapter]`;
      STORAGE.md carries the registry schema incl. Am.2 history shape; ADR-0057 carries Am.2.
- [x] CHANGELOG `[Unreleased]` entry, user-facing wording, no internal codes.
- [x] Commit trailer `Co-authored by: todlando` (grep, never `%(trailers:)`); wip checkpoint
      squashed; PR body `Fixes BigscreenVR/spt-bs-releases#248` ONLY (#249/#272 stay open).

## 3. Field acceptance (mine, real daemon, browser)
- [ ] `http://localhost:5474/hfenduleam/` renders the index; lists a file I `serve add`.
- [ ] Two `report.md` from two dirs → `report.md` + `report~1.md`.
- [ ] `serve rm report.md`, re-add the SAME file → `report.md` again (Am.2).
- [ ] Re-add a DIFFERENT third `report.md` → `report~2.md`.
- [ ] Adapter deactivate → activate: alias unchanged (no `~1`), bytes retained.
- [ ] `curl -I http://localhost:5474/` → 302; `/hfenduleam/docs/` byte-equal to `/docs/` page.
- [ ] `curl http://localhost:5474/hfenduleam/f/` → 404 naming the facet, not a registry miss.

## 4. Land
- [ ] CI green at the tested sha; re-run the ancestor check at land time; ff-only; tested == merged.
- [ ] Pick-audit; alchemy sweep → #248 ACCEPTANCE; W1 dispatch (`#249`) via alchemy `dispatch`.
- [ ] Pool: todlando releases; hertz W3 branch rebases when W3 opens (not now).
