Story-3442 · pramaan-functions · deterministic evidence

The index localizes 21/21 ranges exactly

21/21 ranges · 42/42 endpoints · 100% coordinate localization. Each index start/end lands on a PDF page carrying the expected internal printed number. This is a location baseline, not independent segmentation accuracy: in the hand-verified opening window the index predicts Annexure A at page 60 but misses the affidavit at page 58 (precision 100%, recall 50%, F1 0.667 over those two internal seams).

The assertion that makes the coordinate mapping real

EvidenceValue
Persisted index rowAnnexure A claims printed pages 58–96
Internal page-number blockPrinted 58 occurs on physical PDF page 60
Inferred mappingPDF = printed + 2
Mapped Annexure A rangePDF pages 60–98

The focused test asserts the exact 58 → 60 case. The measurement command asserts the offset again before printing a result, so a parser that silently reverts to zero offset cannot produce a green receipt.

What now exists

document_pages_v2.blocks
  ├─ table.cells (row / col / text) ──► claimed labels and printed ranges
  └─ kind="page_number" ─────────────► printed-to-PDF offset
                                      │
                                      ▼
                         cardinality · identity · location
                                      │
                                      ├─ anomaly list
                                      └─ missing / surplus reconciliation

src/shared/context/index_manifest.py reads the persisted shape already produced by Textract projection. It makes no model call, adds no schema, and needs no cloud capability. Story-3443 is the named production caller that will feed these three independent inputs to the deterministic combiner; Story-3442's measurement command is the first caller.

Ambiguity stays visible

ConditionOutput
Offset changes mid-bundleoffset_changed; locations remain unmapped
Roman-numeral prefaceroman_numeral_preface
Range gap or overlapgap / overlap
Inserted scaninserted_scan
Amended indexamended_index
Duplicate labelduplicate_label
No index tableUNAVAILABLE with a reason; never an empty manifest

The measurement receipt

docs/measurements/story-3442-index-baseline.json contains only redacted index labels/ranges, observed endpoint page-number pairs, and the hand-verified boundary ordinals. The script recomputes the score from those observations; it does not copy a stored aggregate. Pass --pdf to re-read every endpoint from the source paperbook with pdftotext. It has 21 annexure rows—A through S, including M1/M2/M3. “21 parsed” is extraction coverage, not 21 verified correct boundaries.

PYTHONPATH=. python scripts/measure_story_3442_index_baseline.py
PYTHONPATH=. python scripts/measure_story_3442_index_baseline.py --pdf /path/to/paperbook.pdf

parsed_index_rows: 21
printed_to_pdf_offset: 2
index_range_endpoint_localisation:
  ranges_checked: 21
  matching_endpoints: 42/42
  location_accuracy: 1.0
opening_internal_seam_diagnostic:
  precision: 1.0
  recall: 0.5
  f1: 0.667

Proofs

CheckResult
Focused unit contract9 passed
Full unit suite with repo test DB4,713 passed
Red-first proofA planted wrong endpoint changes the computed result to 41/42 and 20/21 ranges
RuffChanged Python files clean and formatted
Focused mypyClean with imports skipped; repo-wide imported debt remains outside this Story
Collision checkNo file changed by open PR #970 is touched