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
+49
View File
@@ -58,6 +58,13 @@
reveals it once whoami says admin, exactly like the
Tuning link above. -->
<a href="/history.html" class="nav-link" id="nav-history" hidden>History</a>
<!-- Phase 79 (task 06): the Tokens link is admin-only —
hidden by default, header.js reveals it once whoami says
admin, exactly like the History link above (the
phase-34 one-bar contract: the SAME nav ships on every
page — test_nav_consistency pins the inventory parity).
Null-safe: header.js is a no-op on a page without it. -->
<a href="/tokens.html" class="nav-link" id="nav-tokens" hidden>Tokens</a>
<!-- Phase 46 (mobile dropdown copy: sign-in — desktop bar copy is
outside the nav; see styles.css .sign-in-mobile rules). -->
<a href="/login.html?next=/" class="auth-link sign-in-link sign-in-mobile" id="sign-in-link-mobile" hidden>
@@ -145,6 +152,48 @@
</div>
</main>
<!-- Phase 79 (task 05): the viewer's inline token gate — the SAME
gate markup as the shell (frontend/index.html #auth-gate), the
ids renamed (#doc-auth-gate …). The content endpoint is now
require_user-gated (phase 79 task 03): a direct anonymous URL
shows THIS gate instead of a content error (the page document
loads; the GATED DATA does not). assets/token-gate.js (loaded
through document.js's import) mounts it with #main as the lock
root; the existing boot sequence (whoami → load content) runs
as onAuthed — an admin (or a validly cached token user) gets
it immediately, the gate never shows. SHIPS hidden + inert
(the phase-16 ship-hidden pattern). -->
<section class="auth-gate" id="doc-auth-gate" hidden inert aria-labelledby="doc-auth-gate-title">
<div class="auth-gate-card">
<div class="auth-gate-glyph" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="10" width="16" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/><circle cx="12" cy="14.5" r="1.4" fill="currentColor" stroke="none"/><path d="M12 16v2"/></svg>
</div>
<h2 id="doc-auth-gate-title">Enter your access token</h2>
<p class="auth-gate-sub">
Ask the admin for a token — it opens chat, the answers, and the
documents they cite. Shared chats stay open.
</p>
<form id="doc-auth-gate-form">
<label class="visually-hidden" for="doc-auth-gate-input">Access token</label>
<input
id="doc-auth-gate-input"
name="token"
type="text"
autocomplete="off"
autocapitalize="none"
spellcheck="false"
placeholder="bor_…"
required
>
<button type="submit" class="auth-gate-submit">Sign in</button>
</form>
<p class="auth-gate-error" id="doc-auth-gate-error" role="alert" hidden>
That token isn’t valid — check it with the admin.
</p>
<a class="sources-gate-link" href="/login.html?next=/">Sign in as admin</a>
</div>
</section>
<footer class="app-footer">
<div class="container footer-inner">
<span class="footer-text">Powered by self-hosted models</span>