"""documents.page_strip_mode: the strip CLASS behind page_count (phase 163, task 01) Revision ID: 0027 Revises: 0026 Create Date: 2026-10-08 Phase 163 (the PDF page strip is ADDITIVE for a text-bearing book — task 01, storage only): * ``documents.page_strip_mode`` — VARCHAR(16) NULLABLE, NO server default, NO index: the CLASS of the page strip a non-NULL ``page_count`` describes, so the viewer knows whether the strip REPLACES the text or sits one toggle behind it. ``"primary"`` — the phase-133 FALLBACK class (an unusable text layer: the screenshots ARE the content), i.e. exactly the pre-phase view: the strip is the document's view and no text is shown. ``"additive"`` — the phase-140 PICTORIAL class (a usable text layer with ≥1 sparse page described): the document keeps its text view and its Summary panel, and the strip is one Text/Pages toggle away. NULL means NO strip (every non-qualifying document — text documents, images, text-heavy PDFs — and every qualifying PDF whose render/write failed). A row written before this phase keeps NULL while its ``page_count`` stands; the wire omits the key in that case and the viewer treats an absent mode as ``"primary"`` (today's DOM), so existing rows render exactly as before until the fast-path re-gate (task 03) stamps them. Stamped and cleared EXACTLY with ``page_count`` — the importer's persist (task 02) and the unchanged- path re-gate (task 03) are the only writers. A display fact, never a query surface, like the 0026 column it accompanies: the content endpoint reads it from the ROW after the ``(source, path)`` lookup, so it carries no index of its own (the ``file_hash`` convention, 0025). One additive, fully reversible migration (A13); no other schema change. The ordering of the copies on disk (task 02) and the re-gate itself (task 03) are importer code — this revision only carries the column. """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "0027" down_revision = "0026" branch_labels = None depends_on = None def upgrade() -> None: # No index (a display fact, never a query surface — the content # endpoint reads it from the row) and no server default (NULL is the # meaningful state for every non-qualifying document, every fail-soft # miss, and every pre-phase row). op.add_column( "documents", sa.Column("page_strip_mode", sa.String(length=16), nullable=True) ) def downgrade() -> None: # The page_strip_mode column is the only 0027 artefact — dropping it # leaves 0026's schema byte-identical (A13, fully reversible). The # class is re-derivable from the source file on the next sync (the # deterministic re-gate of task 03), so no data is lost by the drop. op.drop_column("documents", "page_strip_mode")