A booth that ships its own index.html was served through ten regular
expressions applied to markup the Booth did not write: six in
wrap_verbatim_html hunting for somewhere to hang a favicon and a chip, four
in booth/inline.py substituting rendered ask markup into the author's own
tags. Both worked. Both were the most fragile thing in the service, on the
path the operator uses most.
The whole class is replaced by a declared seam. A report carries one line —
<script src="/_booth/embed.js" defer></script> — and the chrome mounts
through DOM APIs. What the server does to author HTML is now, in full:
return html if declares_embed(html) else html + EMBED_SCRIPT_TAG
Two substring tests and a concatenation. Both of the old wrapper's hard
constraints stop existing rather than being satisfied more carefully:
nothing can displace a leading doctype into quirks mode and nothing can push
the charset meta out of its detection window, because nothing in front of
them ever moves. A page that declares the seam is served exactly as written.
Fragments are still rendered by the _ask_inline.html macros and handed over
GET /b/<name>/embed.json; embed.js places them and decides nothing. Openness
comes from open_marks, order from (created, id), questions in declaration
order. A single-question pick normalizes to key None, so the payload carries
questions as a list rather than an object — keying by name would serialize
that as the string "null".
Placement is an anchor fill, not a replacement: el.insertAdjacentHTML(
'beforeend'), so an author's wrapper and its contents survive. The regex it
replaces was eating the opening tag of dfa-concepts' styled .ask blocks and
orphaning their headings, live, unreported.
data-booth-mark is canonical; data-booth-ask stays a kept alias because two
live reports use it. The comment placeholders are dropped — no users.
Declared cost: the verbatim path now needs JavaScript. The never-invisible
guarantee holds through the index badge and /b/<name>/marks, both of which
render server-side.
Deleted: booth/inline.py entire, wrap_verbatim_html and its six patterns,
_BACK_CHIP, asks_chip, inject_asks, FAVICON_LINK, the styles() macro.
Tests 410 -> 434. tests/test_embed_browser.py drives a real Chromium: the
placement algorithm and the form= binding of a scattered multi-question form
cannot be observed any other way, and that binding was measured rather than
assumed (N=3 per condition, with a form-first positive control and a
points-at-nothing negative control).
Contract: docs/contracts/u3_declared_embed_seam.contract.md, with the
in-session seam review and the cold contract panel both recorded. Two of the
panel's findings were code fixes: a vacuous INV-3 falsifier that a renamed
regex walked straight through, and a bare-substring seam detection that read
a report merely quoting the path as declaring it and silently served it with
no chrome.
3.7 KiB
The browser became a test surface, and the version bound is the foot-gun
2026-09-22 · booth
U3 moved load-bearing logic out of Python and into JavaScript: which fragment
lands at which anchor, what gets appended, and whether a <form> scattered down
a report still owns the controls pointing at it. The Python suite is blind to
every one of those. Shipping U3 with only payload-shape tests would have
deleted ~10 real tests and replaced them with assertions that cannot see the
thing the operator actually depends on.
So tests/test_embed_browser.py drives a real Chromium against a real uvicorn
on an ephemeral port. 12 tests. It found nothing on the first run — but the
probe that preceded it settled a design question no amount of spec-reading
would have.
The probe, and why it had controls
Question: if a control carrying form="F" is inserted into the DOM before
<form id="F"> exists, does it become that form's control? The HTML spec resets
form owner on insertion and on the form attribute changing — it does NOT list
"a matching form was inserted later". The U3 design inserts fragments in visual
order, so this happens routinely.
Four conditions, N=3 each, in Chromium 151 headless:
| condition | input.form?.id |
|---|---|
| A — form inserted first (positive control) | F, F, F |
| B — control inserted first (the question) | F, F, F |
C — form="NOPE", no such form (negative control) |
null, null, null |
| D — remove and re-set the attribute (the proposed fix) | F, F, F |
The positive control proves the instrument can see association at all; the negative proves it is not manufacturing it. Without both, B's answer means nothing — that is the whole lesson of 2026-09-22-vacuous-falsifiers applied before the code instead of after.
The answer is: Chromium re-resolves it, so the fix is unnecessary there. The fix shipped anyway. Sensitivity floor: ONE ENGINE. The operator's own browser was not measured, the failure mode is a form that looks filled in and POSTs a 400, and the guard is three lines. The measurement says "not needed here"; it does not say "not needed".
The foot-gun, which bit before the tests were written
Browsers are box-wide in /opt/ms-playwright with
PLAYWRIGHT_BROWSERS_PATH wired globally — there is no per-project
playwright install. Each playwright release pins one Chromium revision, and
a release wanting a revision the shared store lacks dies with:
Executable doesn't exist at /opt/ms-playwright/chromium_headless_shell-1243/…
That is not a missing-dependency error and it does not name the real problem.
The store had 1223 / 1228 / 1234; playwright 1.63 wanted 1243. The mapping:
1.60 -> 1223 1.61 -> 1228 1.62 -> 1234 1.63 -> 1243
Hence playwright>=1.60,<1.63 in pyproject.toml, with the upper bound as the
point and the reason in a comment beside it. A bare playwright would break
the suite on the next resolve, opaquely.
The hermeticity trade, and how it is paid
A browser layer makes the suite non-hermetic — it can go red for an environment
reason. tests/test_embed_browser.py therefore skips, never fails, when
playwright or a usable browser is missing (pytest.importorskip, plus a
pytest.skip on any launch failure). pytest -q stays green anywhere; the
browser layer is purely additive.
⚠ The failure mode of that choice: if those 12 tests start SKIPPING on this box, U3's placement logic is untested and the suite still says green. If the count drops from 431, check the skip reason before anything else — the pinned bound has probably drifted past the shared store.
Related: 2026-09-22-u3-declared-embed-seam-landed.