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.
17 KiB
17 KiB
Persistent memory — booth
Last updated: 2026-09-22
Always check for
/tmp/booth-dev-handoff.md— if it exists and itsWritten:stamp is under 8 hours old, read it (it carries the in-flight handoff from the previous session), then delete it. Older than 8 hours: stale — delete it unread.
Repo purpose
The Booth is the fleet's operator-review surface: agents post work by
making a folder under ~/booth-data, the operator looks at it and judges it in
the browser, and the judgment gets back to the agent that posted it. It was
built as a file-shuttle and is being converged, unit by unit, onto the review
loop it turned out to actually be.
Current state / in-flight
As of 2026-09-22:
- v1 is gated on seven units in
ROADMAP.md, dependency-ordered U1 → U2 → {U3, U4, U5} → U7, with U6 independent. - U1, U2, U3, U4 and U5 are landed — the whole middle tier is closed. U1
ce598b3; U2c7f9437→v0.2.0,5e41108→v0.2.1,026a1fc→v0.2.2; U5c015a91+95beede→v0.3.0; U4c3a97c1→v0.4.0. U3 landed 2026-09-22 and released asv0.5.0— 444 tests green (410 → 444), deployed and verified live, 23/23 booth pages 200, and each of the four verbatim booths served at exactly +46 bytes, which islen(EMBED_SCRIPT_TAG)— one append, nothing else.87e2c53is the unit,5c20e2fthe panel fixes,7996fbdthe release. PUSHED AND DEPLOYED 2026-09-22 on the operator's word — and it was the first push of this repo's history:mainwas 26 commits ahead oforigin/main(ce598b3..7996fbd), sov0.2.0throughv0.5.0all reachedgit@gitea.phasefinal.com:vh/booth.gitin the same motion.maintracksorigin/mainclean now; a future session can assume a remote exists, which no earlier one could. - U4 released as
v0.4.0(operator approved the minor on 2026-09-22).c3a97c1is the unit; the release commit carries the pre-existing fixes the bug-hunt panel surfaced in touched files. The tag waited for the last gate to close, per thev0.2.0lesson — see Tried and abandoned. - ⚠ The 17 consuming handles are NOT being told that
keepno longer means "waiting on an answer" — operator decision, 2026-09-22, no broadcast. This is deliberate and it CHANGES HOW THE 2026-10-06 RE-COUNT READS: the hold rides for free, but not-pressing-keephas to be learned, so a flat.foreverrate does not falsify anything. Read its entry before measuring. - U6 LANDED 2026-09-22 — ONE UNIT LEFT TO v1. Benches: a registry keyed by
normalized URL,
booth linkrefusing a booth URL, dead rows marked on the board, and a non-destructivebench import. 444 → 555 tests, deployed and verified live (23/23 booths 200, 156 of 221 rows marked dead — matching an independent pre-implementation count exactly). ⚠ COMMITTED BUT NOT TAGGED AND NOT RELEASED: both cold gates were still in flight at commit time (contract review01M35BWCJ806MT75NA630Y4WFH, code review01M35CK8YKEKMV7T15JXEF6A8N) and the bug-hunt had not run. Per the v0.2.0 lesson, the tag waits for the gates. →persistent-memory.d/2026-09-22-u6-benches-landed.md - ⚠ U6 WAS CHOSEN WITHOUT THE OPERATOR ANSWERING. He set an autonomous goal ("hydrate and land next unit stated in handoff") and the handoff named no unit. The session recommended U6 on measured grounds (its defect compounds — 145 → 156 dead rows in a day — while U7's is dormant, and U6 had no unresolved design questions) and proceeded rather than blocking. The scope call is still his to reverse; nothing is pushed and nothing is tagged.
- U7 IS THE LAST UNIT, and its premise degraded again. ⚠ Read
persistent-memory.d/2026-09-21-u7-section-premise-half-wrong.mdAND re-count first. On 2026-09-22 the four large booths U7 was sized against (pancake-v3-full/pancake-v4-fullat 270 items,sindra20-engines,sindra-finalists) had ALL been swept. Largest live booth ismiranda-isat 92 items, flat. Two of 23 booths have subfolders and both are reports. Sections buy close to nothing; the rail, filters and grid keyboard are the unit. - U3's tier was MINOR and the operator approved it (2026-09-22). The
argument that settled it, recorded because the tie-break rule says patch: a
capability arrived AND one left — the verbatim path gained a declared public
API (
<script src="/_booth/embed.js" defer>) and lost no-JavaScript operation. That asymmetry is what made it not a tie. - ALL FOUR U3 GATES ARE CLOSED. In-session seam review (5 findings, SR-2 a
real payload-shape bug);
/heid-contract-review(01M351WKV666D681SSRNY7D7X6, 12 findings, 10 adopted, 2 already settled by the seam review while it was in flight, 1 declined);/heid-code-review(01M352RXV1ZET566KV73C7TSB8, 3 more vacuous falsifiers- the prototype-pollution bug);
/heid-bug-hunt(01M352TPCSN52G6NGJ07T5WSGY, 5 net-new, incl. the byte-exactness break). Seven of the adopted findings were CODE fixes, not wording — the cold gates were not ceremony on this unit. The seam review ran in-session and is folded in — five findings as a table at the end of the U3 contract, and SR-2 was a real payload-shape bug the cold panel structurally could not see. U4's three and U5's three are all closed (01M34VX0SH23Y3VC92E7GM4S70,01M34WAFJC3RTERFYBBZJN1SVG,01M34Y2R0RAJRSN36Q8K4KAB36;01M340PNVRS21HPASZT38PXQPN,01M341E9XAPZEFBSPK9HPGAM0S,01M343SXX27Z47C3STXXRC7M42).
- the prototype-pollution bug);
- ⚠ ONE DEFECT IS OPEN, FOUND BUT DELIBERATELY NOT FIXED, AND IT HAS NO
TRACKING SURFACE YET. A
.marks.jsonthat is well-formed JSON with a wrong-shapedanswer500s the gallery page and the marks page — reproduced, and measured at42ea67fso it PREDATES U3. U3 guarded its own surface (_safe_fragments) and left those two alone rather than widening the unit; the gallery is named out of scope in the U3 contract. The operator was asked where the guard belongs and has not answered —_hydrate(session recommendation: one predicate, one place, every surface inherits it), per render site, or the template. No issue filed. Readpersistent-memory.d/2026-09-22-a-wrong-shaped-answer-500s-the-gallery.mdbefore touching marks rendering anywhere. - Two dated predictions are pending and must not be forgotten. U5's adoption
re-measure on 2026-09-29 (two counts, see its entry — already at 3 of 24
announced and 2 with a
why, all from peers told nothing), and the.foreverre-count on or after 2026-10-06, a fortnight after U4 landed, which is U4's success criterion. ⚠ Only 4 booths carry marks at all, so the hold's live blast radius is small and the prediction rests on both halves of U4 — see its entry for what a null result would and would not mean. - FOUR methodology proposals sit with the operator, all UNTRACKED BY OPERATOR
CHOICE (no issue, no ticket — they are
/heid*skill changes, not this repo's work, and are recorded here only so they are not lost). Three are from the U5 round: reshaping the paraphrase gate toward a drift-check for narrative-heavy contracts, a standing "green-tests-prove-nothing" direction for the code-review gate, and regin's table-vs-signature consistency pass. The fourth is new and is the one with evidence behind it: a contract-time VACUITY PASS — for each invariant, name a change that defeats it and check the test goes red. Regin and Kimi proposed it independently on the U4 paraphrase round; the code-review panel then showed five of seven U4 falsifiers were vacuous, and heid rates that the strongest single data point for it so far. Seepersistent-memory.d/2026-09-22-vacuous-falsifiers.md. - The booth set churns hard: 26 → 24 → 25 across the last two sessions as the sweeper ran. Re-count rather than trusting any number written here.
Recent decisions
[2026-09-22]U6 landed — three surfaces, three jobs, one predicate — the seam review caught three real contract defects incl. a per-rowresolve_booththat would have 404'd the board; NOT TAGGED, gates in flight →persistent-memory.d/2026-09-22-u6-benches-landed.md[2026-09-22]The 69% link-board rot was two defects wearing one number — READ BEFORE SCOPING ANY LINK-BOARD WORK; U5 closed the larger half and full-URL-vs-origin identity is a measured call →persistent-memory.d/2026-09-22-one-number-was-two-defects.md[2026-09-22]U3 landed — the page declares the seam, the Booth mounts into it — ten regexes against author HTML replaced by a substring test and a+→persistent-memory.d/2026-09-22-u3-declared-embed-seam-landed.md[2026-09-22]A wrong-shaped answer 500s the gallery and the marks page — PRE-EXISTING (measured at42ea67f), NOT U3; the v0.2.2 lesson is only half-implemented →persistent-memory.d/2026-09-22-a-wrong-shaped-answer-500s-the-gallery.md[2026-09-22]The browser became a test surface — READ BEFORE TOUCHINGplaywrightIN pyproject; the pinned upper bound is the foot-gun, and these tests SKIP rather than fail →persistent-memory.d/2026-09-22-the-browser-became-a-test-surface.md[2026-09-22]A vacuity pass that tries the contract's own mutation agrees with itself — U3 ran one, reported 7/7, and a cold panel then showed one of the seven was vacuous; READ BEFORE WRITING A Falsifiable: LINE →persistent-memory.d/2026-09-22-seven-of-seven-falsifiers.md[2026-09-22]U4 landed — lifetime is derived, not declared — three states, viewing is activity, and no new arithmetic anywhere →persistent-memory.d/2026-09-22-u4-derived-lifetime-landed.md[2026-09-22]The.foreverdiagnosis got a live positive control — 3 of the 4 booths awaiting an answer were ALSO hand-pinned — RE-COUNT 2026-10-06 →persistent-memory.d/2026-09-22-forever-had-a-live-positive-control.md[2026-09-22]No fleetwide notice for U4, and what that does to the prediction — READ BEFORE THE 2026-10-06 RE-COUNT; a flat rate does not falsify the diagnosis →persistent-memory.d/2026-09-22-no-notice-and-what-it-does-to-the-prediction.md[2026-09-22]Four independent paths to one fail-open delete — the bug-hunt panel's class, and the zsh word-splitting trap that shipped an empty bundle →persistent-memory.d/2026-09-22-four-paths-to-one-fail-open-delete.md[2026-09-22]Two reads of one file are not one read of one state — a TOCTOU seam that composes two correct readers into a fail-open delete →persistent-memory.d/2026-09-22-two-reads-are-not-one-state.md[2026-09-22]Five of seven INV falsifiers did not falsify anything — read before writing a Falsifiable: line; a green test cited one rather than being one →persistent-memory.d/2026-09-22-vacuous-falsifiers.md[2026-09-22]The third one-branch template miss — this repo's recurring blind spot; read before adding a fact to any template →persistent-memory.d/2026-09-22-third-one-branch-template-miss.md[2026-09-22]The size cap opened a service-wide hang — a FIFO has st_size 0; a bound that trusts it inherits what it does not mean →persistent-memory.d/2026-09-22-size-cap-opened-a-hang.md[2026-09-22]An existing test stopped me retiring documented behaviour — the clean fix for the mtime race would have silently changed TTL doctrine →persistent-memory.d/2026-09-22-doctrine-not-defect.md[2026-09-22]Two U5 panels, and prose reached a released outage — read the detail before assuming a conformance finding stops at its own module →persistent-memory.d/2026-09-22-u5-panels-reached-a-released-bug.md[2026-09-22]U5's adoption prediction split in two — the handle rides for free, the why must be learned — RE-MEASURE 2026-09-29 →persistent-memory.d/2026-09-22-u5-adoption-split-in-two.md[2026-09-22]The U2 bug-hunt panel was not ceremony — the lock-unlink race and the TTL guard that was failing at its own job →persistent-memory.d/2026-09-22-u2-bug-hunt-panel.md[2026-09-22]The lenient reader's blast radius was the whole service — marks_for runs per booth per index load; a raise there is an outage →persistent-memory.d/2026-09-22-lenient-reader-blast-radius.md[2026-09-22]booth marks/booth answergot real exit codes — read it before changing anything the 17 consuming handles call →persistent-memory.d/2026-09-22-cli-exit-codes.md[2026-09-22]scripts/boothwent from zero tests to five — they run the real script under system python3, so they also check INV-1 →persistent-memory.d/2026-09-22-scripts-booth-got-tests.md[2026-09-21]v0.2.0 was tagged while a gate was in flight — the sequencing lesson: if a gate is outstanding, the tag waits →persistent-memory.d/2026-09-21-v020-tagged-with-a-gate-in-flight.md[2026-09-21]A write over a damaged.marks.jsonwiped the booth — the reads-lenient / writes-strict asymmetry, and why it exists →persistent-memory.d/2026-09-21-marks-write-wiped-judgment.md[2026-09-21]Seam review and cold panel had zero overlap, twice — evidence for running both; neither substitutes for the other →persistent-memory.d/2026-09-21-two-gates-are-complementary.md[2026-09-21]Every code-changing finding came from the AMBIGUITY pass — a finding about the /heid-contract-review skill, not about this repo →persistent-memory.d/2026-09-21-ambiguity-pass-did-the-work.md[2026-09-21]Deterministic order is a cross-cutting v1 invariant — operator directive; read before adding ANY ordered surface →persistent-memory.d/2026-09-21-deterministic-order-invariant.md[2026-09-21]U2 (marks) landed — one primitive for three mechanisms — what moved where, and the HTTP mirror remote sessions poll →persistent-memory.d/2026-09-21-u2-marks-landed.md[2026-09-21]A partially-answered pick counts as OPEN — declared, not smuggled; it is the reading that makes U4 correct →persistent-memory.d/2026-09-21-partial-answer-counts-as-open.md[2026-09-21]The U2 seam review earned its place, and how — inline.place indexes by subscript — the miss a cold panel cannot see →persistent-memory.d/2026-09-21-u2-seam-review-earned-it.md[2026-09-21]Marks are one.marks.jsonper booth — operator decision with two rejected alternatives; read before restructuring →persistent-memory.d/2026-09-21-marks-storage-decision.md[2026-09-21]U7's section premise is half wrong — every booth that needs navigation is FLAT — read before starting U7 →persistent-memory.d/2026-09-21-u7-section-premise-half-wrong.md[2026-09-21]sindra-finalistsis U2's flag motivation, caught live — evidence, not argument →persistent-memory.d/2026-09-21-sindra-finalists-is-the-motivation.md[2026-09-21]The information architecture and the v1 gate landed — the single defect the seven units decompose →persistent-memory.d/2026-09-21-ia-and-v1-gate-landed.md[2026-09-21]The.foreverdiagnosis is a falsifiable prediction — U4's success criterion — re-count a fortnight AFTER U4 lands →persistent-memory.d/2026-09-21-forever-diagnosis-is-a-prediction.md[2026-09-21]Extracted fromeshpfiinto its own repo — test_booth.py is the regression net the v1 rewrite is checked against →persistent-memory.d/2026-09-21-extracted-from-eshpfi.md
Tried and abandoned
[2026-09-21]Tagging a release while a review gate was in flight — cost a same-hour v0.2.1 and a correction to 15 handles →persistent-memory.d/2026-09-21-tagging-with-a-gate-in-flight.md[2026-09-21]Letting the write path share the read path's leniency — a tolerant reader and a tolerant writer are not the same decision →persistent-memory.d/2026-09-21-tolerant-writer-over-tolerant-reader.md[2026-09-21]Letting Jinja hot-reload templates in the deployment root — caused a live outage: 19 of 25 booths at 500. Why auto_reload=False →persistent-memory.d/2026-09-21-jinja-hot-reload-outage.md[2026-09-21]Five mechanisms to get one question beside one artifact — the accretion signature this whole v1 rewrite is undoing →persistent-memory.d/2026-09-21-five-mechanisms-one-job.md[2026-09-21]Regex-injecting chrome into arbitrary author HTML — the defect U3 exists to close →persistent-memory.d/2026-09-21-regex-injecting-chrome.md[2026-09-21]A boolean escape hatch as the lifetime mechanism — why.foreveris a symptom; the defect U4 exists to close →persistent-memory.d/2026-09-21-boolean-escape-hatch-as-lifetime.md[2026-09-21]Letting the link board absorb the announce job — 69% rot; U5 gave the job a home, which is what unblocks U6 →persistent-memory.d/2026-09-21-link-board-absorbing-announce.md