feat: phases 77–80 — navbar view refresh, static background, API tokens, history suggestion chips
Build and Push Containers / build-and-push-app (push) Successful in 1m45s
Build and Push Containers / build-and-push-db (push) Successful in 13s

Single consolidated commit for four completed, validated phases (77, 78,
79, 80). The pipeline run left all work uncommitted because the harness
commits only with PHASE_COMMIT=1 while child executors are forbidden from
committing; the phases themselves all passed validation and moved to
.agents/phases/complete/.

Phase 77 — navbar view refresh
- router.js dispatches bor:view-refresh on re-show / active re-click /
  popstate (gated on wasMounted; first show and boot exempt)
- History / RAG / Sources / Tuning re-fetch on refresh (admin branch);
  Chat deliberately excluded (stream survival)
- History "Refresh" button (admin-only, in-flight disable + status line)
- New story suite tests/e2e/test_navbar_refresh.py (7 tests)

Phase 78 — static background
- Removed the animated glow layers; static 44px grid over the flat --bg
  canvas; default and reduced-motion renders byte-identical
- Updated background/theme E2E suites; removed bg-glow test pins

Phase 79 — API tokens
- api_tokens model + migration 0012; hash-only token service
- Admin tokens API + Tokens admin view; POST /api/token-auth;
  live-revoking require_user on chat / suggestions / document content
- Frontend token gate with localStorage cache; anonymous E2E suites
  migrated to token login
- New story suite tests/e2e/test_api_tokens.py (9 tests)

Phase 80 — history suggestion chips
- last_questions() endpoint with SEED fallback; startNewChat() refetch
- Seed-semantics docs (config.py, .env.example, README)
- Integration state matrix + E2E suite rewritten to the 4 chip states

Also included: phase-76 report artifacts and the repo restore-test-db
skill (previously untracked), scripts/* ruff fixes from phase 77.

Final gate state (phase 80 final pass, covers everything above):
- uv run pytest --cov=app → 1637 passed, 0 failed, app/ coverage 99%
- uv run ruff check . && uv run pyright → clean, 0 errors
- Per-phase story E2E suites green in isolation
This commit is contained in:
2026-09-07 12:39:01 -04:00
parent 495d042a98
commit 7fce6572d0
215 changed files with 10142 additions and 1643 deletions
+513 -3
View File
@@ -36,6 +36,45 @@ active stamps) are gone with the four folded view documents, so
Chat link), and header.js carries NO ``is-active`` write: the whoami
auth gate + the mobile hamburger are its only nav responsibilities,
and the router is the SINGLE runtime writer of the active state.
Phase 77 task 01 (the re-show refresh hook): a user-initiated re-show
of an ALREADY-MOUNTED view dispatches ``bor:view-refresh`` on the
view's section — gated on the pre-mount ``wasMounted`` capture, so the
first show (the mount) and boot never fire it (the mount's own load is
the first fetch); a re-click of the active view's own nav link
dispatches the event instead of a bare return (no ``pushState`` — the
URL is already that view's path); the History view listens (armed only
in the admin branch, after the whoami gate — anonymous never fetches).
Phase 77 task 02 (the other data views join the refresh): RAG
(``sources.js``), Sources (``git-sources.js``) and Tuning
(``tuning.js``) each listen for ``bor:view-refresh`` on their root and
re-run their existing load (armed only in the admin branch, after the
whoami gate — the same gate guard as History). ``sources.js``'s
``loadDocs`` clears the tbody's rows at the TOP (before the fetch —
the History pattern), so a refresh from a populated list into an empty
result leaves no ghost rows. The Chat view (``app.js``) does NOT
listen — the negative pin: the in-flight SSE stream and the local
conversation must survive every switch (the phase-76 contract), so
the exclusion is a contract, not an oversight.
Phase 77 task 03 (the explicit History refresh control, TODO.md L3):
the History page-head becomes a flex row (scoped to ``#view-history``
— the other four views' page-heads are untouched) carrying the
``#history-refresh`` button (``aria-label="Refresh saved chats"``,
the aria-hidden house refresh glyph + the visible "Refresh" label —
the phase-46 auth-link convention) OUTSIDE the table wrap (reachable
while the empty state shows). history.js binds it in the admin branch
only (the anonymous branch hides it — no dead control beside the
gate); the click disables the button (no double-fire in flight),
reruns the re-entrant ``loadChats()`` and re-enables on success AND
failure (the finally). The outcome lands in ``#history-status``:
``Saved chats refreshed.`` on success (a 0-row fetch is a success) —
and the failure lines now live INSIDE ``loadChats`` (the house copy:
"is the app reachable?" / "try again."), so every caller of a failed
load sees it (the §7.4 never-stale contract). styles.css reuses the
``.new-chat-btn`` visual language (brand pill, ≥44px, hover,
:disabled) and goes icon-only below 640px.
"""
from __future__ import annotations
@@ -64,8 +103,9 @@ def _html() -> str:
def test_view_map_covers_the_shell_paths() -> None:
"""The VIEW map is pathname → view name: the shell's own two URLs
("/" and "/index.html") are the chat view, plus one entry per
folded view (tasks 01–03: tuning, rag, git-sources, history —
all four non-chat navbar views are in)."""
folded view (tasks 01–03: tuning, rag, git-sources, history;
phase 79 task 06: tokens — all five non-chat navbar views are
in)."""
js = _js()
view_start = js.find("const VIEW = {")
assert view_start != -1, "the VIEW map must exist"
@@ -80,8 +120,11 @@ def test_view_map_covers_the_shell_paths() -> None:
assert '"/history.html": "history"' in view_body, (
"task 03 folds the History view into the shell"
)
assert '"/tokens.html": "tokens"' in view_body, (
"phase 79 task 06 folds the Tokens view into the shell"
)
# The view names are the #view-<name> section slugs in index.html.
for name in ("chat", "tuning", "history"):
for name in ("chat", "tuning", "history", "tokens"):
assert f'id="view-{name}"' in _html(), f"missing the #view-{name} section"
@@ -172,6 +215,9 @@ def test_only_non_chat_views_have_lazy_modules() -> None:
assert 'history: () => import("./history.js")' in mods_body, (
"the History view module is lazy-imported on first show"
)
assert 'tokens: () => import("./tokens.js")' in mods_body, (
"the Tokens view module is lazy-imported on first show"
)
assert '"chat"' not in mods_body, "the chat view has no lazy module"
assert 'import("./app.js")' not in js, "app.js must never be lazy-imported"
@@ -227,6 +273,8 @@ def test_router_writes_active_state_title_and_meta() -> None:
assert "Manage the global tuning notes that steer every Brain of Reese answer." in js
assert 'history: "Saved chats · Brain of Reese"' in js
assert "Saved chats — every conversation is saved automatically, one click back." in js
assert 'tokens: "Access tokens · Brain of Reese"' in js
assert "Generate and revoke the API tokens that let people use the app." in js
# The brand composition (phase 39's window.BOR_BRAND, read at
# write time — never a hardcoded stamp).
assert 'window.BOR_BRAND || "Brain of Reese"' in js
@@ -291,6 +339,15 @@ def test_shell_markup_has_one_main_two_views_and_chat_only_active() -> None:
tuning_link = tuning_match.group(0)
assert "hidden" in tuning_link, "#nav-tuning ships hidden (admin-only)"
assert "is-active" not in tuning_link, "no static active stamp on the Tuning link"
# The Tokens nav link (phase 79 task 06) ships hidden (admin-only)
# and UNstamped too — the router is the single writer of the active
# state, and a token user (role "user") must never see the link
# (header.js reveals it for admin only).
tokens_match = re.search(r'<a[^>]*id="nav-tokens"[^>]*>', html)
assert tokens_match, "the shell must carry the #nav-tokens nav link"
tokens_link = tokens_match.group(0)
assert "hidden" in tokens_link, "#nav-tokens ships hidden (admin-only)"
assert "is-active" not in tokens_link, "no static active stamp on the Tokens link"
# ---------- phase 76 task 04: the header is shell-owned ----------
@@ -352,3 +409,456 @@ def test_boot_order_is_brand_app_router() -> None:
assert 'type="module"' in router_tag, "router.js is an ES module"
# No CDN: every asset reference is local (AGENTS.md rule 6).
assert 'src="http' not in html and 'href="http' not in html
# ---------- phase 77 task 01: the re-show refresh hook ----------
def test_reshow_dispatches_view_refresh_gated_on_pre_mount_capture() -> None:
"""Phase 77: a user-initiated re-show of an already-mounted view
dispatches the ``bor:view-refresh`` CustomEvent on the view's
section. The dispatch site is INSIDE the ``if (wasMounted)`` guard,
and the ``wasMounted`` capture runs BEFORE the mount-once set
(``mounted[name] = true``) — so the first show (the mount) and boot
never dispatch: the mount's own load is the first fetch. Event
order: the view is visible and the head/nav state is written
BEFORE the refresh fires, and the focus/scroll tail runs after."""
js = _js()
assert '"bor:view-refresh"' in js, "the refresh event literal must exist"
fn = js.find("async function switchTo")
assert fn != -1, "switchTo must exist"
body = js[fn : js.find("\n}", fn)]
capture = body.find("const wasMounted = mounted[name]")
mount_set = body.find("mounted[name] = true")
assert 0 <= capture < mount_set, (
"the wasMounted capture must precede the mount-once set "
"(first show is exempt from the refresh)"
)
gate = body.find("if (wasMounted)")
dispatch = body.find('root.dispatchEvent(new CustomEvent("bor:view-refresh"))')
assert 0 <= gate < dispatch < gate + 120, (
"the dispatch must sit inside the wasMounted guard"
)
show_loop = body.find("Object.entries(viewEls)")
title_write = body.find("document.title = titleFor(name)")
current_set = body.find("current = name")
focus = body.find("root.focus(")
assert show_loop < title_write < current_set < gate < dispatch < focus, (
"visible → head/nav state → refresh dispatched → focus/scroll tail"
)
def test_active_view_reclick_dispatches_refresh_not_bare_return() -> None:
"""Phase 77: a re-click of the ACTIVE view's own nav link is a
re-fetch, not a no-op — the ``name === current`` branch dispatches
the refresh event on that view's section and returns. It must NOT
pushState (the URL is already this view's path) and must NOT
re-run the switch (no re-mount)."""
js = _js()
fn = js.find('nav.addEventListener("click"')
assert fn != -1, "the delegated click handler on the nav must exist"
body = js[fn : js.find("\n });", fn)]
branch = body.find("if (name === current)")
assert branch != -1, "the active re-click branch must exist"
branch_end = body.find("}", branch)
branch_body = body[branch : branch_end + 1]
assert 'new CustomEvent("bor:view-refresh")' in branch_body, (
"the re-click branch must dispatch the refresh event (not a bare return)"
)
assert "history.pushState" not in branch_body, (
"the re-click must NOT pushState — the URL is already this view's path"
)
assert "switchTo" not in branch_body, "the re-click must NOT re-run the switch"
assert "return" in branch_body, "the re-click still returns early (menu closes)"
def test_history_view_listens_for_view_refresh_in_admin_branch_only() -> None:
"""Phase 77: the History view re-fetches on a user-initiated
re-show — history.js registers a ``bor:view-refresh`` listener on
the view's root that re-runs the (now re-entrant) ``loadChats()``.
The listener is armed only AFTER the whoami gate passes: anonymous
shows the gate and never fetches (the phase-50 contract the story
E2E pins), and the ``started`` flag means the listener can only
re-run a load the mount already made."""
history_js = (ASSETS / "history.js").read_text(encoding="utf-8")
assert 'addEventListener("bor:view-refresh"' in history_js, (
"history.js must listen for the refresh event on the view root"
)
gate = history_js.find("if (!(await fetchIsAdmin()))")
listener = history_js.find('addEventListener("bor:view-refresh"')
assert 0 <= gate < listener, (
"the listener is armed only in the ADMIN branch (after the gate)"
)
assert re.search(r"if \(started\)\s+loadChats\(\)", history_js), (
"the listener is gated on the first load (started)"
)
# Re-entrancy: a re-load drops the data rows (except the hidden
# empty-state row) before fetching — the list is replaced, not
# duplicated.
load = history_js.find("async function loadChats()")
assert load != -1, "loadChats must exist"
load_body = history_js[load : history_js.find("\n }", load)]
assert "tr !== emptyRow" in load_body and "tr.remove()" in load_body, (
"loadChats must remove the data rows (the empty row stays) first"
)
clear_i = load_body.find("tr !== emptyRow")
fetch_i = load_body.find('fetch("/api/chats")')
assert 0 <= clear_i < fetch_i, "the row clearing precedes the fetch"
# ---------- phase 77 task 02: RAG / Sources / Tuning re-fetch; chat stays out ----------
def _asset(name: str) -> str:
path = ASSETS / name
assert path.is_file(), f"missing {path}"
return path.read_text(encoding="utf-8")
def _pin_refresh_listener(js: str, gate: str, listener_call: str, name: str) -> None:
"""Shared shape of the task-02 pin: the view module listens for
``bor:view-refresh`` on its own root, the listener re-runs the
view's existing load, and the listener is armed ONLY in the ADMIN
branch — after the whoami gate (anonymous never fetches)."""
listener = js.find('addEventListener("bor:view-refresh"')
assert listener != -1, f"{name} must listen for the refresh event on the view root"
gate_i = js.find(gate)
assert 0 <= gate_i < listener, (
f"{name}: the listener must be armed in the ADMIN branch (after {gate!r})"
)
assert listener_call in js[listener : listener + 120], (
f"{name}: the listener must re-run the view's load ({listener_call!r})"
)
def test_rag_view_refetches_on_reshow() -> None:
"""Phase 77 task 02: the RAG (knowledge base) view re-fetches on a
user-initiated re-show — sources.js listens and re-runs
``loadDocs()``. ``loadDocs`` is now re-entrant: the tbody's rows
are cleared at the TOP, before the fetch (the History pattern from
task 01), so a refresh from a populated list into an empty result
replaces the list instead of leaving ghost rows."""
js = _asset("sources.js")
_pin_refresh_listener(
js, "const admin = await fetchIsAdmin();", "() => loadDocs()", "sources.js"
)
load = js.find("async function loadDocs()")
assert load != -1, "loadDocs must exist"
body = js[load : js.find("\n }", load)]
clear_i = body.find("tbody.replaceChildren()")
fetch_i = body.find('fetch("/api/docs")')
assert 0 <= clear_i < fetch_i, (
"the row clearing must precede the fetch (a populated → empty refresh "
"must not leave ghost rows)"
)
def test_git_sources_view_refetches_on_reshow() -> None:
"""Phase 77 task 02: the Sources (git-sources) view re-fetches on a
user-initiated re-show — git-sources.js listens and re-runs
``loadSources()``. A re-call resets ALL THREE list states: the
populated render (renderSources replaces the tbody + re-syncs the
empty state) and the load error (``hideLoadError()`` runs on the
success path BEFORE rendering, so an error followed by a
successful refresh clears it)."""
js = _asset("git-sources.js")
_pin_refresh_listener(
js, "const admin = await fetchIsAdmin();", "() => loadSources()", "git-sources.js"
)
load = js.find("async function loadSources()")
assert load != -1, "loadSources must exist"
body = js[load : js.find("\n }", load)]
hide_i = body.find("hideLoadError()")
render_i = body.find("renderSources(")
assert 0 <= hide_i < render_i, (
"the success path must clear the load error before rendering "
"(an error followed by a successful refresh clears the error)"
)
render = js.find("function renderSources(")
assert render != -1, "renderSources must exist"
render_body = js[render : js.find("\n }", render)]
assert "tbody.replaceChildren()" in render_body, (
"a re-render replaces the list (the populated state resets)"
)
def test_tuning_view_refetches_on_reshow() -> None:
"""Phase 77 task 02: the Tuning view re-fetches on a user-initiated
re-show — tuning.js listens and re-runs ``loadNotes()``. A re-call
replaces the list (renderNotes clears it first); a FAILED refresh
keeps the last rendered list — loadNotes's documented contract
(progressive enhancement, never a blanked panel), unchanged by the
listener (it just calls the function)."""
js = _asset("tuning.js")
_pin_refresh_listener(
js, "if (await fetchIsAdmin())", "() => loadNotes()", "tuning.js"
)
render = js.find("function renderNotes(")
assert render != -1, "renderNotes must exist"
render_body = js[render : js.find("\n }", render)]
assert 'tuneList.textContent = ""' in render_body, (
"a re-render clears the list first (the re-call replaces it)"
)
def test_chat_view_does_not_listen_for_view_refresh() -> None:
"""Negative pin: app.js (the chat view) must NOT listen for
``bor:view-refresh`` — the in-flight SSE stream and the local
conversation survive EVERY switch (the phase-76 contract the
stream E2E pins). The exclusion is a contract, not an oversight;
the deliberate-exclusion comment lives at the chat view's
module-scope state in app.js."""
js = _asset("app.js")
assert 'addEventListener("bor:view-refresh"' not in js, (
"the chat view must NOT listen for the refresh event — its "
"in-flight stream and local conversation must survive every "
"switch (phase 76)"
)
assert "bor:view-refresh" in js, (
"the exclusion is documented at the chat view's module-scope state"
)
# ---------- phase 77 task 03: the History refresh button ----------
def _history_view(html: str) -> str:
"""The shell's History view section (the test_history_page pattern):
from the #view-history open tag to the container main's close
(the view is the shell's LAST view section)."""
start = html.find('<section class="view" id="view-history"')
assert start != -1, "the #view-history section must be in the shell"
end = html.find("</main>", start)
assert end != -1, "the container main must close after the view"
return html[start:end]
def test_history_refresh_button_markup_lives_in_the_page_head() -> None:
"""Phase 77 task 03 (TODO.md L3): the History page-head carries the
explicit refresh control — #history-refresh, a ``type="button"``
``.history-refresh`` with the accessible name
``aria-label="Refresh saved chats"``, the house inline-SVG refresh
glyph (aria-hidden) and the visible "Refresh" label (the phase-46
auth-link convention: label visible ≥640px, icon-only below — the
aria-label keeps the name in both). It sits INSIDE the view's
.page-head and BEFORE the table wrap (outside it — the button must
stay reachable while the empty state is showing)."""
view = _history_view(_html())
btn_i = view.find('id="history-refresh"')
assert btn_i != -1, "the #history-refresh button must exist"
tag_start = view.rfind("<button", 0, btn_i)
tag_end = view.find(">", btn_i)
tag = view[tag_start:tag_end]
assert 'type="button"' in tag, "a plain button (no form submit)"
assert 'class="history-refresh"' in tag
assert 'aria-label="Refresh saved chats"' in tag, ("the accessible name")
tail = view[tag_end:tag_end + 600]
assert 'aria-hidden="true"' in tail, "the refresh glyph must be aria-hidden"
assert '<span class="history-refresh-label">Refresh</span>' in tail, (
"the visible Refresh label (icon-only below 640px, label above)"
)
head_i = view.find('class="page-head"')
wrap_i = view.find('id="history-table-wrap"')
assert -1 < head_i < btn_i < wrap_i, (
"the button sits in the page-head, before (OUTSIDE) the table wrap"
)
def test_history_refresh_button_binding_admin_only_with_outcome_lines() -> None:
"""Phase 77 task 03: history.js binds #history-refresh in the ADMIN
branch only — the anonymous branch HIDES the button (the gate is
what anonymous sees; no dead control beside the sign-in gate) and
still fetches nothing. The click handler disables the button
BEFORE the fetch (no double-fire while in flight) and delegates to
the re-entrant load; the re-enable sits in a ``finally`` (success
AND failure — a click can never leave the button stuck disabled).
The success line is ``Saved chats refreshed.``; the failure lines
live INSIDE ``loadChats`` itself — the house copy (network:
"is the app reachable?"; non-2xx: "try again.") — so every caller
of a failed load (the mount's first load, a re-show, the button)
sees the outcome in #history-status."""
js = _asset("history.js")
gate = js.find("if (!(await fetchIsAdmin()))")
assert gate != -1
branch = js[gate:js.find("return;", gate)]
assert "refreshBtn.hidden = true" in branch, (
"the anonymous branch hides the button (no dead control)"
)
admin_after = js[js.find("return;", gate):]
bind = admin_after.find('refreshBtn.addEventListener("click"')
assert bind != -1, "the refresh binding must exist in the admin branch"
handler = admin_after[bind:admin_after.find(");", bind)]
assert "refreshBtn.disabled = true" in handler, (
"the click disables the button before the fetch (no double-fire)"
)
# refreshChats is defined alongside loadChats (before the gate) —
# its BODY is pinned on the whole file, its BINDING on the admin
# branch above (the hoisted function is only reachable from the
# admin-branch binding: the anonymous branch never references it).
fn = js.find("async function refreshChats()")
assert fn != -1, "refreshChats must exist"
fn_body = js[fn:js.find("\n }", fn)]
assert "loadChats()" in fn_body, "the button re-runs the (re-entrant) load"
assert "finally" in fn_body and "refreshBtn.disabled = false" in fn_body, (
"the button re-enables on success AND failure (the finally)"
)
assert '"Saved chats refreshed."' in fn_body, "the exact success line"
load = js.find("async function loadChats()")
load_body = js[load:js.find("\n }", load)]
assert "Couldn't load saved chats — is the app reachable?" in load_body, (
"the network-error line lives in loadChats (every caller sees it)"
)
assert "Couldn't load saved chats — try again." in load_body, (
"the non-2xx line lives in loadChats (every caller sees it)"
)
def test_history_refresh_button_css_reuses_the_new_chat_language() -> None:
"""Phase 77 task 03 (styles.css): .history-refresh reuses the
.new-chat-btn visual language — the solid brand pill (--bg text on
--brand, 5.2:1 ≥ WCAG 4.5:1), the ≥44px target, the lightened
hover fill, the dimmed :disabled (the in-flight state), the glyph
hidden on desktop (the label carries the pill) — and the global
:focus-visible ring applies (no button-scoped focus override).
The page-head flex row is SCOPED to #view-history (the other four
views' page-heads are untouched). Below 640px the pill goes
icon-only (the phase-46 auth-link convention — the aria-label
keeps the accessible name)."""
css = _asset("styles.css")
block = re.search(r"\.history-refresh \{([\s\S]*?)\n\}", css)
assert block, "styles.css must style .history-refresh"
body = block.group(1)
assert "background: var(--brand)" in body, "the .new-chat-btn brand fill"
assert "color: var(--bg)" in body, "--bg text on --brand (5.2:1, AA)"
assert "min-height: 44px" in body, "the comfortable touch target"
assert "border-radius: 999px" in body and "border: 0" in body, "the pill"
assert ".history-refresh:hover { background: #f55a72; color: var(--bg); }" in css
assert ".history-refresh:disabled { opacity: 0.6; cursor: wait; }" in css, (
"the in-flight disabled state is dimmed (the house language)"
)
assert ".history-refresh svg { width: 16px; height: 16px; display: none; }" in css, (
"desktop: the label carries the pill (the glyph is hidden)"
)
row = re.search(r"#view-history \.page-head \{([\s\S]*?)\n\}", css)
assert row and "display: flex" in row.group(1), (
"the page-head flex row is scoped to the History view"
)
mobile = re.search(r"@media \(max-width: 640px\) \{([\s\S]*?)\n\}\n", css)
assert mobile, "the 640px media query must exist"
mbody = mobile.group(1)
assert ".history-refresh-label { display: none; }" in mbody, (
"icon-only below 640px (the phase-46 convention)"
)
assert ".history-refresh svg { display: block; }" in mbody, (
"the glyph is the whole control below 640px"
)
# ---------- phase 79 task 06: the Tokens view (generate · list · revoke) ----------
def test_tokens_view_module_contract() -> None:
"""Phase 79 task 06: tokens.js follows the phase-76 view-module
contract — ``export async function mount(root)`` is the entry, the
whoami gate (``fetchIsAdmin``) runs in mount and the anonymous
branch shows the gate + hides the table + RETURNS with NO
/api/tokens request (the router 403s anonymous), the
``bor:view-refresh`` listener is armed ONLY in the ADMIN branch
(after the gate) and re-runs the re-entrant ``loadTokens()``
(gated on the ``started`` flag), and ``loadTokens`` hides the
once-block and clears the data rows BEFORE the fetch — the
plaintext is never re-shown and the list is replaced, not
duplicated. Every cell is textContent: the file never touches
innerHTML (XSS-safe by construction)."""
js = _asset("tokens.js")
assert "export async function mount(root)" in js, (
"mount(root) must be the module's entry (the phase-76 fold)"
)
assert 'import { fetchIsAdmin } from "./header.js";' in js, (
"the view imports ONLY the shared cached whoami promise"
)
mount_i = js.find("export async function mount(root)")
gate_i = js.find("if (!(await fetchIsAdmin()))")
assert 0 <= mount_i < gate_i, "the whoami gate must run in mount"
# The anonymous branch: gate in, table out, then a bare return —
# and NO fetch call anywhere inside it.
branch = js[gate_i:js.find("return;", gate_i)]
assert "fetch(" not in branch, (
"the anonymous branch must not fetch anything"
)
assert "tableWrap.hidden = true" in branch
assert "gateEl.hidden = false" in branch
# The re-show refresh: armed in the ADMIN branch only (after the
# gate), gated on the first load (started), re-running loadTokens.
listener = js.find('addEventListener("bor:view-refresh"')
assert 0 <= gate_i < listener, (
"the refresh listener is armed only in the ADMIN branch (after the gate)"
)
assert re.search(r"if \(started\)\s+loadTokens\(\)", js), (
"the listener is gated on the first load (started)"
)
# loadTokens: re-entrant — the once-block hides and the data rows
# (except the hidden empty-state row) are dropped BEFORE the fetch.
load = js.find("async function loadTokens()")
assert load != -1, "loadTokens must exist"
load_body = js[load:js.find("\n }", load)]
hide_i = load_body.find("onceBlock.hidden = true")
clear_i = load_body.find("tr !== emptyRow")
fetch_i = load_body.find('fetch("/api/tokens")')
assert 0 <= hide_i < clear_i < fetch_i, (
"once-block hide + row clearing must precede the fetch "
"(a re-render never re-shows the plaintext; the list is replaced)"
)
assert "innerHTML" not in js, (
"every cell is textContent — no innerHTML anywhere (XSS-safe)"
)
def test_tokens_view_scaffold_in_the_shell() -> None:
"""Phase 79 task 06: the shell carries the #view-tokens section —
hidden AND inert + focusable (the WCAG pair, AGENTS.md rule 5) —
with the page-head (h1 \"Access tokens\"), the #tokens-gate (the
#history-gate pattern, ship-hidden, its Sign in returning to the
Tokens view), the role=\"status\" live region, the create row
(label input + Generate — ship-hidden, anonymous-safe), the
#token-once block (ship-hidden — only a 201 reveals it), and the
full-width table (AGENTS.md rule 5) with the visually-hidden
Actions header + the hidden #tokens-empty-row."""
html = _html()
view = html.find('<section class="view" id="view-tokens"')
assert view != -1, "the #view-tokens section must be in the shell"
tag_end = html.find(">", view)
tag = html[view:tag_end]
assert "hidden" in tag and "inert" in tag, (
"the folded view ships hidden AND inert"
)
assert 'tabindex="-1"' in tag, "the target view is focusable"
main_end = html.find("</main>", view)
assert view < main_end, "the view section lives inside the single main"
body = html[view:main_end]
assert "<h1>Access tokens</h1>" in body
gate = re.search(r'<section[^>]*id="tokens-gate"[^>]*>', body)
assert gate and "hidden" in gate.group(0), "#tokens-gate must ship hidden"
assert 'href="/login.html?next=/tokens.html"' in body, (
"the gate's Sign in returns to the Tokens view (no-JS fallback)"
)
assert re.search(r'<span[^>]*id="tokens-status"[^>]*role="status"[^>]*>', body)
create = re.search(r'<div[^>]*id="token-create"[^>]*>', body)
assert create and "hidden" in create.group(0), (
"the create row ships hidden (anonymous-safe)"
)
assert re.search(r'<input[^>]*id="token-label"[^>]*>', body)
assert re.search(r'<button[^>]*id="token-generate"[^>]*>', body)
once = re.search(r'<div[^>]*id="token-once"[^>]*>', body)
assert once and "hidden" in once.group(0), (
"the once-block ships hidden (only a 201 reveals it)"
)
assert re.search(r'<input[^>]*id="token-once-value"[^>]*readonly[^>]*>', body)
assert re.search(r'<button[^>]*id="token-once-copy"[^>]*>', body)
wrap = re.search(r'<div[^>]*id="tokens-table-wrap"[^>]*>', body)
assert wrap and 'role="region"' in wrap.group(0) and 'tabindex="0"' in wrap.group(0)
assert 'id="tokens-tbody"' in body
assert re.search(r'<tr[^>]*id="tokens-empty-row"[^>]*hidden>', body)
# The Actions column header is visually-hidden (the row buttons
# carry their own aria-labels — the history-table convention).
assert '<th scope="col"><span class="visually-hidden">Actions</span></th>' in body