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.
70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
"""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")
|