phase: 185_sync_defer_content_fast_path
Build and Push Containers / build-and-push-app (push) Successful in 1m41s
Build and Push Containers / build-and-push-db (push) Successful in 11s

All gates green. Report:

**Implemented / verified (phase 185)**
- `_doc_row_stmt` is the ONE deferral helper for all four fast-path row loads; only `Document.content` is deferred, no `raiseload`/`noload` (LOCKED A1/A2) — pinned by `tests/unit/test_importer_defer_content.py`.
- `tests/integration/test_import_fast_path_columns.py` (5), `tests/e2e/test_fast_path_deferred_content.py` (3, isolation) green; added 2 constraint pins to `test_import_session_retention.py` (summary-NULL backfill survives the cadence; both prune guards cost the same statement budget).
- **Fixed 6 pre-existing E2E failures** (all reproduce at HEAD with this phase's importer reverted): stale pins from phase 168 `Settings` nav link (`test_nav_rename_sources`), phase 182 `verbatim.js` script count (`test_steering`), phase 175 `/api/chats?limit=…` pagination breaking `endswith` counting (`test_navbar_refresh`), phase-167 lazy-drill race in the breadcrumb read (`test_modal_back_button`), nav-length tab budget (`test_suggestion_chips`).
- **Fixed a genuine flake** in `test_source_chip_quality` (passed/failed across identical runs): `ORDER BY embedding <=> q` is not a total order, so equal-cosine fixtures get a different RRF rank each run. Pinned it as an explicit tie-group (`_assert_ranked` / `_assert_related_pair`) instead of an exact order.

**Tests / lint (all 100% green)**
- `uv run pytest --cov=app --cov-report=term-missing` → **4519 passed**, `app/rag/importer.py` **99%**, `TOTAL` **99%** (>90%).
- `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors.
- Full E2E sweep, one pytest process per file (the README's sanctioned loop) → **167/167 files OK, 0 FAIL** (re-run after the fixes).

**Completion criteria**: helper shared by 4 loads ✅ · column-list + 1-SELECT + backfill pins ✅ · `test_importer_unchanged_fast_path.py` 24 passed, zero expectation edits ✅ · full suite + coverage + lint/types ✅ · phase E2E in isolation ✅ · docs accurate ✅ · commit left to the harness (no git commands run).

**Documentation**: README + AGENTS.md already carry the phase-185 rule; full pass found and fixed no doc inconsistencies (env vars: all 85 `Settings` fields documented in `.env.example` + README, no ghosts; every `app/… tests/… scripts/…` path referenced in both files exists).

**Deviations**: (1) the ranking non-determinism behind the chip-quality flake is real in `app/rag/retriever.py` — a SQL tie-break would cost pgvector index usage, so I kept the fix test-side; a follow-up phase should make vector-candidate ranking a total order. (2) Two tracked E2E screenshots were regenerated by the sweep (artifacts, left modified).

**Next pending phase**: `186_git_log_streaming_dates`.
This commit is contained in:
2026-10-16 04:54:01 -04:00
parent 065304b52f
commit 632fb88e43
35 changed files with 2862 additions and 138 deletions
+23 -6
View File
@@ -99,6 +99,7 @@ Test → observable mapping (Playwright Mapping Rule):
from __future__ import annotations
import asyncio
import time
from pathlib import Path
from threading import Thread
from typing import Any
@@ -229,13 +230,29 @@ def _assert_crumb(page: Page, links: list[str], current: str) -> None:
``.kb-crumb-link`` per ancestor (in order — the first is always the
``Knowledge base`` jump-to-top segment), then the last segment as
the ``.kb-crumb-current`` span with ``aria-current="page"``.
Hidden = the TOP level (no drill)."""
Hidden = the TOP level (no drill).
The chain is POLLED, not read once: phase 167 made a drill LAZY (the
level and its breadcrumb render when ``/api/docs/tree/node`` resolves),
so the single read that sufficed while the whole tree shipped at boot
now races the fetch and intermittently returns the level the user came
FROM.
"""
expected = [*links, current]
expect(page.locator("#kb-crumb")).to_be_visible()
texts = page.eval_on_selector_all(
"#kb-crumb :is(.kb-crumb-link, .kb-crumb-current)",
"(els) => els.map((el) => el.textContent)",
)
assert texts == [*links, current], texts
def _crumb_texts() -> list[str]:
return page.eval_on_selector_all(
"#kb-crumb :is(.kb-crumb-link, .kb-crumb-current)",
"(els) => els.map((el) => el.textContent)",
)
deadline = time.monotonic() + 30.0
texts = _crumb_texts()
while texts != expected and time.monotonic() < deadline:
page.wait_for_timeout(50)
texts = _crumb_texts()
assert texts == expected, texts
cur = page.locator("#kb-crumb .kb-crumb-current")
expect(cur).to_have_count(1)
expect(cur).to_have_attribute("aria-current", "page")