# DOCS-NITS lane — JIT (rides the batch AFTER v0.67.1 tags)

Authored by doyle 2026-09-06 while the v0.67.1 respin (`34017906638` @ `04e32c8c`)
finished its Windows leg. Five items, every one MEASURED by me (evidence inline);
three from flynn's alchemy #79/#80 grills, one from perri's frames-head question,
one from emphasys's poll/listen single-spool ask (Item 5, added ~10:35Z).
Docs-only plus ONE Rust doc-comment. No new REQ: every amendment lands under an
existing `REQ-*` whose `doc` stage already covers that page.

Doc source is `docs-site/src/` (NOT `docs/`). Base off `origin/main` once v0.67.1
has landed there; branch `docs/nits-0672`, worktree `.worktrees/docs-nits-0672`,
own pool, claim from inside the worktree.

## Binding rules for the builder

- **Correct by replacement**, never by annotation — rewrite the sentence that is
  wrong; do not append a "note:" under it.
- **Positive framing**: say what the field/rule DOES, then what a reader can rely
  on. The `TOOL_USE` row in `frames.md:418` (`**reserved — nothing emits this
  today**`) is the house idiom for "named in the vocabulary, unemitted".
- **One rule, written once, cross-linked from its other consumer.** Two prose
  copies of a match rule drift; a pointer does not.
- Items 1 and 4 touch the SAME `seq` bullet in `frames.md#io` — land them as one
  coherent edit of that bullet, not two passes.

## Item 1 — `frames.md#io`: an absent `seq` can mean "turn still open"

**Where:** `docs-site/src/shells/frames.md` §`io` (line ~397), the `seq` bullet:
> `seq` — present only when there is a digest entry to point at … Follow it with
> `spt endpoint digest` …

**Gap:** a shell binary reading `io` frames is never told WHY a pointer may be
missing on a body it just saw. `json-shapes.md` already states the rule (the
`<!-- [doc->REQ-DIGEST-SEAL-ON-IDLE] -->` paragraphs, line ~229): a turn's
entries carry **no `seq` until the turn commits, and a turn commits when the
endpoint goes idle** (or the next input lands).

**Do:** add one sentence + link in the `seq` bullet pointing at
`../reference/json-shapes.md` (the digest-seal-on-idle paragraphs: "a turn
closes when the endpoint goes idle"), so the reader learns that an absent `seq`
on a fresh body is the open-turn case, not an error. Keep the tag that already
covers the section (`REQ-IO-EVENT-TAXONOMY` at `frames.md#io`); no new tag.

⚠ Read Item 4 first — today the pointer is ALSO absent for a second, larger
reason, and the bullet must state both honestly.

## Item 2 — `api hint` doc-comment names a flag its own `--help` cannot show

**Where:** `crates/spt/src/api/mod.rs:421-424` — the `Hint` variant doc-comment
ends `Needs `--manifest`.`

**Measured (flynn, confirmed by me against `reference.md`):** `--manifest` is a
**group-level** option on `spt api` (reference.md `spt api` Options block:
`--manifest <MANIFEST>  Path to the adapter's runtime manifest (when the command
needs it)`). So the working spelling is `spt api --manifest <path> hint --session
<sid>` — flag BEFORE the subcommand; `spt api hint --manifest …` is clap
correctly refusing an unknown subcommand arg, and `spt api hint --help` cannot
list a parent option. Second route per `resolve_ctx_manifest`'s own contract:
with `--manifest` absent the manifest resolves from the REGISTRY via the
group-level `--adapter <name[:profile]>`, so a registered adapter needs no path:
`spt api --adapter alchemy hint --session <sid>`. `NO_MANIFEST: hint needs
--manifest` fires only when NEITHER resolves.

**Do:** replace `Needs `--manifest`.` with a sentence that (a) names the
placement — group-level on `spt api`, before the subcommand — and (b) names the
`--adapter` registry route, and (c) says the `NO_MANIFEST` refusal is the
neither-resolves case. Then **regenerate `reference.md`** — it is generated from
`--help`, and the `xtask check` drift gate compares the committed file against
the ON-DISK `spt` binary (IR-12 stale-artifact kin, arc note 2026-08-30): run the
workspace-bins prebuild leg BEFORE `xtask check`, or the gate can exit 0 over
real drift. Existing tag on the verb covers it; no new tag.

## Item 3 — `[[hints]]` match rule: stated nowhere on the hints page, `regex` field omitted

**Where:** `docs-site/src/shells/overview.md` §"Teach the agent about your
shell: `[[hints]]`" (line ~156, tag `REQ-SHELL-HINTS`). Cross-link target:
`docs-site/src/networking/monics.md` §"What a trigger can watch" (line ~450,
tag `REQ-CLI-MONIC-TRIGGER-SECTION`).

**Measured (flynn 4 probes + my 0.67.0 byte-exact probes 2026-09-06):** hints and
monic triggers share ONE function, `spt_store::matchrule::pattern_matches`
(ruled shared verbatim, LOCKSMITH 2026-08-03). The rule:
- literal (default): **case-insensitive substring** — no word boundary,
  whitespace literal, no escaping (emoji and `#<` fire as written);
- `regex = true` on the `[[hints]]` row: keywords compile as regex; **regex rows
  are CASE-SENSITIVE** (`Regex::new`, no `(?i)`) — measured: 'sweep now' fired
  `(?i)Sweep` and skipped bare `Sweep`; 'Sweep it' fired the bare row;
- an **invalid regex matches nothing** — silences its own hint, never panics.
The `regex` field is in the manifest schema and absent from the hints page
entirely. monics.md states the literal + invalid-regex halves but NOT the
regex-case-sensitivity half.

**Do — write the rule ONCE, on the hints page** (it is the manifest-side home;
monics is a consumer): add a short "How a keyword matches" passage under the
`[[hints]]` section stating all three bullets above, show `regex = true` in the
TOML example, give the two idioms — word boundary: `keywords = ['\bpair
machine\b']` with `regex = true`; case-insensitive regex: `'(?i)\bsweep\b'`;
whitespace tolerance: `'pair\s+machine'`. **Use TOML literal (single-quoted)
strings in the example** — the nicer spelling; both spellings reach the engine
intact (flynn's "basic string loses `\b`" claim was REFUTED byte-exact: his
tool collapsed a backslash level, unquoted-heredoc class). No sentence about
spelling hazards. Tag the passage `<!-- [doc->REQ-MANIFEST-4] -->` (the
manifest-schema REQ, line 80 of the registry) in addition to the section's
existing `REQ-SHELL-HINTS`.
Then in monics.md replace its two-sentence rule with: the case-sensitivity
sentence added (it is the same function, so it is true there too) + a link
"same rule as `[[hints]]`" to the hints passage. One rule, two consumers.

## Item 4 — `digest_seq` / io `seq` are RESERVED and unemitted; COMMUNE is not digest-backed

**Where (three surfaces, one field):**
1. `docs-site/src/harness-contract/api.md` §`api io-events` (~line 477-484,
   tag `REQ-IO-EVENT-POLL-VERB`): example shows `"digest_seq": 88`; prose says
   *when `truncated` is set, follow `digest_seq` into the digest for the whole
   body*.
2. `docs-site/src/shells/frames.md#io` `seq` bullet (~397) — same promise.
3. `docs-site/src/shells/frames.md#boundary` `seq` bullet (~333) — already
   hedged ("absent when there is none to point"); make it positive.

**Measured (2026-09-06, source at `04e32c8c`):** `IoEvent.seq` defaults `None`
(`crates/spt-daemon/src/iobus.rs:78`); the only setter `IoEvent::with_seq`
(`iobus.rs:92`) has **ZERO production callers** (grep: only test/assert sites).
That one field feeds all three surfaces — `compose_io_frame(.., ev.seq, ..)`
(`iobus.rs:204`), `compose_boundary_frame(.., ev.seq)` (`:201`), and the io-log
row's `digest_seq: ev.seq` (`:348`). So **no kind has ever emitted a pointer**;
a `truncated` body's remainder is **not recoverable from core today**. COMMUNE
frames specifically: the frame is the drop-file bytes at delete, bounded 16KB,
and ingest writes no digest entry — not digest-backed at all.

**Operator ruling (2026-09-06, same night, binding):** core does NOT parse
`!!wake!!`; across-commune/wake are claude-spt concepts and do not belong in
core — **never propose a core-side wake bit.** perri was told to move the marker
to the HEAD (frames head-truncate); core-side body retention is HIS addition to
file if wanted. This lane amends DOCS only; it must not grow a retention feature.

**Do:** on all three surfaces, state in the `TOOL_USE` idiom that the pointer is
**named in the shape so a future emitter has a slot, and nothing emits it
today** — a consumer keeps the attribute optional and MUST NOT wait for it.
api.md: drop `"digest_seq": 88` from the example (or show it absent) and
rewrite the *follow `digest_seq`* sentence to say what a reader CAN rely on: the
payload is complete unless `truncated` is set, and a truncated remainder is not
recoverable through this verb today; the `seq`-vs-`digest_seq` naming sentence
stays (it is why the slot is named that way). frames.md#io: the `seq` bullet
becomes one coherent statement covering Items 1 + 4 (unemitted today; when an
emitter arrives, an open turn still carries none until idle-commit → link).
Add one sentence to the COMMUNE paragraph (`frames.md` ~560): the body is the
drop-file bytes bounded at the payload cap, head-truncated per perri's fix
when that lands — check the claude-spt side before writing "head".

## Item 5 — `api.md`: `poll` and `listen` drain ONE spool (emphasys's ask, confirmed 2026-09-06 ~10:34Z)

**Where:** `docs-site/src/harness-contract/api.md` — `### api poll` (~line 506,
first paragraph) and `### api listen` (~line 105, "drain spooled backlog"). One
sentence, written once under `api poll`, cross-referenced from `listen`.

**Measured (source at `04e32c8c`):** both verbs take rows from the SAME
per-perch spool at the same perch path — `listen`'s relay drains via
`spt_daemon::relay::Relay::drain_backlog` (`relay.rs:63`, taker leg
`relay-backlog`) and `poll` via `poll_drain` (`spt/src/api/delivery.rs:714`,
taker leg `hook-poll`); both call into `spt_store::spool::drain_*_audited_at`,
whose take flips `delivered = 1` and stamps the leg in the same UPDATE
(`spool.rs:248`, REQ-SPOOL-TAKE-AUDIT). So a row taken by either verb is gone
for the other: there is no second copy, and a harness that runs a `listen`
relay AND hook-polls sees each message exactly once, on whichever leg reached
it first. (Adapter builders were reasoning from a two-spool picture — an
omp-spt watchdog plan reached its no-finding verdict only after this was
confirmed by message; the docs must carry it.)

**Do:** under `### api poll`, add one sentence in positive form: *`poll` and
`listen` drain the same spool — a message either one delivers is marked taken
and is never re-served by the other, so a harness running both sees each
message exactly once, on whichever leg reaches it first.* Under `### api
listen`, append to "drain spooled backlog" a parenthetical link: *(the same
spool `api poll` drains — see there)*. Existing tags cover both sections
(`REQ-IO-EVENT-POLL-VERB` is io-events, NOT this — check which `REQ-*` tag sits
on `### api poll` before landing and keep it); no new REQ. Do NOT mention the
taker-leg tokens or `delivered` column — internal, adapters must not depend on
them.

## Gate (docs lane; right-sized)

1. Workspace-bins prebuild leg (mandatory) → `cargo run -p xtask -- check`
   (docs drift + spacerun) → `traceable-reqs check` (exit 1 = miss, **exit 2 =
   registry did not parse — nothing was checked**) → `cargo clippy -p spt`
   (doc-comment touched).
2. Docs-site build (`mdbook build docs-site` or the xtask leg that wraps it) —
   the two new cross-links must resolve.
3. Audit: every replaced sentence's OLD string grepped across `docs-site/src`
   and `crates/` — a retraction sweeps STRINGS, not just the one file.
4. Pick-audit 1/1 vs the branch tip; ff-only onto main; delete branch against
   `origin/main` ancestry.

## Out of scope (named so nobody folds them in)

- HEAVY reclass of `brain_resume_conn_deadlock` — ALREADY on the v0.67.1 respin.
- hertz's W2 rig-fixups (`test/w2-rig-fixups`) — his lane.
- Any core-side wake/retention feature — operator-refused.
- #254 spt-shells deprecation-gate inertness — intake candidate, not a nit.
