feat: phases 77–80 — navbar view refresh, static background, API tokens, history suggestion chips
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:
@@ -24,8 +24,11 @@ This phase only surfaces the stored field: the content endpoint returns
|
||||
``summary`` (task 01) and the shared ``renderDocument`` core draws the
|
||||
labeled ``.doc-summary`` panel above the content on BOTH surfaces (task
|
||||
02) — the full-page viewer and the chat/sources modal. The tests assert
|
||||
exactly that contract, plus the phase-16 soft rule: the content
|
||||
endpoint stays public (anonymous fetch → 200, no admin cookie needed).
|
||||
exactly that contract. Phase 79 supersedes the phase-16 soft rule: the
|
||||
content endpoint is ``require_user``-gated, so the viewer surfaces and
|
||||
the API shape pin run under a signed-in session (the shape itself —
|
||||
``summary`` for the yaml, ``null`` for the markdown control — is
|
||||
unchanged).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -140,6 +143,7 @@ def test_full_page_shows_summary_and_original_together(
|
||||
cannot contain, is rendered in the raw ``<pre>``."""
|
||||
_reset_db_and_import(mock_llm)
|
||||
page.set_default_timeout(30_000)
|
||||
login(page, app_url, next="/") # phase 79: the viewer content is gated
|
||||
|
||||
digest_line, pointer_line = _summary_lines(SOURCE, YAML_PATH)
|
||||
page.goto(f"{app_url}/document.html?source={SOURCE}&path={YAML_URL_PATH}")
|
||||
@@ -279,6 +283,7 @@ def test_markdown_doc_has_no_summary_panel(
|
||||
before (first child of the content container is the doc body)."""
|
||||
_reset_db_and_import(mock_llm)
|
||||
page.set_default_timeout(30_000)
|
||||
login(page, app_url, next="/") # phase 79: the viewer content is gated
|
||||
|
||||
# Full page: no panel, markdown column untouched.
|
||||
page.goto(f"{app_url}/document.html?source={SOURCE}&path={MD_URL_PATH}")
|
||||
@@ -294,7 +299,8 @@ def test_markdown_doc_has_no_summary_panel(
|
||||
assert order == ["doc-md"], f"markdown doc gained children: {order}"
|
||||
|
||||
# Modal: same story — no panel, .doc-md is the sole content child.
|
||||
login(page, app_url)
|
||||
# (the session is already signed in — the form login above)
|
||||
page.goto(f"{app_url}/sources.html")
|
||||
row = page.locator("#docs-tbody tr", has_text=MD_PATH)
|
||||
expect(row).to_have_count(1)
|
||||
row.locator("td:nth-child(2) a.doc-link").click()
|
||||
@@ -312,18 +318,21 @@ def test_markdown_doc_has_no_summary_panel(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. API shape (cheap, via the page context's request — still anonymous)
|
||||
# 4. API shape (cheap, via the page context's request — signed in since
|
||||
# phase 79 gated the endpoint)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_content_api_summary_shape_anonymous(
|
||||
def test_content_api_summary_shape(
|
||||
page: Page, app_url: str, mock_llm: int, db_ready: None
|
||||
) -> None:
|
||||
"""``GET /api/documents/content`` carries ``summary`` — the string
|
||||
for the summarized yaml, ``null`` for the markdown control — and
|
||||
stays PUBLIC: no admin cookie is set anywhere in this test, so both
|
||||
200s prove the phase-16 soft rule (viewer public) is unchanged."""
|
||||
for the summarized yaml, ``null`` for the markdown control. Phase 79
|
||||
superseded the phase-16 soft rule: the endpoint is require_user, so
|
||||
the pin runs under the form-login session (the shape itself is
|
||||
unchanged)."""
|
||||
_reset_db_and_import(mock_llm)
|
||||
login(page, app_url, next="/")
|
||||
|
||||
digest_line, _ = _summary_lines(SOURCE, YAML_PATH)
|
||||
expected_summary = f"{digest_line}\nSource: {SOURCE}/{YAML_PATH}"
|
||||
@@ -331,7 +340,7 @@ def test_content_api_summary_shape_anonymous(
|
||||
resp = page.context.request.get(
|
||||
f"{app_url}/api/documents/content?source={SOURCE}&path={YAML_URL_PATH}"
|
||||
)
|
||||
assert resp.status == 200, "anonymous viewer access must stay public"
|
||||
assert resp.status == 200
|
||||
body = resp.json()
|
||||
assert body["source"] == SOURCE
|
||||
assert body["path"] == YAML_PATH
|
||||
@@ -343,7 +352,7 @@ def test_content_api_summary_shape_anonymous(
|
||||
resp_md = page.context.request.get(
|
||||
f"{app_url}/api/documents/content?source={SOURCE}&path={MD_URL_PATH}"
|
||||
)
|
||||
assert resp_md.status == 200, "anonymous viewer access must stay public"
|
||||
assert resp_md.status == 200
|
||||
md_body = resp_md.json()
|
||||
assert md_body["format"] == "md"
|
||||
assert md_body["summary"] is None, "markdown docs never carry a summary"
|
||||
|
||||
Reference in New Issue
Block a user