# W18 — Close out Phase 4: inline action results, and the open decisions

**Where this starts.** Phase 4's roadmap scope is delivered (W14 Watches, W15
Milestones, W16 Tags + Liaison) and W17 made alchemy releasable. So the four-phase
build path in `ROADMAP.md` is complete. W18 is not new capability — it is closing the
items the build deliberately left open, and saying plainly which ones are not mine to
close.

## Scope

1. **Resolve the last roadmap known unknown: `spt shell cmd --json` on action verbs**
   — "to return a new issue number inline instead of via a later `spt send`." Today a
   `create` answers asynchronously: the drive returns `SHELL_SPOOLED`, the shell does
   the work, and the issue number arrives later as a message. A submitter-facing
   Liaison therefore cannot answer "filed as #N" in the same breath as filing.

   Resolve it **empirically first**, the way the cross-node drive unknown was resolved
   (that one came back NO and produced a filing rather than a workaround):
   - Does `spt shell cmd --json` carry a *result* back from a `relay`-receipt shell at
     all, or is `--json` only the machine-readable rendering of the *dispatch*
     acknowledgement? The CLI reference says `--json` is "honored by the read/status
     commands … action commands ignore it", which suggests the answer is no.
   - If no: file it with doyle with the concrete need (a Liaison answering its
     submitter in one turn), and record the resolution in `ROADMAP.md` beside the
     other two so it stops reading as an open question.
   - If yes: wire it through `create`/`comment`/`state` and give the Liaison playbook
     the one-turn answer pattern.

   **Do not build a workaround before the probe.** A synthetic
   correlate-the-later-message mechanism is exactly the kind of thing that becomes
   permanent; the honest outcomes are "spt-core supports it" or "filed and documented".

2. **State Phase 4 complete in `ROADMAP.md`** — mark the phase and its delivered
   bullets, and fold W17 (distribution) in, since it was unplanned scope that shipped
   and is currently only recorded in `W17-PLAN.md`.

3. **Sweep the doc surface for statements the last two waves falsified.** `CONTEXT.md`
   and the phase plans still describe the scanner's cursor as riding entry `seq`
   (it anchors on the turn's `input_seq` now) and may still imply the tag-idle
   workaround. `docs/DOCS-STRATEGY.md` says doc generation is CI-gated against drift,
   so this is a real gate, not tidying.

4. **Implement activity-gated polling** — Request
   [#19](https://github.com/BigscreenVR/spt-progress-tool/issues/19), decided in
   ADR-0011 (which supersedes ADR-0010). Gate the scanner's tick on the owner's
   `activity` frame: idle scans once then quiesces, busy resumes. The two constraints
   are the work: the idle-edge scan MUST precede quiescing (or the mint-then-idle
   deadlock returns), and a lost busy-frame MUST degrade to latency, not silence —
   so a heartbeat floor or resume-on-any-inbound is part of the feature, not a
   follow-up. Decide the parked-confirm expiry question on the record while building.

## Needs the Operator, not me

- **Release signing** (`W17-PLAN.md` open question). Alchemy publishes unsigned, so
  anyone who can publish a release on the repo can push code to every updating node.
  That is acceptable while the repo is private and Operator-controlled and not
  beyond it. The blocker is **where the private key lives**, which is a custody
  decision. I can prepare the options and the runbook text; I should not pick.

## Blocked elsewhere — do not start, watch for the ping

- **Cross-node shell drive** (spt-bs-core#70 leg-b) and, riding its fix-verify, the
  **spawn interim-undo** I owe doyle: restore `[update]` to the local registration,
  reinstall the shipped release, prove install-and-spawn in one step. doyle pings when
  the wave lands; that restoration *is* the fix's field-acceptance test.

## Gate

`cargo test` green · `traceable-reqs check` exit 0 · every roadmap known unknown reads
as resolved or filed, never as an open question · no doc still describing the retired
tag-idle workaround or an entry-`seq` cursor.

## Outcome (2026-07-25)

All four scope items closed; gate met (190 tests green, `traceable-reqs check` exit 0).

1. **The `--json` unknown came back NO, empirically.** `spt shell cmd --json` and
   `spt shell send --json` both answer only `SHELL_SPOOLED:… (relay drain: api poll
   --link)` — not JSON, and carrying no shell result; the shell's reply arrived
   afterwards as an ordinary message. No workaround built. Filed with doyle, who banked
   it as `SHELL-CMD-RESULT-CHANNEL` with a design lean toward the correlation-id option
   (additive, transport untouched) over a synchronous drive, and folded the side finding
   — `--json` must precede the shell ref or the vocabulary check refuses it as a
   positional — into the already-triaged #61-63 shell-CLI-truth wave. Recorded in
   `ROADMAP.md` beside the cross-node resolution; the Liaison's two-message pattern
   stays the contract.
2. **Phase 4 and the whole build path read as delivered** in `ROADMAP.md`, with W17
   distribution folded in as the unplanned scope it was.
3. **Doc sweep done.** `CONTEXT.md`'s replay-safety paragraph described a per-entry
   `seq` cursor and "an open turn's entries carry no `seq` yet" — both falsified by
   `REQ-HAZARD-TAG-SEQ-DRIFT`; rewritten around the turn's `input_seq`, with the phase
   plan annotated as-built. Nothing anywhere still describes the tag-idle workaround.
4. **Activity-gated polling built** (`src/scan_gate.rs`), with both ADR constraints as
   the load-bearing parts: the idle-edge scan precedes the quiesce, and three
   independent floors keep a lost frame costing latency rather than silence
   (`REQ-HAZARD-SCAN-GATE-DEAF`). The parked-confirm question was decided by removing
   the coupling rather than accepting it — expiry never needed a digest, so it left the
   gated path entirely and got *prompter*. Edges derive from `since`, not the state
   word, because the drain is latest-wins.

**What is proven and what is not.** The gate is unit-proven, not field-proven: the
running `alchemy-1` is the *released* 0.2.0 binary (via the interim absolute-path
registration), so this code is not deployed anywhere yet. One contract assumption also
rides on the docs rather than on a probe — that spt-core pushes `activity` to a shell
whose `command_receipt` is `relay` and which declares no drive channel. The docs say
receiving it is undeclared and automatic, and the fail-open design bounds the risk to
"behaves exactly as it did before" if that is wrong — but it is an assumption, and W19
is where it gets tested for real.
