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
+64 -17
View File
@@ -44,29 +44,69 @@ def _script_srcs(path: Path) -> list[str]:
# ---------- header.js: the module itself ----------
def test_header_module_exports_the_three_functions() -> None:
"""header.js must export the three functions every page script
imports (fetchIsAdmin / initSharedHeader / clearChatStorage)."""
def test_header_module_exports_the_header_functions() -> None:
"""header.js must export the functions every page script imports
(fetchWhoami — the phase-79 canonical call — fetchIsAdmin, its
phase-16/19 backward-compatible delegation, initSharedHeader,
clearChatStorage) plus resetWhoami (the phase-79 cache
invalidation the token gate uses after a mid-page auth)."""
js = _text(HEADER_JS)
assert "export function fetchWhoami" in js
assert "export function fetchIsAdmin" in js
assert "export function resetWhoami" in js
assert "export async function initSharedHeader" in js
assert "export function clearChatStorage" in js
def test_whoami_fetch_is_cached_in_a_module_level_promise() -> None:
"""The whoami fetch is cached in the module-level `adminPromise`
marker — first call stores the promise, later calls return it, so a
page makes exactly ONE /api/whoami request per load no matter how
many consumers await it. Anonymous-safe: a failure resolves to
false."""
"""The whoami fetch is cached in the module-level `whoamiPromise`
marker (phase 79, task 05: it stores the FULL response —
{ authenticated, role } — not just the admin flag) — first call
stores the promise, later calls return it, so a page makes exactly
ONE /api/whoami request per load no matter how many consumers
await it. Anonymous-safe: non-2xx / network failure / malformed
body all resolve to { authenticated: false, role: "anonymous" }.
The string `fetch("/api/whoami")` appears in this file EXACTLY
ONCE — the single-request contract (the rest of the frontend goes
through fetchWhoami/fetchIsAdmin)."""
js = _text(HEADER_JS)
assert re.search(r"let\s+adminPromise\s*=\s*null", js), (
"module-level adminPromise marker missing"
assert re.search(r"let\s+whoamiPromise\s*=\s*null", js), (
"module-level whoamiPromise marker missing"
)
assert 'fetch("/api/whoami")' in js
assert "if (!adminPromise)" in js, "fetchIsAdmin must reuse the stored promise"
assert "return adminPromise" in js
assert ".catch(() => false)" in js, "network failure must resolve to anonymous"
assert js.count('fetch("/api/whoami")') == 1, (
"the SINGLE /api/whoami call site lives in header.js exactly once"
)
assert "if (!whoamiPromise)" in js, "fetchWhoami must reuse the stored promise"
assert "return whoamiPromise" in js
assert 'role: "anonymous"' in js, "the anonymous fallback carries the role"
assert ".catch(() => ANONYMOUS_WHOAMI)" in js, (
"network failure must resolve to the anonymous role"
)
def test_fetch_is_admin_delegates_to_fetch_whoami() -> None:
"""Phase 79 (task 05): fetchIsAdmin() is a thin delegation —
fetchWhoami().then(w => w.role === "admin"): SAME single request,
all phase-16/19 callers keep working, and a token user (role
"user") reads FALSE here (the admin-only surfaces key off
role === "admin", never off `authenticated`)."""
js = _text(HEADER_JS)
fn = js.find("export function fetchIsAdmin")
assert fn != -1
body = js[fn : js.find("\n}", fn)]
assert "fetchWhoami().then((w) => w.role === \"admin\")" in body
def test_reset_whoami_clears_the_module_cache() -> None:
"""Phase 79 (task 05): the token gate changes the session MID-PAGE
(silent re-auth / interactive login) — resetWhoami() drops the
cached promise so the NEXT fetchWhoami() is a fresh post-auth
request (a boot-fired pre-auth whoami would still say anonymous)."""
js = _text(HEADER_JS)
fn = js.find("export function resetWhoami")
assert fn != -1
body = js[fn : js.find("\n}", fn)]
assert "whoamiPromise = null" in body
def test_init_shared_header_toggles_only_elements_that_exist() -> None:
@@ -77,7 +117,11 @@ def test_init_shared_header_toggles_only_elements_that_exist() -> None:
fn = js.find("function initSharedHeader")
assert fn != -1
body = js[fn : js.find("\n}", fn)]
assert "await fetchIsAdmin()" in body
# Phase 79 (task 05): the header boots on the FULL whoami — the
# auth pair keys off the authenticated role (admin OR token user),
# the admin-only surfaces off role === "admin".
assert "await fetchWhoami()" in body
assert 'whoami.role === "admin"' in body
for selector in ("#nav-sources", "#nav-git-sources", "#nav-tuning"):
assert f'querySelector("{selector}")' in body
# Sign in: both the bar copy AND the mobile dropdown copy (phase 46)
@@ -410,7 +454,10 @@ def test_app_js_delegates_the_shared_controls_to_header_module() -> None:
(`./header.js`) so esbuild can bundle it into the image."""
js = _text(APP_JS)
assert 'from "./header.js"' in js
assert "fetchIsAdmin" in js and "initSharedHeader" in js
# Phase 79 (task 05): app.js reads the FULL whoami (the same cached
# promise) — the auth pair off `authenticated`, the admin-only
# surfaces off role === "admin".
assert "fetchWhoami" in js and "initSharedHeader" in js
assert "signOutBtn.addEventListener" not in js, (
"the sign-out binding moved to header.js"
)
@@ -419,7 +466,7 @@ def test_app_js_delegates_the_shared_controls_to_header_module() -> None:
)
assert "loadAuthState" not in js, "loadAuthState was deleted in phase 19"
assert "function applyAuthState" in js, "chat-page tuning gate stays"
assert "isAdmin = await fetchIsAdmin();" in js
assert 'who.role === "admin"' in js and "who.authenticated" in js
init_idx = js.find("await initSharedHeader();")
restore_idx = js.find("restoreConversation();")
assert -1 < init_idx < restore_idx, (