Story 2483: addressable document pages v2

Why this exists

Textract's vaulted response is immutable evidence, but downstream readers need a small, queryable address book. This Story projects each page into text plus blocks with version-scoped opaque anchors. The same vaulted bytes therefore recreate the same page rows and anchors without paying the provider again.

The fixture-only path

vaulted Textract JSON from Story 2482
  → claim one in-flight extraction version
  → project page text + LAYOUT blocks
  → mint deterministic, opaque anchor IDs
  → publish one current version
  → mark chunk/assertion artifacts from older versions stale

existing document_pages v1 → remains the only serving surface
No serving cutover. documents.document_pages_v2 is a shadow fixture register. There are no endpoints, DTO changes, or reads from the live document surface. T2a/T2b own the later atomic cutover.

Three tables, three jobs

TableAuthority
documents.extraction_versions Serializes re-extraction: at most one in_flight version and one canonical current version per document.
documents.document_pages_v2 One row per page and extraction version: page text, generated search vector, and JSONB blocks carrying anchor, kind, text, bbox, confidence, and character offsets.
documents.extraction_artifact_staleness An absence-means-fresh marker register. context.chunk and context.assertion are today's artifact kinds; later Deep Read envelopes and findings register by data, not a schema migration.
Tenant boundary. All three tables carry firm_id, firm-leading indexes, forced RLS, and composite foreign keys that keep a page or marker attached to the same firm's document and extraction version. A second firm sees zero rows even when the first firm's rows exist.

Fixture proof

Recall

The vault is the immutable observation; v2 pages are its deterministic address book. They are safe to rebuild, impossible to mix across firms, and deliberately invisible to serving code until the atomic cutover Story.