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
+48 -8
View File
@@ -28,6 +28,7 @@ from __future__ import annotations
import re
from collections.abc import Iterator
from datetime import datetime
from pathlib import Path
import pytest
@@ -93,11 +94,13 @@ class FakeImportSources:
limit: int | None = None,
ignore_by_root: dict[str, list[str]] | None = None, # phase 89
include_hidden_by_root: dict[str, bool] | None = None, # phase 105
doc_dates_by_root: dict[str, dict[str, datetime]] | None = None, # phase 106
) -> ImportSummary:
self.calls.append(
{"sources": list(sources), "prune": prune, "limit": limit,
"ignore_by_root": ignore_by_root,
"include_hidden_by_root": include_hidden_by_root}
"include_hidden_by_root": include_hidden_by_root,
"doc_dates_by_root": doc_dates_by_root}
)
return ImportSummary(files=1, added=1)
@@ -194,7 +197,7 @@ def test_resolve_sources_git_urls_cloned_into_sources_dir(
sources_dir=str(tmp_path / "bor"),
)
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, settings)
assert sources == [tmp_path / "bor" / "homelab", tmp_path / "bor" / "deploy"]
assert ignore_map == {} # phase 89: no row carries a list → empty map
@@ -204,6 +207,13 @@ def test_resolve_sources_git_urls_cloned_into_sources_dir(
str(tmp_path / "bor" / "homelab"): False,
str(tmp_path / "bor" / "deploy"): False,
}
# Phase 106: git rows are listed with their checkout's date walk —
# the fake checkouts are not git repos, so the walk fails soft to
# ``{}`` (the importer would then take the mtime fallback).
assert date_map == {
str(tmp_path / "bor" / "homelab"): {},
str(tmp_path / "bor" / "deploy"): {},
}
assert calls == [
("https://host/a/homelab.git", tmp_path / "bor" / "homelab"),
("git@host:user/deploy.git", tmp_path / "bor" / "deploy"),
@@ -218,11 +228,14 @@ def test_resolve_sources_cli_source_wins(
settings = _settings(git_sources="https://host/a/repo.git")
manual = tmp_path / "Manual"
sources, ignore_map, hidden_map = import_docs._resolve_sources([manual], settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources([manual], settings)
assert sources == [manual]
assert ignore_map == {} # phase 89: manual dirs have no rows → no ignore
assert hidden_map == {} # phase 105: manual dirs have no rows → hidden skipped
# Phase 106: manual dirs have no rows (no clone) → no date map
# entries (the importer's mtime fallback applies).
assert date_map == {}
assert calls == [] # git is never touched when --source is given
@@ -243,12 +256,14 @@ def test_resolve_sources_db_rows_win_over_env(
sources_dir=str(tmp_path / "bor"),
)
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, settings)
assert sources == [tmp_path / "bor" / "only"]
assert ignore_map == {} # phase 89: no row carries a list → empty map
# Phase 105: the default-flag row contributes its root with False (A4).
assert hidden_map == {str(tmp_path / "bor" / "only"): False}
# Phase 106: the git row's (fake, non-repo) checkout fails soft → {}.
assert date_map == {str(tmp_path / "bor" / "only"): {}}
assert calls == [("https://db.example/only.git", tmp_path / "bor" / "only")]
@@ -257,10 +272,13 @@ def test_resolve_sources_defaults_when_nothing_configured(
) -> None:
# Both origins empty (the resolver's ``([], "env")``) → legacy dirs.
monkeypatch.setattr(import_docs, "effective_sources", lambda db: ([], "env"))
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, _settings())
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, _settings())
assert sources == [p.expanduser() for p in import_docs.DEFAULT_SOURCES]
assert ignore_map == {} # phase 89: the legacy fallback has no rows
assert hidden_map == {} # phase 105: the legacy fallback has no rows
# Phase 106: the legacy fallback has no rows (no clone) → mtime
# fallback for every file.
assert date_map == {}
def test_resolve_sources_rows_branch_builds_ignore_map(
@@ -286,13 +304,16 @@ def test_resolve_sources_rows_branch_builds_ignore_map(
)
settings = _settings(sources_dir=str(tmp_path / "bor"))
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, settings)
assert sources == [tmp_path / "bor" / "only", local_dir]
# Keyed by the SAME string the importer sees (the root, not the name).
assert ignore_map == {str(local_dir): ["ignore/"]}
# Phase 105: both rows are flag-off → per-root False entries (A4).
assert hidden_map == {str(tmp_path / "bor" / "only"): False, str(local_dir): False}
# Phase 106: ONLY the git row is listed (local rows take the mtime
# fallback); the fake checkout's date walk fails soft to ``{}``.
assert date_map == {str(tmp_path / "bor" / "only"): {}}
def test_resolve_sources_two_rows_sharing_root_string_extend(
@@ -317,7 +338,7 @@ def test_resolve_sources_two_rows_sharing_root_string_extend(
)
settings = _settings(sources_dir=str(tmp_path / "bor"))
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, settings)
shared = str(tmp_path / "bor" / "shared")
assert sources == [tmp_path / "bor" / "shared", tmp_path / "bor" / "shared"]
@@ -325,6 +346,10 @@ def test_resolve_sources_two_rows_sharing_root_string_extend(
# Phase 105 collision: the shared root gets the OR of the flags —
# both rows off here, so one False entry for the one root string.
assert hidden_map == {shared: False}
# Phase 106 collision: both git rows resolve to the SAME root — one
# date walk for the one root string (last row's walk wins, both
# fail soft to ``{}`` for the fake checkout).
assert date_map == {shared: {}}
def test_main_rows_branch_passes_ignore_map_to_import(
@@ -362,6 +387,9 @@ def test_main_rows_branch_passes_ignore_map_to_import(
# Phase 105: the default-flag row passes the per-root map too — a
# False entry, not an absent key (the importer reads it per root).
assert call["include_hidden_by_root"] == {str(local_dir): False}
# Phase 106: the local-only resolution contributes no date map
# (no clone — the importer's mtime fallback applies).
assert call["doc_dates_by_root"] == {}
assert call["prune"] is False # the CLI's no-prune default is unchanged
@@ -539,6 +567,12 @@ def test_main_git_sources_clone_then_import(
tmp_path / "bor" / "homelab",
tmp_path / "bor" / "deploy",
]
# Phase 106: the git rows' (fake, non-repo) checkouts fail soft to
# ``{}`` — but the roots ARE listed (the CLI feeds the map).
assert fake_import.calls[0]["doc_dates_by_root"] == {
str(tmp_path / "bor" / "homelab"): {},
str(tmp_path / "bor" / "deploy"): {},
}
for dest in (tmp_path / "bor" / "homelab", tmp_path / "bor" / "deploy"):
assert (dest / "notes.md").is_file()
# The final summary print reflects the import (added > 0).
@@ -574,6 +608,9 @@ def test_main_cli_source_still_imports_manual_dir(
assert rc == 0
assert calls == []
assert fake_import.calls[0]["sources"] == [manual]
# Phase 106: manual --source has no rows (no clone) → no date map
# (the importer's mtime fallback applies).
assert fake_import.calls[0]["doc_dates_by_root"] == {}
assert fake_import.calls[0]["prune"] is False
# Phase 53: a manual --source run that changes the KB bumps exactly
# once (the CLI is the other canonical sync path).
@@ -605,12 +642,15 @@ def test_resolve_sources_mixed_git_and_local(
sources_dir=str(tmp_path / "bor"),
)
sources, ignore_map, hidden_map = import_docs._resolve_sources(None, settings)
sources, ignore_map, hidden_map, date_map = import_docs._resolve_sources(None, settings)
assert sources == [tmp_path / "bor" / "only", local_dir]
assert ignore_map == {} # phase 89: neither row carries a list
# Phase 105: both rows default-flag → per-root False entries (A4).
assert hidden_map == {str(tmp_path / "bor" / "only"): False, str(local_dir): False}
# Phase 106: only the git row is listed (local takes the mtime
# fallback); the fake checkout's date walk fails soft to ``{}``.
assert date_map == {str(tmp_path / "bor" / "only"): {}}
assert calls == [("https://db.example/only.git", tmp_path / "bor" / "only")]