# NOW-SIGNAL #23 — W1 lane JIT (todlando, 2026-08-30)

Lane worktree `.worktrees/ns23-w1`, branch `feat/ns23-w1-updates-sealbrief`, base `aa9ac15a`.
Pool claimed `ns23-w1` from this worktree. **Nothing committed yet.**

## Dispatch (doyle, W1)

1. **#245 UPDATES** category — update events + version for spt-core, the endpoint's harness
   adapter, and the adapters of registered shells.
2. **#18 SEAL_BRIEF** category — ≤2 short sentences (operator constraint VERBATIM, not ours to
   relax), once per session.
3. **#11b render rider** — the EDGE_TRANSITIONS subnet-join line.

## DONE so far (builds clean: `cargo build -p spt` = 0)

- `Category::Updates` + `Category::SealBrief` on the enum, in `V1` (appended at the END —
  render order is declaration order, so the ratified v1 order is untouched), tags `UPDATES` /
  `SEAL_BRIEF`, dispatch arms in `cmd_now_signal`.
- `gather_updates` — three subjects; **the seen-set key carries the version**, which is what
  makes the delta discipline the event detector and is why no event journal is built. Shell
  adapters keyed PER SHELL. Absence is silence.
- `gather_seal_brief` + `SEAL_BRIEF_TEXT` constant.
- `REQ-NOW-SIGNAL-UPDATES` and `REQ-NOW-SIGNAL-SEAL-BRIEF` minted and activated
  (`doc, impl, unit`). `traceable-reqs check` = 1 with exactly the two expected misses:
  **doc and unit for both**. impl is covered.

⚠ **Caught mid-build, worth not re-learning:** the first `SEAL_BRIEF_TEXT` cited
`spt seal verify`, which **does not exist**. The real surface is `spt api seal verify <token>`
with the content on **stdin** (`api/mod.rs:599`, `SealCmd::Verify`); `spt seal` is the MINT verb
(TOTP ceremony). A payload whose whole job is telling an agent how to act is the worst place for
an invented command. Corrected, and the REQ title carries the correction so it cannot silently
regress.

## REMAINING, in order

1. ✅ **#11b rider — DONE.** The render arm is now `subnet_join_line(label, pubkey, subnet)`,
   an extracted pure fn (so it is testable without a roster on disk) rendering
   `node <label> joined subnet <x>`, with the unlabeled fallback as
   `node pubkey <12hex>… joined subnet <x>`. Nothing invented; the from-node half is the
   board's. Minted `REQ-NOW-SIGNAL-EDGE-SUBJECT-NAMING` (`impl`, `unit` — **no doc stage**:
   a render precision fix inside a category the docs already list is not a new surface) and
   the REQ title carries the node-level-only measurement so the next reader of #11b does not
   re-derive it.
2. ✅ **doc stage — DONE, but NOT where the JIT said.** The category vocabulary lives in
   `docs-site/src/shells/frames.md` (§ "The v1 categories"), not in
   `docs-site/src/harness-contract/api.md` — api.md carries the VERB and links out to it.
   Added a "Categories added after v1" section there with both `[doc->…]` tags. Also
   corrected `manifest.md`'s "a name outside the closed v1 vocabulary is ignored" — post-v1
   names now parse, so "closed v1" had become false there.
3. ✅ **unit stage — DONE.** Three tests: UPDATES told-once + re-fires when the key's version
   moves (both arms in ONE test, so the empty second poll cannot pass vacuously);
   SEAL_BRIEF once-per-session + exactly two sentences + **names `spt api seal verify` and
   asserts it does NOT say `spt seal verify`** (the invented-verb correction, made unable to
   regress silently); the join line's two renders.

### Two defects found and fixed while doing the above (neither was in the dispatch)

- **`SEAL_BRIEF_TEXT` carried literal \n escapes (backslash-n, four spaces).** SEAL_BRIEF is not passed through
  `one_line`, so the brief would have rendered into the XML payload with embedded newlines
  and four-space indents. Rebuilt as a `\`-continued literal (no newlines in the string);
  the test now asserts the constant contains no newline.
- **`Category::V1` had been GROWN, which made its own test false.** The previous session
  appended `Updates`/`SealBrief` to the const named `V1`, and
  `every_category_round_trips_through_its_own_tag` asserts `V1.len() == 7` ("the v1
  vocabulary is CLOSED at seven"). Split it: **`V1` = the ratified seven, `ALL` = render
  order (V1 then post-v1)**. `parse`, the poll loop and the tests read `ALL`; the closure
  assertion survives, plus a new `ALL.starts_with(V1)` — the ratified render order can no
  longer shift under an append.

- **A THIRD defect, which my own tests EXPOSED rather than caused.**
  `a_second_poll_with_nothing_new_emits_nothing_and_the_first_was_not_empty` went red at
  `assert!(second.is_empty())`. Cause: seen-sets live under `perch::session_dir`, which reads
  `SPT_HOME`, and `crate::testutil::isolated_home()` retargets `SPT_HOME` **process-wide** for
  every test that takes the lock. Three tests in this module flushed a seen-set and reloaded it
  WITHOUT taking that lock, so a guard-holding test running in parallel could move the home
  between the flush and the reload — the keys were then "unseen" and the second poll rendered.
  Adding two more guard-holding tests widened the window until it fired. Fixed by giving all
  three (`a_second_poll_…`, `a_changed_state_…`, `a_new_session_has_seen_nothing`) the same
  guard — the mechanism `testutil.rs` documents as "one variable, one lock" (releases#111).
  **The red was a real latent race in the suite, not a defect in the new work**; it would have
  fired eventually on any future test that took the guard.

- **A FOURTH defect, caught by `xtask check`'s WARNING rather than its exit code.**
  `xtask check` exited 0 and printed `warning: associated constant V1 is never used` —
  golden CI's `cargo clippy --workspace --all-targets -- -D warnings` would have turned that
  warning into a red. Fixed STRUCTURALLY rather than with a per-line `allow`: `V1` (the
  ratified seven) + `POST_V1` (what was ruled in after) + `Category::all()` chaining them.
  Both consts are now live in production code, and render order became a structural fact —
  a post-v1 category cannot reach in front of the ratified set — instead of an asserted one.

## Measurements (this lane, all exit files READ, not summarized)

| Leg | Result |
| --- | --- |
| `cargo test -p spt --bins api::nowsignal` | **15 passed, 0 failed** |
| `traceable-reqs check` | **exit 0** |
| `cargo run -p xtask -- check` | **exit 0**, `xtask check: OK` (warning above, now gone) |
| `cargo clippy -p spt --all-targets -- -D warnings` | **exit 0**, zero warnings |
| leg-0 reap | no lane `spt.exe` under `.worktrees/ns23-w1/target` |
| battery phase A (`nextest --workspace`, HEAVY excluded, env-scrubbed) | **exit 0** — 3186 run, 3186 passed (9 leaky), 1 skipped, 108.7s |

⚠ `-p spt --lib` does NOT work: `spt` is bin-only (`error: no library targets found in
package spt`). Use `--bins`. Per IR-67 a `--bins` leg is NOT integration coverage — this
lane's REQs require `doc/impl/unit` only, so no int leg is owed.

⚠⚠ **The wrapper-exit trap fired THREE times in this lane, twice on my own commands.** A
compound whose LAST element is an `echo` (or a failed `&&` head) reports the wrapper's
status, and the harness's completion notification quoted **exit 0** over a python
`AssertionError` and over a `grep -oP` that exited 2. Both times the tell was the missing or
non-zero **exit FILE**, never the summary. Every figure in the table above was read from a
leg's own exit file with its output.

### Lane state: HANDED OFF

Committed **`f6e5f96d`** on `feat/ns23-w1-updates-sealbrief` (base `aa9ac15a`),
4 files, 363 insertions. **NOT landed to main — doyle's gate.** Co-author trailer verified by
raw body read + `git log --grep`, never `%(trailers:)`.

Battery completed after the table above:

| Leg | Result |
| --- | --- |
| battery phase B (HEAVY set, env-scrubbed) | **exit 0** — 210 run, 210 passed (1 slow), 611.5s |
| `traceable-reqs check` (re-run post-refactor) | **exit 0** |
| post-battery reap | **4 lane `spt.exe` survivors killed** under `.worktrees/ns23-w1/target` — the reap earned its place this time |

Handoff sent to doyle (`SENT:doyle`) carrying the sha, the three REQs, the four
out-of-dispatch defects, and the battery figures.

### Next, when doyle rules

- **#113** — all four forks ruled and fully unblocked (see Standing context below).
- **#44/#45** — W2, after #113.

## Standing context

- #113 is fully unblocked behind W1 (all four forks ruled). Fork 2's ruling: REMOVE the
  mirror-into-running-context, KEEP the digest record, RE-KIND it (`echo_commune`, not
  `echo_mirror` — the old name becomes false the moment nothing mirrors); read ADR-0019 first,
  retag `REQ-TERM-7` evidence in the same commit, and the **boundary-delta measurement is that
  lane's FIRST step**.
- #44/#45 are W2, after this.
