Files
brain-of-reese/tests/unit/test_remove_confirm_modal.py
ducoterra 58e9d94cff
Build and Push Containers / build-and-push-app (push) Successful in 4m3s
Build and Push Containers / build-and-push-db (push) Successful in 18s
phase: 100_page_width_consistency
**Phase 100 — final verification pass: all green.**

- Verified the shipped CSS contract directly: `--chat-column: 72rem` in `:root`; 0 literal `max-width: 46rem`; no `@media (min-width: 1500px)` block; exactly 4 token-capped reading columns; tuning/theme/doc-edit shells cap-free, structurally `.sources-shell`; `mock_llm.py` diff is comment-only.
- Defect found & fixed (phase-93 suite): `test_theme_semantic_completion.py::test_reset_removes_tag_byte_identical` raced theme.js's post-PUT refetch — it asserted the `#bor-theme` tag was gone right after the result line, but tag removal lands in the reconcile after the re-fetch (failed ~1 in 5 runs after `test_chat_rag`, reproduced). Fixed with an auto-waiting `expect(...).to_have_count(0)` (settled-state wait, the file's existing house pattern). 6/6 clean on the repro loop after.
- Tests/lint/coverage: `uv run pytest --cov=app --cov-report=term-missing` → 2052 passed, **99%** on `app/`; `uv run ruff check .` + `uv run pyright` → 0 errors.
- E2E in isolation (all passed): `test_wide_desktop_column.py` 3 (the phase suite — chat==tuning==theme==RAG ±4px at 1280 & 1920, ≈1152px; shared ≈1152px; standalone doc ≈1112px; modal unchanged ≈1100px; 360px overflow-free), `test_ui_customization` 4, `test_admin_theme_tab` 5, `test_document_viewer` 7, `test_save_share_ux` 5, `test_sticky_navbar` 3, `test_markdown_tables` 6, `test_responsive_polish` 7, `test_chat_rag` 3, `test_theme_semantic_completion` 8.
- Completion criteria: ① measured 72rem everywhere (≥~1200px, ±4px) + full-width below + 360px clean — **PASS** (E2E); ② zero 46rem rules / no 1500px block / four token selectors — **PASS** (grep + unit pins); ③ B4 byte-identical no-op + mobile squeeze — **PASS** (theme + responsive suites green); ④ full suite / coverage / lint — **PASS**; ⑤ atomic commit — left to the harness per executor protocol (all changes in the working tree, uncommitted).
- Deviations: none from phase decisions; only change in this pass is the race fix above (test-only, behavior unchanged).
- Next pending phase: `98_sync_summary_visibility` (numeric order in `todo/`; also pending: 99, 101–105).
2026-09-12 13:37:19 -04:00

480 lines
23 KiB
Python

"""Unit: the remove confirmation modal on /git-sources.html (phase 69,
task 02).
Removal is a TOTAL removal (owner request 2026-09-02): the row, the
source's indexed documents (chunks + embeddings), and — for git clones
and uploaded archives — the files on the server's disk, all immediately
(task 01's ``DELETE /api/git-sources/{id}`` rewire). The page must
confirm that through a real in-app ``role="alertdialog"`` modal —
``window.confirm`` is retired — that names the source and states the
policy BEFORE the request goes out.
The browser behavior itself is E2E-gated by the phase-69 story suite
(``tests/e2e/test_source_removal_cleanup.py`` — task 03: focus,
Escape, ≥44px targets, the a11y interaction layer); like the other
frontend-adjacent unit files (``test_stale_ui_copy.py`` /
``test_summary_edit_ui.py`` house pattern), this module pins the
source-level contract a silent regression would break:
* ``window.confirm`` is GONE from the whole frontend (the only
native confirm the app ever shipped);
* the static ``#remove-confirm-dialog`` markup — ``role="alertdialog"``,
``aria-modal``, ``aria-labelledby``/``aria-describedby``, hidden by
default, the six child ids, the ``role="alert"`` error line, real
``type="button"`` buttons, the locked modal copy (verbatim);
* the JS lifecycle — ``openRemoveConfirm`` (textContent-only source
population with makeRow's ``value`` expression, error cleared, focus
on Cancel, the trigger recorded), cancel = Escape / Cancel button /
backdrop (no request; focus returns to the trigger; a no-op while a
DELETE is in flight), ``confirmRemove`` (§7.4 in-flight state: both
buttons disable + "Removing…", success → close/reload/announce —
the removal confirmation is the LAST announcement, non-2xx → the
in-modal alert line + dialog stays open, network →
the fixed reachable? line, re-enable in the finally);
* the stale "prunes on the next sync" removal copy is GONE from
``git-sources.js`` + the shell (phase 76 task 02 folded git-
sources.html into index.html's Sources view); the new hint copy is
PRESENT (the README pins are task 03's);
* styles.css — the modal classes on the house dark-tech palette
(phase-08 tokens only, no CDN, no blur), ≥44px buttons, the
``[hidden]`` override, the err-token destructive pair.
"""
from __future__ import annotations
import re
from pathlib import Path
FRONTEND = Path(__file__).resolve().parents[2] / "frontend"
# Phase 76 (task 02): git-sources.html is folded into the ONE-document
# shell — the dialog markup now lives in the Sources view section of
# index.html (moved verbatim, ids unchanged).
SHELL_HTML = FRONTEND / "index.html"
JS = FRONTEND / "assets" / "git-sources.js"
CSS = FRONTEND / "assets" / "styles.css"
#: The locked modal copy (phase 69, 00_phase.md) — one fixed paragraph
#: for both kinds (the UI cannot tell an upload from the owner's own
#: directory; naming the source above it makes the target
#: unambiguous). Pinned verbatim against the normalized text.
MODAL_COPY = (
"This permanently removes the source entry, all of its indexed "
"documents from the knowledge base, and — for git clones and "
"uploaded archives — the files on the server's disk. Files in "
"your own local directories are never touched. This cannot be undone."
)
#: The new hint-box contract (phase 69): removal is immediate and
#: total; the modal spells it out; the Sync button mirrors the
#: remaining sources (upstream churn is still pruned on that run).
HINT_TOTAL_REMOVAL = "Removing a source is a total removal, done immediately"
HINT_MODAL_SPELLS_OUT = (
"the confirmation modal spells out exactly what will be deleted"
)
HINT_FOREVER_SAFE = "files in your own local directories are never touched"
#: The success announce (the existing #git-sources-announcer live
#: region).
ANNOUNCE_OK = "Source removed — its files and index entries were cleaned up."
#: The retired confirm copy (task 02 stale-copy pins).
OLD_STAY_INDEXED = "stays indexed until the next sync"
# "prunes/pruned … on the next sync" in any shape (the old hint +
# confirm message).
OLD_NEXT_SYNC = re.compile(r"prun\w+[^.]{0,120}?next sync")
def _text(path: Path) -> str:
return path.read_text(encoding="utf-8")
def _norm(text: str) -> str:
"""Collapse whitespace runs — the markup wraps long lines, so the
locked copy is pinned against the normalized text."""
return re.sub(r"\s+", " ", text).strip()
def _js() -> str:
return _text(JS)
def _css() -> str:
return _text(CSS)
def _fn(js: str, name: str) -> str:
"""The source of a (possibly async) top-level function via
balanced-brace counting (the test_summary_edit_ui.py helper)."""
for prefix in ("async function ", "function "):
start = js.find(f"{prefix}{name}(")
if start != -1:
depth = 0
for i in range(js.find("{", start), len(js)):
if js[i] == "{":
depth += 1
elif js[i] == "}":
depth -= 1
if depth == 0:
return js[start : i + 1]
raise AssertionError(f"unbalanced braces in {name}()")
raise AssertionError(f"{name}() must exist in git-sources.js")
def _element_block(html: str, id_attr: str, tag: str = "div") -> str:
"""The <tag … id=…> element's full markup (a balanced-tag walk —
the dialog nests the backdrop / panel / actions divs; the hint is
a <p>)."""
marker = f'id="{id_attr}"'
i = html.find(marker)
assert i != -1, f"missing id={id_attr} in the shell's Sources view"
opens = [m.start() for m in re.finditer(rf"<{tag}\b", html[:i])]
assert opens, f"no <{tag}> owns id={id_attr}"
open_i = opens[-1]
depth = 0
for m in re.finditer(rf"<{tag}\b[^>]*>|</{tag}>", html[open_i :]):
if m.group(0).startswith(f"</{tag}>"):
depth -= 1
else:
depth += 1
if depth == 0:
return html[open_i : open_i + m.end()]
raise AssertionError(f"unbalanced <{tag}> for id={id_attr}")
# ---------- window.confirm is gone (the whole frontend) ----------
def test_window_confirm_is_gone_from_the_whole_frontend() -> None:
"""``window.confirm`` — the only native confirm the app ever
shipped — is absent from EVERY frontend file (markup, scripts,
styles — comments included: the retirement note must not keep the
literal)."""
for path in sorted(FRONTEND.rglob("*")):
if path.is_file():
assert "window.confirm" not in path.read_text(encoding="utf-8"), (
f"window.confirm survived in {path.relative_to(FRONTEND)}"
)
# ---------- the static dialog markup ----------
def test_dialog_markup_is_the_locked_alertdialog() -> None:
"""#remove-confirm-dialog: role="alertdialog" + aria-modal + the
labelled/describedby pair, hidden by default, inside the manager
(#git-sources-content — the static-markup convention, stable E2E
selectors); all six child ids present; the error line is
role="alert"; both buttons are real type="button"; the title is
the locked h2; the modal copy is the locked paragraph verbatim."""
html = _text(SHELL_HTML)
frag = _element_block(html, "remove-confirm-dialog")
open_tag = frag[: frag.find(">") + 1]
assert 'role="alertdialog"' in open_tag
assert 'aria-modal="true"' in open_tag
assert 'aria-labelledby="remove-confirm-title"' in open_tag
assert 'aria-describedby="remove-confirm-copy"' in open_tag
assert "hidden" in open_tag, "the dialog ships hidden"
# Static markup INSIDE the manager (the #git-sources-hint / gate
# convention) — E2E can wait for the content to be revealed.
assert html.find('id="git-sources-content"') < html.find("remove-confirm-dialog")
for child in (
"remove-confirm-title",
"remove-confirm-source",
"remove-confirm-copy",
"remove-confirm-error",
"remove-confirm-cancel",
"remove-confirm-remove",
):
assert f'id="{child}"' in frag, f"missing #{child} in the dialog"
title = re.search(r"<h2[^>]*id=\"remove-confirm-title\"[^>]*>(.*?)</h2>", frag, re.S)
assert title and _norm(title.group(1)) == "Remove this source?"
err = re.search(r"<p[^>]*id=\"remove-confirm-error\"[^>]*>", frag)
assert err and 'role="alert"' in err.group(0), "the in-modal error is role=alert"
for btn in ("remove-confirm-cancel", "remove-confirm-remove"):
m = re.search(rf"<button[^>]*id=\"{btn}\"[^>]*>", frag)
assert m and 'type="button"' in m.group(0), f"#{btn} is a real type=button"
cancel = re.search(r'<button[^>]*id="remove-confirm-cancel"[^>]*>(.*?)</button>', frag, re.S)
remove = re.search(r'<button[^>]*id="remove-confirm-remove"[^>]*>(.*?)</button>', frag, re.S)
assert cancel and _norm(cancel.group(1)) == "Cancel"
assert remove and _norm(remove.group(1)) == "Remove source"
assert _norm(MODAL_COPY) in _norm(frag), "the locked modal copy, verbatim"
# The source value is a <code> (mono) — textContent-only in JS.
src = re.search(r"<code[^>]*id=\"remove-confirm-source\"[^>]*>", frag)
assert src, "#remove-confirm-source is a <code>"
# ---------- the JS lifecycle ----------
def test_open_populates_source_via_text_content_and_focuses_cancel() -> None:
"""openRemoveConfirm(s, triggerBtn): the source value is
textContent ONLY (never innerHTML — the credential-masking
discipline, phase 32) with makeRow's exact ``value`` expression
(``s.path ?? s.url`` for local rows, ``s.url`` for git); the error
line clears; the dialog unhides; the trigger is recorded; the
keydown handler attaches; and focus lands on Cancel — the safe
default for a destructive action (AFTER the unhide)."""
body = _fn(_js(), "openRemoveConfirm")
assert "s.kind === \"local\"" in body, "the kind-typed value branch"
assert (
"removeSourceEl.textContent = isLocal ? s.path ?? s.url : s.url" in body
), "the same `value` expression makeRow uses, via textContent"
assert "removeSourceEl.innerHTML" not in _js(), "XSS contract: textContent only"
assert "removeError.hidden = true" in body, "a new attempt starts clean"
assert "removeTriggerBtn = triggerBtn" in body, "the trigger is recorded"
trigger_i = body.find("removeTriggerBtn = triggerBtn")
unhide_i = body.find("removeDialog.hidden = false")
attach_i = body.find('document.addEventListener("keydown", onRemoveDialogKeydown)')
focus_i = body.find("removeCancelBtn.focus()")
assert -1 < trigger_i < unhide_i < attach_i < focus_i, (
"record the trigger → unhide → attach keydown → focus Cancel"
)
def test_row_button_opens_the_modal_and_the_row_error_span_is_retired() -> None:
"""makeRow's per-row Remove button opens the modal
(openRemoveConfirm(s, btn)) — no confirm call, no per-row error
span (the modal carries the in-flight error line); the retired
.git-source-row-error class is gone from the JS AND the CSS."""
js = _js()
make = _fn(js, "makeRow")
assert "openRemoveConfirm(s, btn)" in make, "the row's Remove opens the modal"
assert "removeSource(" not in js, "the window.confirm-era flow is gone"
assert "git-source-row-error" not in js
assert "git-source-row-error" not in _css(), "the dead per-row error CSS is retired"
def test_cancel_paths_close_without_a_request() -> None:
"""Cancel (Cancel button / Escape / backdrop) closes as cancel:
the dialog hides, the error line clears, the buttons reset
("Remove source"), the keydown handler detaches, and focus
RETURNS to the recorded trigger — and the cancel path never
fetches. A cancel while a DELETE is in flight is a no-op (no
half-cancel of an in-progress server-side removal)."""
js = _js()
cancel = _fn(js, "cancelRemoveConfirm")
guard_i = cancel.find("if (removeInFlight) return")
close_i = cancel.find("closeRemoveConfirm()")
assert -1 < guard_i < close_i, "the in-flight guard precedes the close"
assert "fetch" not in cancel, "cancel never sends a request"
close = _fn(js, "closeRemoveConfirm")
hide_i = close.find("removeDialog.hidden = true")
clear_i = close.find("removeError.hidden = true")
reset_i = close.find('removeRemoveBtn.textContent = "Remove source"')
detach_i = close.find('document.removeEventListener("keydown", onRemoveDialogKeydown)')
save_i = close.find("const trigger = removeTriggerBtn")
null_i = close.find("removeTriggerBtn = null")
focus_i = close.find("trigger.focus()")
assert -1 < hide_i < clear_i < reset_i < detach_i < save_i < null_i < focus_i, (
"hide → clear → reset → detach → save trigger → focus return"
)
assert "removeCancelBtn.disabled = false" in close
assert "removeRemoveBtn.disabled = false" in close
def test_escape_and_backdrop_and_cancel_button_all_cancel() -> None:
"""While open (handler attached on document in openRemoveConfirm,
detached in closeRemoveConfirm): Escape → preventDefault +
cancelRemoveConfirm; the dim backdrop AND the Cancel button wire
to cancelRemoveConfirm (only "Remove source" wires to
confirmRemove). Tab/Shift+Tab stay inside the two-button modal
(aria-modal honored for keyboard users)."""
js = _js()
keydown = _fn(js, "onRemoveDialogKeydown")
esc_i = keydown.find('e.key === "Escape"')
prevent_i = keydown.find("e.preventDefault()", esc_i)
cancel_i = keydown.find("cancelRemoveConfirm()", esc_i)
assert -1 < esc_i < prevent_i < cancel_i, "Escape: prevent + cancel"
assert 'e.key === "Tab"' in keydown, "the two-button focus cycle"
assert 'removeCancelBtn.addEventListener("click", cancelRemoveConfirm)' in js
assert 'removeBackdrop.addEventListener("click", cancelRemoveConfirm)' in js
assert 'removeRemoveBtn.addEventListener("click", confirmRemove)' in js
def test_confirm_runs_the_inflight_never_stale_lifecycle() -> None:
"""confirmRemove: the §7.4 in-flight state BEFORE the fetch —
both buttons disable + the confirm relabels "Removing…"; one
DELETE /api/git-sources/{id}. Success (204): close (focus return)
→ loadSources → announce (the removal confirmation is the LAST
announcement — the reload's "N sources listed." must not
overwrite it). Non-2xx: the in-modal role=alert line
(apiDetail, 422 shape-aware) and the dialog STAYS open (no
closeRemoveConfirm in the failure slice). Network failure: the
fixed reachable? line. The finally re-enables BOTH buttons +
relabels "Remove source" — never stale on any outcome."""
body = _fn(_js(), "confirmRemove")
guard_i = body.find("if (!removingId || removeInFlight) return")
inflight_i = body.find("removeInFlight = true")
clear_i = body.find("removeError.hidden = true")
dis_c = body.find("removeCancelBtn.disabled = true")
dis_r = body.find("removeRemoveBtn.disabled = true")
label_i = body.find('"Removing…"')
fetch_i = body.find("fetch(`/api/git-sources/${encodeURIComponent(removingId)}`")
method_i = body.find('method: "DELETE"', fetch_i)
assert -1 < guard_i < inflight_i < clear_i < dis_c < dis_r < label_i < fetch_i < method_i, (
"guard → in-flight → clear error → disable both + label → DELETE"
)
# Success: close → reload → announce (the exact order, the exact
# announce string) — the removal confirmation is the LAST
# announcement: the reload's "N sources listed." must not
# overwrite it (phase 69 task 03's E2E pins the success line on
# the announcer after a real removal).
ok_i = body.find("if (r.ok)")
close_i = body.find("closeRemoveConfirm()", ok_i)
reload_i = body.find("await loadSources()", close_i)
announce_i = body.find(f'announce("{ANNOUNCE_OK}")', reload_i)
assert -1 < ok_i < close_i < reload_i < announce_i
assert body.count("closeRemoveConfirm()") == 1, (
"only the success path closes — failures stay open for one retry"
)
# Non-2xx: the in-modal alert line, dialog stays open.
nonok_i = body.find("Could not remove the source — try again.")
catch_i = body.find("} catch {")
assert -1 < nonok_i < catch_i
assert "await apiDetail(r," in body[body.find("if (r.ok)"):catch_i], (
"the server detail is apiDetail-extracted (422 shape-aware)"
)
failure_slice = body[body.rfind("// non-2xx", 0, catch_i):catch_i]
assert "closeRemoveConfirm" not in failure_slice, "failure keeps the dialog open"
# Network: the fixed reachable? line.
net_i = body.find("Could not remove the source — is the app reachable?", catch_i)
assert -1 < net_i < body.find("finally"), "the network copy lands in the catch"
# Never stale: the finally re-enables BOTH buttons + relabels.
fin_i = body.find("finally")
fin = body[fin_i:]
assert "removeInFlight = false" in fin
assert "removeCancelBtn.disabled = false" in fin
assert "removeRemoveBtn.disabled = false" in fin
assert 'removeRemoveBtn.textContent = "Remove source"' in fin
# ---------- the stale copy is gone; the new hint is present ----------
def test_stale_next_sync_removal_copy_is_gone() -> None:
"""The phase-35 "prunes on the next sync" removal contract is
superseded: the retired confirm copy AND any 'prune(s/d) … next
sync' shape are absent from git-sources.js + the shell (code AND
comments — the docstring copy moved with the flow). The README
pins are task 03's."""
for path in (JS, SHELL_HTML):
raw = _text(path)
norm = _norm(raw)
assert OLD_STAY_INDEXED not in raw, (
f"retired confirm copy still in {path.name}"
)
m = OLD_NEXT_SYNC.search(norm)
assert m is None, f"'next sync' removal copy survived in {path.name}: {m.group(0)!r}"
def test_new_hint_copy_is_present_in_the_html() -> None:
"""The #git-sources-hint carries the new contract: removal is a
total removal, done immediately (the modal spells it out; foreign
local directories are never touched), and the Sync button still
mirrors the remaining sources (upstream churn is pruned on that
run — not 'on the next sync')."""
hint = _norm(_element_block(_text(SHELL_HTML), "git-sources-hint", tag="p"))
for frag in (HINT_TOTAL_REMOVAL, HINT_MODAL_SPELLS_OUT, HINT_FOREVER_SAFE):
assert frag in hint, f"the new hint copy is missing: {frag!r}"
assert "pruned on that run" in hint, "the Sync-mirror clause (upstream churn)"
assert "next sync" not in hint, "no 'next sync' removal claim in the hint"
def test_module_docstring_carries_the_new_contract() -> None:
"""The git-sources.js module docstring's remove bullet + scope
boundary moved with the flow: the modal names the source + states
the policy, and removal performs the FULL cleanup server-side
(row + index + app-managed files) — with the navigating-away
note (the row + index commit first; an interrupted file step
leaves an inert orphan dir)."""
doc = _js().split("*/", 2)[0] # the module docstring (first block)
for frag in (
'role="alertdialog"',
"textContent ONLY",
'relabels "Removing…"',
"not recommended",
"inert orphan",
"FULL cleanup server-side",
"foreign local directories are never touched",
):
assert frag in doc, f"the module docstring lost: {frag!r}"
# ---------- styles.css ----------
def test_modal_css_classes_present_and_house_tokens_only() -> None:
"""styles.css carries the modal class family on the house
dark-tech palette (phase-08 tokens): the overlay + backdrop +
panel, the title / source / copy / error / actions / two-button
chrome; the [hidden] override (the documented, testable
contract); the global 3px :focus-visible outline is NOT
suppressed; no CDN; no blur (the phase-08 no-blur perf anchor)."""
css = _css()
for cls in (
".remove-confirm",
".remove-confirm-backdrop",
".remove-confirm-panel",
".remove-confirm-title",
".remove-confirm-source",
".remove-confirm-copy",
".remove-confirm-error",
".remove-confirm-actions",
".remove-confirm-btn",
".remove-confirm-cancel",
".remove-confirm-remove",
):
assert f"{cls} " in css or f"{cls}." in css or f"{cls}[" in css, (
f"styles.css must style {cls}"
)
hidden = css.find(".remove-confirm[hidden]")
assert hidden != -1 and "display: none" in css[hidden : hidden + 60], (
"the hidden attr must beat the display rule"
)
assert ":focus-visible {" in css and "outline: 3px solid var(--brand)" in css
# No blur (the phase-08 no-blur perf anchor) — the backdrop rule
# itself must not carry backdrop-filter.
backdrop = css[css.find(".remove-confirm-backdrop {") :]
backdrop = backdrop[: backdrop.find("\n}")]
# Comments stripped — the note "no backdrop-filter (no-blur)" must
# not trip the pin; only a real declaration may.
backdrop = re.sub(r"/\*.*?\*/", "", backdrop, flags=re.S)
assert "backdrop-filter" not in backdrop, "no blur (phase-08 perf anchor)"
assert "url(http" not in css and "@import url(" not in css, (
"no CDN (AGENTS.md rule 6)"
)
def test_modal_css_targets_and_contrast_pairs() -> None:
"""The WCAG 2.1 AA basics in CSS: both buttons >=44px; the
destructive button rides the err token family (err-ink on err-bg
9.3:1, the err-line border — the .tuning-delete / .steering-delete
convention; the hover inverts to --bg on --err-line, 5.2:1);
Cancel is the ghost ink-soft family (5.1:1 on --surface); the
error line is the err pair; the panel caps at the chat-column
width (the --chat-column token — 72rem, owner instruction
2026-09-12) or the viewport."""
css = _css()
btn = css[css.find(".remove-confirm-btn {"):]
btn = btn[: btn.find("\n}")]
assert "min-height: 44px" in btn and "min-width: 44px" in btn
remove = css[css.find(".remove-confirm-remove {"):]
remove = remove[: remove.find("\n}")]
for prop in ("var(--err-bg)", "var(--err-ink)", "var(--err-line)"):
assert prop in remove, f"the destructive pair must keep {prop}"
hover = css[css.find(".remove-confirm-remove:hover:not(:disabled) {"):]
hover = hover[: hover.find("\n}")]
assert "var(--err-line)" in hover and "var(--bg)" in hover, (
"the hover inversion: dark --bg on --err-line (5.2:1)"
)
cancel = css[css.find(".remove-confirm-cancel {"):]
cancel = cancel[: cancel.find("\n}")]
assert "var(--ink-soft)" in cancel and "transparent" in cancel
err = css[css.find(".remove-confirm-error {"):]
err = err[: err.find("\n}")]
assert "var(--err-ink)" in err and "var(--err-bg)" in err
panel = css[css.find(".remove-confirm-panel {"):]
panel = panel[: panel.find("\n}")]
assert "min(var(--chat-column)" in panel, (
"the chat-column cap via the token (or the viewport)"
)