Nothing in flight. The in-flight section is rewritten to the post-release
state and carries the five things a fresh session must not do: push (main is 8
ahead of origin/main), seed the registry, send the 17-handle note, run either
dated prediction early, or start U7 without re-counting the booths first.
Two new detail files: the release itself, and what each of the five review
passes could only see alone -- the strongest evidence this repo has for running
all of them rather than picking one. The earlier U6 entry is reconciled; it was
written while the gates were still out and said NOT TAGGED.
Restored in the rewrite: the warning that the 17 handles were never told `keep`
stopped meaning "waiting on an answer", which is load-bearing for how the
2026-10-06 re-count reads, and the fact that a remote now exists.
The standing link board carried three jobs because only one of them had a
surface. Re-measured before contracting, its 221 rows split into 178 booth
announcements (156 already dead) and 43 non-booth rows, of which 8 are the same
bench re-posted. U5 gave the booth announcement a home; this gives the running
service one, and refuses the one shape that now has somewhere better to go.
- booth/benches.py (new, stdlib-only and sibling-free): the Bench record, URL
normalization as the identity, a lenient read on the render path and a strict
read on the write path, atomic replace under an flock, and a stated total
order (state rank, name casefolded, id).
- links.booth_target: ONE predicate for "is this a booth URL", consumed by the
CLI refusal, the board's dead marker and bench import. Host-agnostic,
path-shaped, percent-decoded, never raises.
- booth link refuses a booth URL, names `booth new --why`, and writes nothing —
not the row, not the board directory, not the announcement.
- The board marks rows whose booth has been swept. Nothing here deletes a row:
removal stays the operator's two clicks through the existing bulk control.
- booth bench add|ls|state|rm|import. import writes nothing without --apply and
never edits links.md.
- docs/archive/links-2026-09-22.md: the board archived verbatim into git.
Identity is the FULL normalized URL, not the origin, and that was measured:
origin identity collapses the 43 non-booth rows to 19 groups by merging eight
distinct gitea repositories into one row, three unrelated HuggingFace model
cards into one, and the two LRPG surfaces on 10.100.10.50:8321 — the design
doc's own example of two real benches — into one. Full-URL identity still
collapses both cases that doc names: talk 5 to 1, Peedlar 3 to 1.
booth link is NOT deprecated. Roughly 14 of the 35 distinct non-booth targets
are reference bookmarks for which the board is the right and only home; the
design doc's plan to deprecate it would have evicted a third of its live
content. Corrected there, along with what "normalized URL" means.
The seam review found three real defects in the contract before any code: the
claim that test_stdlib_only already forbids sibling imports (it exempts `booth`
on purpose), naming resolve_booth as the dead marker's existence check (it
raises HTTPException(404), so one swept booth would have 404'd the whole board
page), and silence on percent-encoding (booth links are emitted through
quote(name, safe=""), so a raw comparison marks every encoded booth dead
forever). That both list_booths and sweep_once skip the registry was verified
against the real functions rather than assumed.
444 -> 555 tests. Deployed and verified live: 23/23 booths 200, and the board
renders 156 dead of 221 rows, matching an independent pre-implementation count.
NOT TAGGED: both cold gates are in flight (contract review
01M35BWCJ806MT75NA630Y4WFH, code review 01M35CK8YKEKMV7T15JXEF6A8N) and the
bug-hunt has not run. Per the v0.2.0 lesson, the tag waits for the gates.