Files
booth/persistent-memory.md
T
vh e702be4e1a fix: a wrong-shaped answer no longer 500s the gallery and the marks page
Pre-existing, measured at 42ea67f, so it predates U3. `_hydrate` checked only
that `answer` was a dict and never that `answer["answers"]` was one, so
`marks_for` and `hold_read` both reported the mark healthy with no read error
-- and `_ask_inline.html` then asked a list for `.get`. The v0.2.2 lesson was
half-implemented: that outage was a file that could not be PARSED and the
reader was made lenient, while this one parses perfectly and breaks one layer
further in, at render, where no leniency existed.

Closed at the hydration boundary rather than by a third copy of the guard --
one predicate, one place, every surface inherits it. Only the multi case is
checked, because only the multi case indexes; requiring `answers`
unconditionally would break every single-question pick, and that direction has
its own test. Measured before and after: gallery and marks pages 500 -> 200,
the error visible on the page, the booth's other healthy pick untouched.

The placement was the one open operator question of the session. It was
surfaced three times without a ruling, so it is taken under a stated assumption
and is cheap to move: the whole fix is one condition in one function.

Two things fell out of it worth more than the fix.

`_safe_fragments` no longer has a reachable natural trigger. Probed every wrong
answer shape a .marks.json can carry: `answers` as a list, a string or null all
become hydration errors now, and a wrong-typed value INSIDE `answers` renders
without raising, because Jinja absorbs attribute access on a non-mapping. U3's
guard is a pure backstop, and its test now says so and trips it synthetically
through the shared macro module rather than asserting a path nothing reaches.
A guard tested by an unreachable input is an untested guard.

And that guard's handler could not survive the failure it was handling: it
caught a raising `_pick_fragments` and rebuilt the broken-ask box through the
SAME macro module that had just raised, so whenever `whole` was the broken
thing it re-raised and took the whole report. Found by accident while building
the falsifier. Fixed, with its own test.

Both new falsifiers were verified RED against their defeating change rather
than assumed.

607 -> 611 tests.
2026-09-22 14:34:28 -07:00

14 KiB
Raw Blame History

Persistent memory — booth

Last updated: 2026-09-22

Always check for /tmp/booth-dev-handoff.md — if it exists and its Written: 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:

  • NOTHING IS IN FLIGHT. U6 (benches) landed, all four review gates closed, released as v0.6.0 and deployed. Tree clean at 3296a86, 607 tests green, 19/19 booths 200 live. ⚠ NOT PUSHED — push is the operator's call and he did not give it this session; main is ahead of origin/main. → persistent-memory.d/2026-09-22-u6-benches-released.md
  • v1 is gated on seven units. SIX ARE LANDED. U7 IS THE LAST ONE. U1 ce598b3; U2 → v0.2.0/v0.2.1/v0.2.2; U5 → v0.3.0; U4 → v0.4.0; U3 → v0.5.0; U6 1c3ce5d → v0.6.0.
  • ⚠ Before starting U7, read persistent-memory.d/2026-09-21-u7-section-premise-half-wrong.md AND re-count the booths first. Its premise has degraded twice over: every booth that needs navigation is FLAT, and on 2026-09-22 the four large booths it was sized against (pancake-v3-full/pancake-v4-full at 270 items, sindra20-engines, sindra-finalists) had ALL been swept. Largest live booth is miranda-is at 92 items. Two of 19 booths have subfolders and both are reports. Sections buy close to nothing; the rail, filters and grid keyboard are the unit.
  • THE LAST OPEN DEFECT IS CLOSED. The wrong-shaped answer that 500'd the gallery and marks pages (pre-existing, measured at 42ea67f) is fixed at _hydrate — the placement the session recommended three times and the operator never ruled on, taken under a stated assumption and cheap to move (one condition in one function) if he disagrees. Measured before/after: both pages 500 → 200, error visible, the booth's other pick untouched. Two things fell out of it that matter more than the fix — U3's _safe_fragments lost its natural trigger and is now a synthetically-falsified backstop, and that guard's own handler could not survive the failure it was handling. Read persistent-memory.d/2026-09-22-a-wrong-shaped-answer-500s-the-gallery.md before touching marks rendering anywhere.
  • ⚠ TWO OPERATOR DECISIONS ARE OUTSTANDING AND BOTH ARE DELIBERATELY NOT DONE. (1) The single althing note to the 17 handles about booth link refusing booth URLs — gated as multi-recipient, drafted nowhere, NOT SENT. (2) Seeding the bench registry from the board — he said "no seeding yet", so booth bench import --apply has NOT been run against live data and .benches.json does not exist in ~/booth-data.
  • ⚠ THE 17 CONSUMING HANDLES WERE NEVER TOLD that keep stopped meaning "waiting on an answer" — operator decision 2026-09-22, no broadcast, and it still stands. This CHANGES HOW THE 2026-10-06 RE-COUNT READS: the hold rides for free but not-pressing-keep has to be learned, so a flat .forever rate does NOT falsify the diagnosis. Read its entry before measuring.
  • A remote exists and main is AHEAD of it. origin is git@gitea.phasefinal.com:vh/booth.git; the first push of this repo's history was 2026-09-22 (26 commits, v0.2.0–v0.5.0 in one motion). As of this snapshot main is 8 commits ahead of origin/main — the whole of U6 including v0.6.0. Pushing is the operator's call.
  • Two dated predictions are pending and must not be run early. U5's adoption re-measure on 2026-09-29; the .forever re-count on or after 2026-10-06. Before the second, read persistent-memory.d/2026-09-22-no-notice-and-what-it-does-to-the-prediction.md.
  • FIVE methodology proposals sit with the operator, untracked by his choice — four from earlier rounds plus Kimi's new one: promote "the falsifiable test is weaker than the invariant it guards" to its own ambiguity class in /heid-contract-review. It now has two data points in this repo (five of seven U4 falsifiers vacuous; U6 shipped a tie-break falsifier that could not fail). They are /heid* skill changes, not this repo's work.
  • The booth set churns hard: 26 → 24 → 25 → 23 → 19 across five sessions. Re-count rather than trusting any number written here.

Recent decisions

  • [2026-09-22] The last open defect closed, and building its falsifier found another — the wrong-shaped answer fixed at _hydrate; _safe_fragments lost its natural trigger and its handler could not survive the failure it handled → persistent-memory.d/2026-09-22-a-wrong-shaped-answer-500s-the-gallery.md
  • [2026-09-22] U6 released as v0.6.0 — benches, and the number that was two defects — six of seven v1 units landed, NOT PUSHED → persistent-memory.d/2026-09-22-u6-benches-released.md
  • [2026-09-22] Three cold panels on one unit, and what each lens could only see alone — READ BEFORE DECIDING TO SKIP A GATE; all five passes found something the others structurally could not → persistent-memory.d/2026-09-22-three-cold-panels-on-one-unit.md
  • [2026-09-22] U6 landed — three surfaces, three jobs, one predicate — the seam review caught three real contract defects incl. a per-row resolve_booth that would have 404'd the board → 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 at 42ea67f), 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 TOUCHING playwright IN 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 .forever diagnosis 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 answer got 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/booth went 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.json wiped 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.json per 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-finalists is 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 .forever diagnosis 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 from eshpfi into 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 .forever is 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