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
+251
View File
@@ -0,0 +1,251 @@
"""Integration: the phase-106 D5 date surfaces against REAL Postgres
rows (task 06).
The two tool surfaces the model reads carry the document's creation
date: the ``read`` result's SECOND line (``date: YYYY-MM-DD`` — the
FIRST line stays the byte-identical ``Document {source}/{path}:``
header the E2E mock's ``_READ_RESULT_PREFIX`` contract keys on) and
the ``ls`` FILE line's APPENDED `` | date: YYYY-MM-DD`` field (the
mock's ``_CATALOG_LINE_RE`` ``title: .+$`` tail absorbs it). The rows
carry DISTINCT fixed ``created_at`` values, so the pins prove the date
is the ROW's date (per row), not a constant.
Requires: podman compose up -d db
"""
from __future__ import annotations
import asyncio
import uuid
from collections.abc import AsyncIterator, Iterator
from copy import deepcopy
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any, cast
import pytest
from sqlalchemy import delete, text
from sqlalchemy.orm import Session
from app.config import Settings
from app.models import Document, GitSource
from app.rag.agent import AgentHolder, run_agent
from app.rag.llm import LLMClient, RetryPiece, StreamPiece, ToolCallPiece, ToolResultPiece
if TYPE_CHECKING:
from app.rag.scaffolding import ScaffoldingFilter
#: DISTINCT fixed creation dates — the per-row date pins (task 06):
#: each document renders ITS OWN row's UTC date part.
D1 = datetime(2019, 6, 15, 3, 4, 6, tzinfo=UTC) # "2019-06-15"
D2 = datetime(2020, 1, 2, 5, 0, 0, tzinfo=UTC) # "2020-01-02"
D3 = datetime(2024, 6, 15, 23, 59, 59, tzinfo=UTC) # "2024-06-15" (late UTC instant)
D1_STR, D2_STR, D3_STR = "2019-06-15", "2020-01-02", "2024-06-15"
def _doc(
db: Session,
source: str,
path: str,
title: str,
content: str,
created_at: datetime,
) -> Document:
doc = Document(
id=uuid.uuid4(),
source=source,
path=path,
full_path=f"/tmp/{source}/{path}",
title=title,
content=content,
content_hash="0" * 64,
created_at=created_at, # D1: explicit — the pins prove per-row dates
)
db.add(doc)
return doc
@pytest.fixture()
def kb(db) -> Iterator[None]:
"""Fresh documents table (chunks first — the FK)."""
db.execute(text("TRUNCATE chunks, documents"))
db.commit()
yield
db.execute(text("TRUNCATE chunks, documents"))
db.commit()
@pytest.fixture()
def src(db) -> Iterator[GitSource]:
"""One registered git source — the scoped ``ls`` source-name check
reads the real registry (``repo_name`` resolves the URL to
``Homelab``)."""
row = GitSource(url="https://github.com/reese/Homelab.git", kind="git")
db.add(row)
db.commit()
yield row
db.execute(delete(GitSource).where(GitSource.id == row.id))
db.commit()
class ScriptedToolLLM:
"""One scripted tool-call stream, then one canned answer stream.
Records every ``chat_stream`` request's messages and tools."""
def __init__(self, call: ToolCallPiece) -> None:
self.call = call
self.requests: list[
tuple[list[dict[str, Any]], list[dict[str, Any]] | None]
] = []
async def chat_stream(
self,
messages: list[dict[str, str]],
tools: list[dict[str, Any]] | None = None,
scaffolding: ScaffoldingFilter | None = None,
) -> AsyncIterator[StreamPiece | ToolCallPiece]:
self.requests.append((deepcopy(messages), deepcopy(tools)))
if len(self.requests) == 1:
yield self.call
else:
yield StreamPiece("content", "ans")
def _settings(**kwargs: Any) -> Settings:
kwargs.setdefault("_env_file", None)
return Settings(**kwargs) # pyright: ignore[reportCallIssue]
def _run_call(
db: Session, name: str, arguments: dict[str, Any]
) -> tuple[AgentHolder, ScriptedToolLLM]:
"""Drive one scripted tool call through ``run_agent``."""
holder = AgentHolder()
llm = ScriptedToolLLM(ToolCallPiece(id="call_1", name=name, arguments=arguments))
asyncio.run(_consume(cast("LLMClient", llm), db, holder))
return holder, llm
async def _consume(
llm: LLMClient, db: Session, holder: AgentHolder
) -> list[StreamPiece | ToolCallPiece | RetryPiece | ToolResultPiece]:
out: list[StreamPiece | ToolCallPiece | RetryPiece | ToolResultPiece] = []
async for piece in run_agent(
llm,
db,
system_prompt="SYSTEM_PROMPT",
user_message="QUESTION",
seed_docs=[],
settings=_settings(),
holder=holder,
):
out.append(piece)
return out
# --------------------------------------------------------------------
# read — the date is the stored row's date, on the SECOND line
# --------------------------------------------------------------------
def test_read_result_second_line_is_stored_date(kb, db) -> None:
"""A real row (distinct ``created_at``): the ``read`` result's
SECOND line is the stored date (the UTC date part), the FIRST line
stays the byte-identical header, and the content follows whole."""
created = _doc(
db, "Alpha", "deep/nested/doc.md", "The Doc", "FULL-TEXT", D2
)
db.commit()
holder, llm = _run_call(db, "read", {"path": "Alpha/deep/nested/doc.md"})
content = llm.requests[1][0][3]["content"]
lines = content.splitlines()
assert lines[0] == "Document Alpha/deep/nested/doc.md:" # byte-identical header
assert lines[1] == f"date: {D2_STR}" # the STORED date (row's UTC date part)
assert lines[2:] == ["FULL-TEXT"]
assert holder.read_docs == [created]
assert holder.tool_calls == 1
def test_read_result_date_is_the_row_date_not_a_constant(kb, db) -> None:
"""Two rows with DISTINCT dates: each ``read`` renders its OWN
row's date (a late-UTC instant renders its date part, no time)."""
a = _doc(db, "Alpha", "a.md", "A", "A-TEXT", D1)
b = _doc(db, "Alpha", "b.md", "B", "B-TEXT", D3)
db.commit()
holder_a, llm_a = _run_call(db, "read", {"path": "Alpha/a.md"})
assert llm_a.requests[1][0][3]["content"] == (
f"Document Alpha/a.md:\ndate: {D1_STR}\nA-TEXT"
)
holder_b, llm_b = _run_call(db, "read", {"path": "Alpha/b.md"})
assert llm_b.requests[1][0][3]["content"] == (
f"Document Alpha/b.md:\ndate: {D3_STR}\nB-TEXT"
)
assert holder_a.read_docs == [a] and holder_b.read_docs == [b]
# --------------------------------------------------------------------
# ls — every FILE line carries its date in the appended field
# --------------------------------------------------------------------
def test_ls_drill_file_lines_carry_their_dates(kb, src, db) -> None:
"""A source drill against real rows: EVERY file line ends with the
appended `` | date: YYYY-MM-DD`` field — each row's OWN stored date
— while the header and subfolder lines stay date-free."""
_doc(db, "Homelab", "backups/cron.md", "Cron", "CRON", D1)
_doc(db, "Homelab", "backups/restic.md", "Restic", "RESTIC", D2)
_doc(db, "Homelab", "networking/lan.md", "LAN", "LAN", D3)
_doc(db, "Homelab", "readme.md", "Readme", "README", D1)
db.commit()
holder, llm = _run_call(db, "ls", {"path": "Homelab"})
content = llm.requests[1][0][3]["content"]
lines = content.splitlines()
# The root level: one direct file (readme.md — D1) + the two
# subfolder lines (date-free) + the date-free header.
assert lines[0] == "Homelab — 1 documents, 2 folders:"
assert lines[2] == " backups/ — 2 documents" # subfolder: no date
assert lines[3] == " networking/ — 1 documents" # subfolder: no date
assert lines[5] == (
f"source: Homelab | path: readme.md | title: Readme | date: {D1_STR}"
)
assert holder.tool_calls == 1
# Drill into backups: BOTH files list, each with its OWN date.
holder2, llm2 = _run_call(db, "ls", {"path": "Homelab/backups"})
lines2 = llm2.requests[1][0][3]["content"].splitlines()
assert lines2[0] == "Homelab/backups — 2 documents, 0 folders:"
assert lines2[2] == (
f"source: Homelab | path: backups/cron.md | title: Cron | date: {D1_STR}"
)
assert lines2[3] == (
f"source: Homelab | path: backups/restic.md | title: Restic | date: {D2_STR}"
)
assert holder2.tool_calls == 1
def test_ls_top_level_source_lines_carry_no_date(kb, db) -> None:
"""The top level (source lines) is UNCHANGED in shape — sources are
not documents, so no date rides them (only FILE lines do). The
registry is FRESH (truncated + the one source re-registered), so
the top level is exactly the one source block."""
db.execute(text("TRUNCATE git_sources"))
db.commit()
db.add(GitSource(url="https://github.com/reese/Homelab.git", kind="git"))
db.commit()
try:
_doc(db, "Homelab", "a.md", "A", "A-TEXT", D1)
db.commit()
holder, llm = _run_call(db, "ls", {})
content = llm.requests[1][0][3]["content"]
assert content == "1 sources:\n\nHomelab — 1 documents"
assert "date" not in content
assert holder.tool_calls == 1
finally:
db.execute(text("TRUNCATE git_sources"))
db.commit()