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
+94
View File
@@ -0,0 +1,94 @@
"""Unit tests: the ApiToken model registers the api_tokens contract
(phase 79, task 01).
Schema-level assertions without a live DB (the
``tests/unit/test_models.py`` doc_drafts precedent): the table name, the
column set + nullability, the UNIQUE ``token_hash`` (two tokens with the
same hash must collide — the unique constraint backing
``ix_api_tokens_token_hash``), and a display-only, non-unique
``label``. The real duplicate-rejection behaviour is pinned against the
dev DB in ``tests/integration/test_migration_0012.py``.
"""
from __future__ import annotations
from sqlalchemy import PrimaryKeyConstraint, String, UniqueConstraint
from sqlalchemy.schema import Table
import app.models # noqa: F401 (import registers all tables on Base.metadata)
from app.db import Base
def _table() -> Table:
return Base.metadata.tables["api_tokens"]
def test_api_tokens_table_registered() -> None:
assert "api_tokens" in Base.metadata.tables, "ApiToken must register api_tokens"
assert _table().name == "api_tokens"
def test_api_tokens_column_contract() -> None:
"""The column set + nullability: ``id`` UUID PK; ``label`` /
``token_hash`` NOT NULL; ``created_at`` NOT NULL with a server
default (now()); ``last_used_at`` / ``revoked_at`` NULL until the
service (tasks 02/03) sets them."""
tok = _table()
assert set(tok.c.keys()) == {
"id", "label", "token_hash", "created_at", "last_used_at", "revoked_at",
}
assert tok.c["id"].primary_key is True, "api_tokens.id must be the PK"
assert tok.c["id"].nullable is False, "api_tokens.id must be NOT NULL"
assert tok.c["label"].nullable is False, "label must be NOT NULL"
label_type = tok.c["label"].type
assert isinstance(label_type, String), "label must be String(120)"
assert label_type.length == 120, "label must be String(120)"
assert tok.c["token_hash"].nullable is False, "token_hash must be NOT NULL"
hash_type = tok.c["token_hash"].type
assert isinstance(hash_type, String), "token_hash must be String(64)"
assert hash_type.length == 64, (
"token_hash must be String(64) — a sha256 hex digest"
)
assert tok.c["created_at"].nullable is False, "created_at must be NOT NULL"
assert tok.c["created_at"].server_default is not None, (
"created_at needs a server default (now())"
)
for name in ("last_used_at", "revoked_at"):
assert tok.c[name].nullable is True, (
f"{name} must be NULL until first use / revocation"
)
def test_api_tokens_token_hash_is_unique() -> None:
"""Two tokens with the same hash must collide: a UNIQUE constraint
covers exactly ``token_hash`` (the backing constraint of the
``ix_api_tokens_token_hash`` unique index — the stored credential
is the lookup key)."""
tok = _table()
uq = [
c
for c in tok.constraints
if isinstance(c, UniqueConstraint)
and not isinstance(c, PrimaryKeyConstraint)
and {col.name for col in c.columns} == {"token_hash"}
]
assert uq, "api_tokens must be unique on (token_hash) — the stored credential"
def test_api_tokens_label_is_not_unique() -> None:
"""``label`` is the hand-out name — display-only: the column itself
is not unique and no unique constraint may cover it (two tokens can
share a label, e.g. two "alice" tokens issued at different times)."""
tok = _table()
assert not tok.c["label"].unique, "label must not be unique"
covering = [
c
for c in tok.constraints
if isinstance(c, UniqueConstraint)
and any(col.name == "label" for col in c.columns)
]
assert not covering, "no unique constraint may cover api_tokens.label"