# 2026-09-27 — One submit saves every ask on the page **The report.** Prime to infra-ops, relayed to booth-dev (althing thread `01M3JED397G1SZH7580PCXNVVA`): "submitting a question should go through and submit ALL answers. As it is, I go through, submit a question and it only submits the last one and clears out the top ones." **Confirmed against live data before any code, and it matched to the second.** `auk-audition`: three single-question picks, no anchors, so all three in the embed tail in `(created, id)` order. The access log: one POST at 15:02:23 (303) that saved `auk-emotion`, the LAST pick on the page; the reload; a POST at 15:02:27 that 400'd (the submit of a pick the reload had just blanked — the browser showed a raw `{"detail": ...}` page); then `auk-clone` at 15:04:45 and `auk-events` at 15:05:02, answered one at a time. infra-ops' reading of the code was right. `vastblue-site-visit-2026-09-29` carries 14 picks and would have hit it next. **The cause is not the server.** One pick = one `
` = one POST to `/answer`, and every POST did what `/answer` promises. The page offered one button per form and no way to send them together. Two surfaces, two machineries: - **Verbatim report (embed.js).** Plain form POST + 303 reload. The reload wiped every unsent pick. - **Booth pages (base.html's in-place script: marks page, lightbox verdict aside, review rail).** R2 C3 already CARRIED unsent picks across a swap, so they survived — but were never SAVED. And pressing a blank pick's submit was a 400, whose failure path reloads and wipes them all. **The shape (booth-dev's call; infra-ops said "the shape is your call").** Client-side, both surfaces, no server change: a submit on a pick form, while ANOTHER pick form on the page is dirty (a control differs from its server-rendered default), sends every dirty pick form — the pressed one only if dirty — one POST each to the unchanged `/answer` with `Accept: application/json` (the 204), serially in document order, a refusal stopping none of the rest. With no other dirty form, nothing changes: the browser's own POST (verbatim) or C3's one-form path (Booth pages). - **Rejected: a server batch endpoint + one page-level form.** It would also fix no-JS, but needs ask-scoped field names, a single `` element placed by embed.js, and rewires `formKey` identity on the Booth pages (one form holding many `ask` fields). Large blast radius for a no-JS verbatim path that does not exist (U3's named cost). Atomic all-or-nothing was also the WRONG semantics: one stale pick would refuse the rest — the 2026-09-09 partial-answer ruling's reasoning, one level up. - **Untouched picks are not re-sent**, the pressed one included: re-sending re-dates an answer nobody gave. - **A refusal never clears what was entered, on either surface** (the first cut had the Booth batch take C3's say-and-reload path; heid's panel caught it, see below). Verbatim: no reload while anything of ours is dirty; the pressed form's server-rendered, empty, hidden `.bk-ask-status` line says what did not save. Booth pages: refresh in place with only the forms the server took counted as sent, so the refused pick and any draft carry by identity; the status region names what did not save. C3's ONE-form failure path (step 4, say and reload) is untouched — not this change's surface, and it still loses drafts on a refusal (named, not fixed). **The heid bug-hunt panel (4/4 arms, thread `01M3JFDM1AWT2TCKS6E9NJM9G4`) was the gate that paid.** All four landed on the same blind spot: the batch reads every form AT THE PRESS but the page stays live through the flight, and every guard protecting that window (`sending`, `__busy`, `held`) SURVIVED mutation, because no test pressed or edited inside a flight. Six of its rows reproduced RED in a browser before any fix: a successful embed batch reloading away a pick made mid-flight (S1); the Booth batch's refusal reload (S2); saved forms staying dirty so a retry re-dated them (S3); in-flight marked on DOM nodes a queued swap replaced (S5 — now keyed by `formKey` identity via `flightKey`); a press in flight falling through to a native POST (embed) or a blank 400 (Booth) (S6). The trick that made them testable: hold every POST's reply 700ms in the CLIENT (`_HOLD_POSTS`, the `_HOLD_FIRST_REFRESH` pattern) so the window is wide enough to act in on purpose. S7 (no ask dedupe) rejected as unreachable; S8–S11 accepted with reasons in the fold reply (`01M3JHYKA0GSJG4F836J1Z6TJS`). **Lesson: a feature that opens an async window needs a test that acts inside it; a green suite of single presses says nothing about the window.** **Round two: a single cold Hulda arm on the FOLDED tree found six more** (thread `01M3JHYKCPXFA34XMRCEW4P3DJ`), all in how the fold's own rules interacted — and Heid's primed second voice, reading only the fold's delta, cleared two of them wrongly and retracted. Four reproduced RED: a sent pick changed mid-flight came back as the saved copy (#1); a note queued behind a flag carried back as a draft because "just sent" was a DOM-node test (#3); a batch whose page GET failed reloaded drafts away (#4); the embed reloaded over a refusal the operator had set back to its first value (#6). Fix: "just sent" = `flightKey` identity + the form's serialization at the press (`sentSet`), a batch never reloads, and a refusal blocks the embed reload on its own. #2 was a message fix (a withdrawn pick's input has nowhere to go); #5 accepted (needs a refused POST AND a failed GET; costs a re-date of identical content). **A cold read of the whole diff beat a primed read of the delta** — worth remembering before scoping a re-review to "just the fold". **Contracts amended in the same commit:** U3 "Submitting several asks at once" + INV-8; R2 C3 steps 3 and 3a. **Mutation tables:** `tests/mutations/u3_submit_all.toml` (15 rows) and `tests/mutations/r2_submit_all.toml` (11 rows), all proved; the r2_flow row anchored on `form.__busy` moved to `pending[flightKey(form)]`. **Tests:** 23 new browser tests plus two tightened (the author-form test now wears our id prefix; the one-form failure test now asserts the reload it names). Suite 928 → 951. The `[a3]` case reproduced the live log exactly (only a3 saved). **Not done, named:** no no-JS batch on the Booth pages (each plain form still saves one pick with scripts off — the no-JS path is unchanged, as asked); a verbatim batch that keeps the page leaves the saved picks' tags stale until a reload, and the message says so; C3's one-form refusal still reloads drafts away; Hulda #5 (above) accepted.