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:
@@ -0,0 +1,69 @@
|
||||
"""documents.created_at + created_at_manual: sourced document creation dates (phase 106)
|
||||
|
||||
Revision ID: 0020
|
||||
Revises: 0019
|
||||
Create Date: 2026-09-13
|
||||
|
||||
Phase 106 (document dates end to end: sourced at sync, displayed,
|
||||
editable, recency-weighted in retrieval): every ``documents`` row carries
|
||||
a NOT-NULL **creation date** — sourced at sync time (the git last-commit
|
||||
date for git sources, the file mtime for local dirs and unpacked uploads
|
||||
— D2) and normalized by ``app.rag.doc_dates.normalize_doc_date``
|
||||
(undetermined or future → today; stored as UTC ``timestamptz`` at full
|
||||
precision — D3). It is DISTINCT from ``indexed_at`` (the INDEX time,
|
||||
untouched — the two concepts stay separate):
|
||||
|
||||
* ``documents.created_at`` — TIMESTAMP WITH TIME ZONE NOT NULL with
|
||||
server default ``now()``: pre-phase-106 rows backfill to the migration
|
||||
moment (≈ today — the owner's instruction, D1: "the timestamp can't be
|
||||
null so just set it to today's date during the migration") and the
|
||||
next sync replaces them with the real sourced date (the importer's
|
||||
unchanged path, phase 106 task 04 — a sync may move a date OLDER, D4).
|
||||
* ``documents.created_at_manual`` — BOOLEAN NOT NULL with server default
|
||||
``false``: ``true`` only while ``created_at`` is the OWNER'S
|
||||
correction (D1 — the ``folder_summaries.manually_edited`` phase-97
|
||||
precedent). Set ONLY by ``PATCH /api/documents/date`` (phase 106 task
|
||||
05); the sync-time importer SKIPS the refresh on a manual row (the
|
||||
correction survives syncs) and a content change RESETS both the date
|
||||
and the flag (a new version = a new date — D4).
|
||||
|
||||
One additive, fully reversible migration (A13); no other schema change.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "0020"
|
||||
down_revision = "0019"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"documents",
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"documents",
|
||||
sa.Column(
|
||||
"created_at_manual",
|
||||
sa.Boolean(),
|
||||
server_default=sa.text("false"),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Both columns are the only 0020 artefacts — dropping them leaves
|
||||
# 0019's schema byte-identical (A13, fully reversible).
|
||||
op.drop_column("documents", "created_at_manual")
|
||||
op.drop_column("documents", "created_at")
|
||||
Reference in New Issue
Block a user