phase: 106_document_dates
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:
+72
-7
@@ -235,6 +235,10 @@ class DocSummary(BaseModel):
|
||||
path: str
|
||||
title: str
|
||||
chunks: int
|
||||
#: The document's creation date (phase 106, D8) — ISO-8601, verbatim
|
||||
#: from the row (the ``indexed_at`` style); the RAG view's file table
|
||||
#: renders it as the ``Created`` column (before ``Indexed``).
|
||||
created_at: str
|
||||
indexed_at: str
|
||||
|
||||
|
||||
@@ -251,7 +255,8 @@ class KbTreeFile(BaseModel):
|
||||
JSON shape is the contract). ``path`` is SOURCE-RELATIVE (the RAG
|
||||
view prefixes the source in its breadcrumb); ``title`` /
|
||||
``chunks`` (content + ``is_summary`` chunks — the same count
|
||||
``GET /api/docs`` returns) / ``indexed_at`` (ISO-8601) are verbatim
|
||||
``GET /api/docs`` returns) / ``created_at`` (phase 106, D8 — the
|
||||
document's creation date) / ``indexed_at`` (ISO-8601) are verbatim
|
||||
from the catalogue row the endpoint reads.
|
||||
"""
|
||||
|
||||
@@ -259,6 +264,10 @@ class KbTreeFile(BaseModel):
|
||||
path: str
|
||||
title: str
|
||||
chunks: int = Field(ge=0)
|
||||
#: The document's creation date (phase 106, D8) — ISO-8601, verbatim
|
||||
#: from the catalogue row; the RAG view's file table renders it as
|
||||
#: the ``Created`` column (before ``Indexed``).
|
||||
created_at: str
|
||||
indexed_at: str
|
||||
|
||||
|
||||
@@ -269,11 +278,14 @@ class KbTreeFolder(BaseModel):
|
||||
node IS the root); ``documents`` is the recursive subtree count
|
||||
(the phase-94 ``ls`` count rule: every path equal to the folder or
|
||||
starting with ``folder + "/"`` — the file sharing a folder's name
|
||||
counts); ``summary`` is the stored ``folder_summaries`` row (AI or
|
||||
manual — any row) or null; ``children`` are the direct subfolders
|
||||
(path order) followed by the direct files (catalog order) — the
|
||||
recursive union (Pydantic v2 resolves it with
|
||||
``from __future__ import annotations``).
|
||||
counts); ``updated_at`` (phase 106, D9) is the subtree's MAX
|
||||
document ``created_at`` — DERIVED in the pure tree builder as it
|
||||
recurses, never stored (``null`` for a node with no documents at
|
||||
all — the ``summary: str | None`` shape); ``summary`` is the stored
|
||||
``folder_summaries`` row (AI or manual — any row) or null;
|
||||
``children`` are the direct subfolders (path order) followed by the
|
||||
direct files (catalog order) — the recursive union (Pydantic v2
|
||||
resolves it with ``from __future__ import annotations``).
|
||||
|
||||
``summary_pending`` (phase 98, D3 — ONE concept): true iff this
|
||||
folder's recursive count ≥ ``MIN_DOCS_PER_FOLDER`` (2) AND it has
|
||||
@@ -287,6 +299,10 @@ class KbTreeFolder(BaseModel):
|
||||
kind: Literal["folder"] = "folder"
|
||||
path: str
|
||||
documents: int = Field(ge=0)
|
||||
#: The subtree's MAX document ``created_at`` (phase 106, D9 —
|
||||
#: derived in the pure builder, never stored); ISO-8601, ``null``
|
||||
#: for a node with no documents at all.
|
||||
updated_at: str | None = None
|
||||
summary: str | None = None
|
||||
summary_pending: bool = False
|
||||
children: list[KbTreeFolder | KbTreeFile] = Field(default_factory=list)
|
||||
@@ -299,7 +315,10 @@ class KbTreeSource(BaseModel):
|
||||
always present, a registered 0-document source lists with
|
||||
``documents: 0`` and no children), then the indexed-only sources
|
||||
(alphabetical) — the phase-97 superset rule. ``documents`` is the
|
||||
source's whole recursive count; ``summary`` is the stored
|
||||
source's whole recursive count; ``updated_at`` (phase 106, D9) is
|
||||
the source's subtree MAX document ``created_at`` — DERIVED in the
|
||||
pure tree builder, never stored (``null`` for a 0-document source —
|
||||
the ``summary: str | None`` shape); ``summary`` is the stored
|
||||
``(source, "")`` source-root row or null; ``children`` are the
|
||||
source's direct subfolders + direct files (same shape as a folder
|
||||
node's).
|
||||
@@ -316,6 +335,10 @@ class KbTreeSource(BaseModel):
|
||||
|
||||
name: str
|
||||
documents: int = Field(ge=0)
|
||||
#: The source's subtree MAX document ``created_at`` (phase 106,
|
||||
#: D9 — derived in the pure builder, never stored); ISO-8601,
|
||||
#: ``null`` for a 0-document source.
|
||||
updated_at: str | None = None
|
||||
summary: str | None = None
|
||||
summary_pending: bool = False
|
||||
children: list[KbTreeFolder | KbTreeFile] = Field(default_factory=list)
|
||||
@@ -350,6 +373,10 @@ class DocContent(BaseModel):
|
||||
#: markdown documents, pre-phase-30 rows, and the fail-soft path where
|
||||
#: summary generation failed but the document was still indexed.
|
||||
summary: str | None = None
|
||||
#: The document's creation date (phase 106, D8) — ISO-8601, verbatim
|
||||
#: from the row; the viewer's top meta row renders the ``Created``
|
||||
#: badge from it (before the ``Indexed`` badge).
|
||||
created_at: str
|
||||
content: str
|
||||
indexed_at: str
|
||||
chunks: int
|
||||
@@ -389,6 +416,44 @@ class SummaryResult(BaseModel):
|
||||
chunks: int
|
||||
|
||||
|
||||
class DateUpdate(BaseModel):
|
||||
"""``PATCH /api/documents/date`` body (phase 106, task 05, D7).
|
||||
|
||||
``source`` / ``path`` name the indexed document (the same pair the
|
||||
public ``GET /api/documents/content`` looks up); ``date`` is the
|
||||
owner's corrected creation date — an ISO date (``YYYY-MM-DD``) or a
|
||||
full ISO datetime. **Null/absent is the CLEAR** (the "revert to
|
||||
sync" operation, D7): the ``created_at_manual`` flag is dropped and
|
||||
the stored date stands until the next sync refreshes it (the API is
|
||||
DB-only — it cannot re-read the source). Unconstrained
|
||||
``str | None`` on purpose: a MALFORMED non-null value 422s in the
|
||||
handler (``datetime.fromisoformat``), so the error detail can name
|
||||
the field; an unknown pair must 404 as "document not found"
|
||||
(row-lookup semantics), exactly like the public content endpoint.
|
||||
"""
|
||||
|
||||
source: str
|
||||
path: str
|
||||
date: str | None = None
|
||||
|
||||
|
||||
class DateResult(BaseModel):
|
||||
"""``PATCH /api/documents/date`` response (phase 106, task 05, D7).
|
||||
|
||||
Echoes the STORED state after the change: ``created_at`` is the
|
||||
stored ISO-8601 value (a set stores the normalized parse — a
|
||||
manually set FUTURE date folds to today, D3; a clear leaves the
|
||||
stored date standing) and ``created_at_manual`` the flag (true
|
||||
after a set, false after a clear). The viewer re-renders its
|
||||
Created badge from exactly this echo — no second fetch.
|
||||
"""
|
||||
|
||||
source: str
|
||||
path: str
|
||||
created_at: str
|
||||
created_at_manual: bool
|
||||
|
||||
|
||||
class FolderSummaryUpdate(BaseModel):
|
||||
"""``PATCH /api/folders/summary`` body (phase 97, task 03).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user