# The Booth — roadmap Design: [`docs/design/information-architecture.md`](docs/design/information-architecture.md). Current version: `0.4.0` (U1, U2, U4 and U5 landed; extracted from eshpfi 2026-09-21). ## v1 target What must be true to cut 1.0. Seven capabilities, each closing a **measured** defect — not a wish. The measurements are in the IA doc. | # | capability | closes | unit | |---|---|---|---| | 1 | ~~**One item record**~~ — **landed `ce598b3`** | captions never reach the zoom view (never sent, not lost) | U1 | | 2 | ~~**Marks**~~ — **landed `c7f9437`, released `v0.2.0`** | 5 mechanisms for 1 job; operator→session loop runs through chat | U2 | | 3 | **Declared embed seam** — `/_booth/embed.js`, chrome mounts via DOM | 6 regexes injected into arbitrary author HTML, load-bearing for asks | U3 | | 4 | ~~**Derived lifetime**~~ — **landed `c3a97c1`, released `v0.4.0`** | 70% of booths on the `.forever` escape hatch (54% when first counted) | U4 | | 5 | ~~**Self-announcing booths**~~ — **landed `c015a91`, released `v0.3.0`** | job 5 had no home, so it lived on the link board as 145 dead rows | U5 | | 6 | **Benches** — registry, identity, enforced rule, migration | 69% link-board rot; the same bench posted 5× | U6 | | 7 | **Navigation at 270 items** — sections, rail, filters, grid keyboard | one flat wall; subfolder structure discarded at render | U7 | Ordering is dependency-driven, not priority-driven: **U1 → U2 → {U3, U4, U5} → U7**, with **U6 independent** of all of them (different storage, different surface) and therefore the safest thing to land first or in parallel. **U1, U2, U4 and U5 are landed.** U3 is unblocked and unstarted; U6 remains independent and unstarted; U7 waits on the rest. **U5's adoption is a measured prediction, not a finished result**, and it is TWO predictions rather than one. The operator declined a fleetwide announcement so that adoption could be told apart from design; within fifty minutes of the deploy a peer that had been told nothing (`comfy-dev`) created a booth and it announced itself with a handle and an empty `why`. That is the split: - **The handle rides for free.** It is written by `booth new` and `booth add`, so every existing caller starts announcing without learning anything. - **The `why` has to be learned.** It needs someone to know the flag exists. Both get re-measured on **2026-09-29**: find ~/booth-data -maxdepth 2 -name .booth.json | wc -l # free grep -l '"why": "[^"]' ~/booth-data/*/.booth.json | wc -l # learned A high first count with a near-zero second is the predicted shape of "nobody was told" — an adoption failure fixed by announcing, which is a different thing from nobody wanting it. Same instrument as U4's `.forever` prediction below. ### Cross-cutting invariant — deterministic order, everywhere **Every ordered collection the Booth renders must have a stated, deterministic order.** Not "usually stable", not "whatever the filesystem yields" — a rule someone can name, that produces the same sequence on every render of the same state. The rule itself is free to be anything defensible: byte order over a path, creation time, an explicit number, even an arbitrary-but-recorded sequence. What is forbidden is *no rule*. This matters more here than in most services because the Booth's whole job is **comparison**. The operator is judging `pancake-v3-full` against `pancake-v4-full`, tile 47 against tile 47. If the order shifts between two page loads — or differs between the gallery, the zoom ring, the zip manifest and the `marks` read — then every positional reference the operator makes ("the third one from the left", "the one after the banded one") is silently wrong, and a flag or a note lands on the wrong artifact. Non-determinism does not present as a bug report; it presents as the operator's judgment being quietly misfiled. Where it already binds, and what the rule is in each case: | collection | rule | |---|---| | items in a booth | `sorted(rel)` — byte order over the booth-relative path (U1 INV-3) | | the zoom prev/next ring | the item order, filtered to images — same sequence, one source | | caption sidecar resolution | sorted scan, so two media files sharing a stem resolve the same way every time (a real non-determinism U1 removed) | | marks in a booth | `(created, id)` — time, with the id as tie-break so two marks written in the same second cannot swap | | legacy ask import | `(mtime, name)`, which is the order `list_asks` gave them | | link board rows | pinned first, then newest-first | | a booth's announcement | not a collection — one flat record per booth, nothing to order (U5) | U4 added no ordered collection — a booth's lifetime is one state per booth, not a sequence — so the rule above did not need a new row. The three lifetime surfaces (index card, booth header, marks page) render through ONE macro precisely so they cannot disagree, which is the same property stated for ordering: one rule, one place, every surface reading it. Where it is still to be decided, and must be before the unit ships: **U7's section ordering and its compare pairing** (sections need a stated order among themselves, not just within; pairing by filename needs a rule for what happens to an unpaired file), and **U6's bench listing**. The test for any new ordered surface: *can you write the rule down in one line?* If not, it does not have one yet. ### Explicitly NOT in v1 - **Backward compatibility with the `ask` CLI verbs.** Pre-1.0, and `ask` / `asks` / `answer` become thin aliases over `marks` rather than a second code path. The 17 consuming handles get one althing note naming the change — the one case where telling peers is real coordination and not a broadcast. - **A migration that deletes anything.** `links.md` is archived verbatim and committed before the registry is seeded from it. ## Parking lot Deferred with a home, per the anti-creep gate. Default is park; these were weighed against the v1 path and lost on purpose. | item | why parked | |---|---| | **Compare mode** — pair-by-name A/B across subfolders | The best idea in the set, and the only one that is a *new capability* rather than a fix for a measured defect. The four-booth `pancake-v3/v4` dance still works. First thing in v1.1. | | Virtualized / progressive grid loading | Speculative. 270 `` may be fine. **Measure the real booth before optimising it** — if it renders inside a second, this is invented work. | | Bench uptime history + graphs | The v1 need is "is it dead", which one flag answers. A time series is a different product. | | Cross-booth search | No evidence of the need in the usage data. | | Per-viewer state (who has seen what) | The Booth has one viewer. Revisit if that stops being true. | | Auth | Standing non-goal. LAN/mesh-internal. Blur stays cosmetic and says so. | ## Post-v1, already committed - **SVOS theme retrofit by `design-dev`.** Runs as a parallel track, not a v1 gate: we own the information architecture (it is driven by the measurement above), design-dev owns the visual and interaction system. The handoff is a `/vor-ui` brief written against the landed v1 structure — the same shape `hamr-dev` and `pewpew-dev` used. ## Gate A proposed feature is on the v1 path or it is parked. **Default: parked.** When both are defensible, park it — same asymmetry as the patch-default in SemVer. Applies regardless of who proposed it.