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.
40 lines
2.0 KiB
Markdown
40 lines
2.0 KiB
Markdown
# The 69% link-board rot was two defects wearing one number
|
|
|
|
_2026-09-22 · booth_
|
|
|
|
**Re-measuring the board before writing U6's contract split its headline number
|
|
in half, and the half U6 owns is the smaller one.** The IA doc records *211
|
|
rows, 145 (69%) pointing at booths that no longer exist*. Re-counted on
|
|
2026-09-22 the board was 221 rows — and the split nobody had taken before:
|
|
|
|
| | count | share |
|
|
|---|---|---|
|
|
| rows that are booth URLs | **178** | 80% of the board |
|
|
| …whose booth is already swept | **156** | **71% of the whole board** |
|
|
| rows that are NOT booth URLs | 43 | 19% |
|
|
| …distinct after full-URL normalization | 35 | |
|
|
| …collapsed by the re-post problem U6 names | **8 rows** | |
|
|
|
|
So the 69% is:
|
|
|
|
1. **Booth-announcement rot — 178 rows.** A session posted a booth URL because
|
|
a booth could not announce itself. **U5 already closed the cause.** Nothing
|
|
stopped the habit, so the board took 11 more of these in the day after it was
|
|
first measured.
|
|
2. **Bench re-post — 8 rows.** An append log with no identity. This is the part
|
|
the registry fixes, and it is an order of magnitude smaller.
|
|
|
|
**The third thing, which the IA doc does not describe at all:** of the 35
|
|
distinct non-booth targets, roughly **14 are running services (benches)** and
|
|
roughly **14 are reference bookmarks** — gitea repos, HuggingFace model cards, a
|
|
vLLM recipe, a Headscale page — with the rest ephemeral one-shot links. The IA
|
|
doc planned for `booth link` to survive "as a deprecated alias". That would have
|
|
evicted a third of the board's live content from the only home it has. **U6 does
|
|
not deprecate `booth link`**; it removes exactly one shape from it.
|
|
|
|
**Why this is worth keeping.** The single 69% figure implies the registry is the
|
|
big win. It is not — the enforced rule and the dead marker are. A unit scoped
|
|
off the unsplit number would have built the registry, declared victory, and left
|
|
178 rows rotting. Re-measure before contracting; the number in the design doc is
|
|
a day old the moment it is written.
|