Reported by draupnir after the Booth v0.2.0 consumer note, and it is the higher-impact half of what it found: this file is autoloaded by every agent family on this box through the shared FLEETTOOLS bootstrap pointer, so it is the first thing a cold session reads about the Booth. Draupnir was a live specimen — it read this page earlier in the same session to post a booth, before the announcement landed, and only luck of scope kept it off the broken surfaces. Stale and now corrected: `booth asks` presented as the listing verb (it is a deprecated alias and emits JSON now, not the old table); `<stem>.ask.json` / `<stem>.answer.json` presented as the storage (one `.marks.json` per booth replaced them, and the answer sidecar 404s forever, so a remote session polling it is polling nothing); the `#ask-<stem>` anchor convention. Not stale, and left alone deliberately: the `data-booth-ask=` placeholder conventions. The inline placement engine survives v0.2 and is removed in a later unit, so those still work exactly as documented. Added: `booth marks` and `booth marks-import`, the marks.json HTTP read path, the partially-answered-counts-as-open change, and the 409-on-damaged-file behaviour.
4.6 KiB
The Booth — ephemeral media board + operator decisions
URL: http://10.100.10.50:8090/ · CLI: booth (on PATH on nh3-dev)
Storage: a booth is just a folder — ~/booth-data/<name>/ on nh3-dev. No upload API.
Wiped 24h after last activity unless kept. No auth (LAN/WG-internal), so never drop secrets in a booth.
Show the operator something
booth new <name>; booth add <name> <file>... # or just cp/rsync into ~/booth-data/<name>/
rsync -a ./out/ nh3-dev:booth-data/<name>/ # from another host
booth url <name> # -> http://10.100.10.50:8090/b/<name>/
booth keep <name> / booth unkeep <name> # exempt from / restore the 24h sweep
Auto-renders a gallery of images, webm+mp4 video and audio. A <file>.txt sidecar
becomes that item's caption (this is how you label an A/B pair). A folder's own
index.html is served verbatim — build a custom report when you want one.
Ask the operator to decide (no chat round-trip)
As of Booth v0.2.x an ask is one shape of a MARK — pick (this), note (free
text the operator volunteers), flag (the operator pointing at one item). All three
are read the same way, and the operator can now flag and annotate items, which he
previously had to relay in chat.
booth ask <booth> <id> "<prompt>" "<option A>" "<option B>" [...] [--no-notes]
booth answer <booth> <id> --wait # blocks until answered (default 1h)
booth answer <booth> <id> # non-blocking; exit 1 while unanswered
booth marks <booth> [--wait [SECS]] # EVERY mark, as JSON; --wait while any pick is open
booth marks-import <booth> # pull pre-v0.2 sidecars in; deletes nothing
booth asks <booth> survives as a deprecated alias for marks and now emits JSON,
not the old aligned table — if you parse those columns, that parse is broken.
Open picks are flagged on the Booth front page, so a waiting question is visible without pinging anyone. ⚠ A partially answered pick counts as OPEN (changed in v0.2.0; the old badge called it closed while the panel called it partial).
Storage is one file per booth: <booth>/.marks.json. The pre-v0.2
<stem>.ask.json / <stem>.answer.json sidecar pair is gone — <stem>.answer.json
will 404 forever, so a remote session polling it is polling nothing. Read over HTTP
instead, one call for the whole booth:
curl -sf http://10.100.10.50:8090/b/<booth>/marks.json # {booth, marks:[...], open:[ids]}
Re-answering overwrites — a mark is the CURRENT judgment, not a log. Radio only, no
multi-select. Page anchors are #mark-<id> (was #ask-<stem>), and
/b/<booth>/asks 308-redirects to /b/<booth>/marks.
⚠ If a booth's .marks.json is ever damaged, reads degrade to "no marks" so the page
still loads, but every WRITE refuses with a 409 — deliberately, because overwriting
would replace every judgment in that booth. Repair it by hand; nothing deletes it.
Put the ask WHERE THE ARTIFACT IS. If the booth has its own index.html, the
Booth substitutes placeholders: <div data-booth-ask="<stem>"></div> for the whole
ask, data-booth-ask="<stem>:<question-key>" for one question's radios,
data-booth-ask-submit="<stem>" for notes + submit. (<stem> is the pick's id;
these placeholders are unchanged in v0.2.x.) Per-question fragments bind to
one form via the HTML5 form= attribute, so a five-clip audition can put each radio
group under its own audio and still submit in a single POST.
⚠ Keep the placeholder OUTSIDE any grid/flex container or it becomes a cell in it.
⚠ A partial answer is legal and you must handle it. The operator can leave any
question blank; the sidecar then carries complete: false and the blank keys in
unanswered. Check complete before acting, and read a key in unanswered as
"not decided yet", never as "declined".
Standing convention — the link board
Every operator-facing link worth more than the next thirty seconds goes on the standing link board, in the same turn you mention it.
booth link <url> "<short description>" # on nh3-dev
ssh nh3-dev 'booth link <url> "<desc>"' # from anywhere else
booth links # read it
Still put the link in your reply — this is in addition to, not instead of. Links in chat drown in scrollback; the board is a kept booth rendered at the top of the Booth front page. Don't post noise: if the operator would not click it a week from now, it does not belong there.
Full schema + rules: ~/development/booth/README.md (gitea vh/booth; extracted from eshpfi 2026-09-21)