phase: 106_document_dates
Build and Push Containers / build-and-push-app (push) Successful in 4m35s
Build and Push Containers / build-and-push-db (push) Successful in 14s

Everything is verified green. Final report:

**Phase 106 — Document dates (final verification pass; all 10 tasks already complete)**

- Verified all phase artifacts: alembic `0020` (dev DB at `0020`), `doc_dates.py`, git `file_commit_dates`, importer `doc_dates_by_root`/`dates_updated`, both entry-point wirings, date APIs + tree `created_at`/`updated_at`, LLM surfaces (prompt block, `read` line 2, appended `ls` field), `apply_recency_boost` in `retrieve()`, UI columns/badge, admin editor, mock-LLM regex — all present and correct; no defects found, no fixes needed.
- `uv run pytest --cov=app --cov-report=term-missing` → **2299 passed, TOTAL 99%** (>90% ✓)
- `uv run pytest tests/e2e/test_document_dates.py -v --no-cov` → **6/6 passed** in isolation (DB up)
- 12 regression E2E suites (retrieval_quality, whole_document_context, agent_document_tools, ls_tree_drilldown, read_truncation_cap, kb_tree, kb_tree_nav, document_viewer, edit_summaries, import_documents, sync_button, hidden_folders_toggle, smoke) → **all green in isolation**
- `uv run ruff check .` → clean; `uv run pyright` → **0 errors, 0 warnings**

**Completion criteria:** 1) non-null `created_at` + 0020 upgrade/downgrade on dev DB ✓ (real-Alembic integration tests) 2) sync refresh/older/manual-persists/content-reset/no sources_meta bump ✓ 3) zip/tar mtime + future→today ✓ 4) LLM date surfaces + cross-check ✓ 5) UI Created/Updated/badge positions ✓ 6) admin editor set+revert round-trip ✓ 7) old-correct-beats-new-similar (defaults & boost-off) + near-tie + `BOR_RECENCY_BOOST=0` byte-identical ✓ 8) full gate ✓ 9) commit/phase-move — left to harness per instructions.

- **Notable:** recency default tuned 0.001 → **0.0007** (task 07 step 5 explicitly permits; measured margins recorded in `test_recency_boost.py` docstring).
- **Next pending phase:** none — `todo/` holds only this phase.
This commit is contained in:
2026-09-13 19:28:05 -04:00
parent cec819743d
commit ee3efb28c9
113 changed files with 8228 additions and 344 deletions
+24 -11
View File
@@ -3,8 +3,9 @@
The RAG (Knowledge base) view lists the catalog the way the agent's
``ls`` sees it (the phase-94 concept, ONE end to end): sources at the
top, then per level the subfolders (recursive count + the STORED
description) and the level's files (the existing 5-column
``#docs-table`` — unchanged). The view's catalog load is now
description) and the level's files (the ``#docs-table`` — phase 106,
task 08, D8, added the Created column BEFORE Indexed, making it the
6-column contract). The view's catalog load is now
``loadTree()``: ONE fetch of ``GET /api/docs/tree`` (the full recursive
tree in a single payload — task 02), then client-side drill navigation
(no per-level fetch, no URL change).
@@ -17,8 +18,12 @@ suite, task 06):
* the shell's RAG view ships the static tree skeleton (``#kb-crumb``,
``#kb-level`` + ``#kb-level-title``/``#kb-level-summary``,
``#folders-wrap`` + ``#folders-table`` — ONE table for every level,
the Folder | Documents | Description head) in order after
``#stat-cards`` and BEFORE the unchanged file table;
the Folder | Documents | Updated | Description head — phase 106
(task 08, D8) added the Updated column (the subtree's MAX document
created_at, D9) BETWEEN Documents and Description) in order after
``#stat-cards`` and BEFORE the file table (now the 6-column
Source | Path | Title | Chunks | Created | Indexed head — phase 106
(task 08, D8) added the Created column BEFORE Indexed);
* the exact ``/api/docs/tree`` fetch (and the flat ``/api/docs`` fetch
is gone from the view module);
* the drill state (``current`` / ``kbTree``), the state transitions
@@ -189,8 +194,10 @@ def test_rag_view_ships_the_tree_skeleton_in_order() -> None:
``#kb-crumb`` is the location nav; ``#kb-level`` is the level block
(title + description); ``#folders-wrap`` hosts the ONE
folders/sources table (``.table-wrap`` card, Folder | Documents |
Description, visually-hidden caption, the ``.docs-table``
language + ``.kb-folders-table``)."""
Updated | Description — phase 106 (task 08, D8), visually-hidden
caption, the ``.docs-table`` language + ``.kb-folders-table``).
The file table's head is the 6-column contract (Source | Path |
Title | Chunks | Created | Indexed — phase 106 (task 08, D8))."""
view = _rag_view(_text(SHELL_HTML))
for fragment in (
'<nav id="kb-crumb" class="kb-crumb" aria-label="Catalog location" hidden></nav>',
@@ -209,6 +216,7 @@ def test_rag_view_ships_the_tree_skeleton_in_order() -> None:
for column in (
"<th scope=\"col\">Folder</th>",
"<th scope=\"col\">Documents</th>",
"<th scope=\"col\">Updated</th>", # phase 106 (task 08, D8)
"<th scope=\"col\">Description</th>",
):
assert column in head.group(1), f"#folders-table head must carry {column!r}"
@@ -221,12 +229,13 @@ def test_rag_view_ships_the_tree_skeleton_in_order() -> None:
< view.find('id="folders-wrap"')
< view.find('id="docs-table"')
), "the skeleton must sit between the stat cards and the file table"
# The file table is UNCHANGED (the 5-column contract, makeRow's home).
# The file table's head (the 6-column contract — phase 106 (task
# 08, D8) added Created BEFORE Indexed; makeRow's home).
doc_head = re.search(
r'<table class="docs-table" id="docs-table">.*?<thead>(.*?)</thead>', view, re.S
)
assert doc_head, "the file table must keep its static thead"
for column in ("Source", "Path", "Title", "Chunks", "Indexed"):
for column in ("Source", "Path", "Title", "Chunks", "Created", "Indexed"):
assert f">{column}</th>" in doc_head.group(1), f"#docs-table head must keep {column!r}"
@@ -287,7 +296,9 @@ def test_source_row_click_drills_into_the_source_root() -> None:
The row carries the recursive count and the stored (source, "")
description — textContent only."""
js = _js()
body = js[js.find("function makeSourceRow(") : js.find("function makeSourceRow(") + 1200]
# Slice to the next function (the row builders grew with phase 106's
# Updated cell — a fixed window would drift out of the function).
body = js[js.find("function makeSourceRow(") : js.find("function makeFolderRow(")]
assert 'link.className = "folder-link"' in body, "the source row uses the row drill link"
assert "link.textContent = s.name" in body
assert "goTo({ source: s.name, folder: \"\" })" in body, (
@@ -309,7 +320,7 @@ def test_folder_row_click_drills_into_the_folder() -> None:
`{ source, folder: f.path }` (the source-relative folder path)."""
js = _js()
start = js.find("function makeFolderRow(")
body = js[start : start + 1400]
body = js[start : js.find("function renderLevel(")]
assert 'link.className = "folder-link"' in body
assert "link.textContent = f.path.split(\"/\").pop()" in body, (
"the label is the last segment (the breadcrumb language)"
@@ -1370,7 +1381,9 @@ def test_styles_carry_the_one_line_clamp() -> None:
"white-space: nowrap",
):
assert prop in text, f".kb-desc-text must carry {prop!r}"
col_i = css.find(".kb-folders-table td:nth-child(3) {")
# Phase 106 (task 08): the clamp follows the Description cell, which
# moved to the 4th column (Updated took 3rd).
col_i = css.find(".kb-folders-table td:nth-child(4) {")
assert col_i != -1
col = css[col_i : col_i + 400]
col = col[: col.find("\n}")]