Patch, agent discretion. Bundles the pre-existing render-time 500 on the gallery and marks pages, closed at the hydration boundary, plus the `_safe_fragments` handler that could not survive the failure it was handling. 611 tests.
150 lines
9.3 KiB
Markdown
150 lines
9.3 KiB
Markdown
# The Booth — roadmap
|
|
|
|
Design: [`docs/design/information-architecture.md`](docs/design/information-architecture.md).
|
|
Current version: `0.6.1` (U1 through U6 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**~~ — **landed `87e2c53`, released `v0.5.0`** | 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**~~ — **landed `1c3ce5d`, released `v0.6.0`** | 69% link-board rot (re-measured: 178 booth rows + 8 bench re-posts) | 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 through U6 are landed.** **U7 is the last unit before the 1.0 cut** — its
|
|
only dependency was `{U3, U4, U5}` and that closed with U3.
|
|
|
|
⚠ **Before starting U7, read
|
|
`persistent-memory.d/2026-09-21-u7-section-premise-half-wrong.md`, and re-count
|
|
the booths first.** Half its premise is already known to be wrong — every booth
|
|
that actually needs navigation is FLAT — and the booth set churned again on
|
|
2026-09-22: the four large booths U7 was sized against (`pancake-v3-full` and
|
|
`pancake-v4-full` at 270 items, `sindra20-engines`, `sindra-finalists`) have all
|
|
been swept. The largest live booth is now `miranda-is` at 92 items, flat. Two of
|
|
23 booths have subfolders (`pewpew-ui-brief`, `dfa-concepts`) and **both are
|
|
reports** — the job where grid navigation matters least. Sections, one of U7's
|
|
four named components, buys close to nothing. The rail, the filters and the grid
|
|
keyboard are the unit.
|
|
|
|
**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) |
|
|
| the bench registry | `(state rank, name casefolded, id)` — live before promoted before retired, then alphabetical, with the id as a TOTAL tie-break so two benches sharing a name cannot swap (U6) |
|
|
| the link board's dead marker | not an order — a per-row stamp read from the existing `order_for_display` sequence, so marking cannot move a row (U6) |
|
|
| embed anchors in a verbatim report | **document order** — what `querySelectorAll` yields, so the author's markup decides (U3) |
|
|
| the embed tail (fragments the author did not place) | **payload order**, which is the marks order `(created, id)` — one rule, whether a fragment lands at an anchor or at the end (U3) |
|
|
| questions within a pick | declaration order, in the payload's `questions` LIST — carried by the format rather than by object-key insertion order (U3) |
|
|
|
|
U3's three rows are the first case where the rule binds across a language
|
|
boundary: the order is decided in Python and honoured in JavaScript, and a
|
|
browser test asserts it rather than a string assertion that could not see it.
|
|
|
|
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). **U6's bench listing is settled** — the row above.
|
|
Compare pairing is parked to v1.1 with compare mode itself, so U7 carries one
|
|
undecided rule, not two.
|
|
|
|
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 `<img loading="lazy">` 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.
|