phase: 163_pdf_strip_additive_selfheal
All gates verified. Final report: **Phase 163 — final verification pass (all 5 tasks already complete; verification-only run)** - Verified D1–D4 in code: `page_strip_mode` column + migration 0027, `DocContent` absent-never-null rule keyed off `page_count`; `_persist_pdf_pages` render → atomic tmp+`os.replace` writes → stale-tail-last + `strip_class` stamp; `_regate_pdf_strip` on both unchanged paths (mirror gate, heal, repair, fail-soft, one INFO line only when acting); viewer mode branch + Text/Pages toggle (Text default, `aria-pressed`, no innerHTML, lazy imgs). - `uv run pytest --cov=app --cov-report=term-missing` → **3796 tests, exit 0, coverage 99%** (>90%) - `uv run pytest tests/e2e/test_pdf_strip_additive_selfheal.py -v --no-cov` → **5 passed** in isolation - `test_pdf_pages_in_sources + test_pictorial_pdf + test_garbage_pdf` → 10 passed; `test_document_viewer(_context) + test_image_documents` → 20 passed - `uv run ruff check .` clean; `uv run pyright` → 0 errors; alembic **0027 downgrade→upgrade CLI round-trip clean** (+ integration pins) - Completion criteria: additive view w/ toggle ✅ · primary DOM byte-identity ✅ · write-first order/mid-write-failure/atomic pins ✅ · heal + repair E2E ✅ · zero vision/LLM on fast path (mock-captured) ✅ · non-qualifying payload byte-identity ✅ · docs pass ✅ - **Documentation**: README.md + AGENTS.md reviewed in full — the phase's updates are accurate; all config-table defaults cross-checked against `app/config.py` (no drift), every page route, script, and E2E command referenced exists, every `.env.example` var documented — **verified consistent, no changes needed**. - Notable: live `Letumdei` repair is owner-runtime post-deploy per D5 (next production Sync self-heals; no live changes made); the modified `.agents/screenshots/doc_modal_scrollbar_1280.png` is an E2E-regenerated artifact left for the pipeline commit. - **Next pending phase:** `164_chat_error_honesty`
This commit is contained in:
@@ -293,6 +293,82 @@ def test_git_source_python_default_include_hidden_false() -> None:
|
||||
assert col.default.arg is False, "the default must resolve to False"
|
||||
|
||||
|
||||
def test_documents_page_strip_mode_column_contract() -> None:
|
||||
"""Phase 163 (task 01): ``documents.page_strip_mode`` — the CLASS of
|
||||
the page strip a non-NULL ``page_count`` describes (``"primary"`` =
|
||||
the phase-133 fallback class, the strip IS the view; ``"additive"``
|
||||
= the phase-140 pictorial class, text + a Text/Pages toggle):
|
||||
VARCHAR(16), NULLABLE (NULL = no strip — and the pre-phase shape for
|
||||
a row whose strip stands), NO server default, and NO index (the
|
||||
content endpoint reads it from the ROW — a display fact, never a
|
||||
query surface, the ``file_hash`` / ``page_count`` convention).
|
||||
``page_count`` (0026) stays exactly as phase 148 left it."""
|
||||
from sqlalchemy import String as SAString
|
||||
|
||||
documents = Base.metadata.tables["documents"]
|
||||
assert "page_strip_mode" in documents.c, (
|
||||
"documents must have the page_strip_mode column (phase 163)"
|
||||
)
|
||||
col = documents.c["page_strip_mode"]
|
||||
assert col.nullable is True, (
|
||||
"page_strip_mode must be NULLABLE — NULL means no strip (and the"
|
||||
" pre-phase row whose strip stands)"
|
||||
)
|
||||
assert isinstance(col.type, SAString), "page_strip_mode must be a String column"
|
||||
assert col.type.length == 16, (
|
||||
"page_strip_mode must be String(16) — the two class names, bounded"
|
||||
)
|
||||
assert col.server_default is None, (
|
||||
"page_strip_mode must carry NO server default — NULL is the"
|
||||
" meaningful pre-phase state"
|
||||
)
|
||||
assert not col.index, (
|
||||
"page_strip_mode must be UNINDEXED — the content endpoint reads it"
|
||||
" from the row, no query filters on it"
|
||||
)
|
||||
covering = [
|
||||
idx
|
||||
for idx in documents.indexes
|
||||
if any(c.name == "page_strip_mode" for c in idx.columns)
|
||||
]
|
||||
assert not covering, "no table-level index may cover page_strip_mode"
|
||||
|
||||
# page_count is untouched: still the nullable, unindexed sibling.
|
||||
page_count = documents.c["page_count"]
|
||||
assert page_count.nullable is True, "page_count must stay NULLABLE (0026)"
|
||||
assert not page_count.index, "page_count must stay UNINDEXED (0026)"
|
||||
|
||||
|
||||
def test_document_page_strip_mode_orm_defaults_to_none() -> None:
|
||||
"""A freshly constructed ``Document`` (no DB) exposes
|
||||
``page_strip_mode``: an omitted kwarg and an explicit ``None`` both
|
||||
read NULL (no strip — the shape every non-qualifying document keeps,
|
||||
and the shape a pre-phase-163 row keeps WHILE its ``page_count``
|
||||
stands, which the wire serializes ABSENT), and each class name
|
||||
round-trips verbatim as a plain string (the real DB round-trip is
|
||||
pinned in ``test_migration_0027.py``)."""
|
||||
from app.models import Document
|
||||
|
||||
base = dict(
|
||||
source="s",
|
||||
path="p.pdf",
|
||||
full_path="/tmp/s/p.pdf",
|
||||
title="p",
|
||||
content="c",
|
||||
content_hash="1" * 64,
|
||||
page_count=36,
|
||||
)
|
||||
for mode in ("primary", "additive"):
|
||||
assert Document(**base, page_strip_mode=mode).page_strip_mode == mode
|
||||
|
||||
assert Document(**base, page_strip_mode=None).page_strip_mode is None
|
||||
omitted = Document(**base)
|
||||
assert omitted.page_strip_mode is None, (
|
||||
"an omitted page_strip_mode must read NULL while page_count stands"
|
||||
" (the pre-phase strip the viewer keeps showing as 'primary')"
|
||||
)
|
||||
|
||||
|
||||
def test_doc_drafts_column_contract() -> None:
|
||||
"""Phase 59: the editable triple (title/path/body) + status +
|
||||
timestamps are NOT NULL; ``branch`` / ``commit_sha`` are NULL
|
||||
|
||||
Reference in New Issue
Block a user