Files
pinion-pdf/VERIFICATION.md
T
daveandClaude Fable 5 c9c90920cc v1.1.0: headerless mode for audits printed without row/column headings
Sniffs the format on page 1: printed row numbers -> gridded mode
(unchanged); UUID-keyed rows -> new headerless mode, which anchors rows
on per-record UUIDs and takes the column map from the page-1 header
labels. Handles fused cells (ID+Name+Org+networks), mid-value line
wraps, two-timestamp Time Frame ranges, wrapped header labels, and
per-export schema drift. Validated against three monthly JC PD audits:
120/120 cross-engine spot checks, 0 anomalies, field-identical to the
standalone converter it replaces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bQ28wRBiQYcSR9FzV9U7c
2026-09-12 18:29:13 -04:00

99 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# How the reference conversion was validated
pinion was developed against a real file: a 131,072-page, 360 MB PDF of a
law-enforcement network-audit spreadsheet (1,048,576 rows × 14 columns,
printed from Excel via PDF-XChange on Windows). Every design decision below
was forced by something that actually went wrong on that file.
## Independent checks the output passed
1. **Row-number chain.** Excel prints its own row numbers in the left
margin. The reconstruction reproduces 1..1,048,576 with no gaps and no
duplicates. Any method that drops or invents rows fails this
immediately — a competing hand count of the same file reported
1,081,343 rows, which is exactly one phantom row per page (Trap 3) and
also exceeds Excel's hard row limit, so it can be rejected on
arithmetic alone.
2. **Cross-engine spot check.** 40 randomly sampled rows were re-extracted
with pdftotext (Poppler — a completely independent PDF engine) and
matched on ID, agency, and printed row number: 40/40.
3. **Implementation-independent reproduction.** Four separately written
implementations (a hardcoded-geometry minimal script, a heavily
commented teaching version, a parallel self-verifying version, and this
package) produce **byte-identical** CSVs across all 1,048,575 data
rows, verified with `cmp`.
4. **Internal consistency.** Every value in the ID column is a
well-formed UUID (1,048,575 of 1,048,575); every row has exactly the
same field count; zero text runs failed to map to a row or column.
## Bugs the checks caught during development
These are the reasons the self-checks exist. Each was caught only by
diffing against known-good output or by a check line going red:
- **Single-character redactions are ~3.4pt wide** — narrower than some
grid marks. A size-based bar filter dropped 5 of them. Fix: filter by
shape (taller than half a row, thicker than a hairline), not by area.
- **Some blackout bars are filled four-sided paths, not rectangles.**
Visually identical, different PDF instructions. Inspecting individual
line segments missed 4 bars, including one 121pt wide. Fix: measure
whole filled regions, never segments.
- **Two redaction bars cover the printed row number itself**, making the
gutter unreadable on those pages. Anchoring rows solely on printed
numbers silently dropped both rows. Fix: anchor on geometry, use the
printed numbers as a cross-check (`printed numbers disagreed` must be 0).
- **A one-character join bug** (`" ".join` vs `"".join`) corrupted the 191
cells whose text the PDF drew in multiple pieces. Caught by byte-diff.
## Known artifacts in the source data (not conversion errors)
- 19 rows contain an unmapped `ff` ligature (the subset font has no
Unicode mapping for it): `Sex O�enses`, `Tra�ic`. Exact-match filters on
those strings will miss these rows.
- 141 rows contain double-encoded UTF-8 from the source system
(`100’s`, `–`). Present in the PDF itself.
- The sheet ends at exactly 1,048,576 rows — Excel's ceiling — with the
final row fully populated. Comparison of per-day record counts against
the file's claimed date range shows several end-of-range days thinned or
absent: the export was truncated at the ceiling, and roughly 17% of the
original records are estimated to be missing. All totals from the file
are floors.
# Headerless mode (v1.1.0)
Validated against three monthly Johnson City TN PD audit PDFs printed via
"Microsoft Print to PDF" without headings (48/74/69 pages; 2,032 / 2,437 /
2,607 rows).
Checks passed on all three:
1. **Cross-engine spot check.** 40 random rows per file re-verified
against pdftotext: 120/120 after making the probes wrap-tolerant (a
cell that wraps mid-value in the PDF never appears as one contiguous
string in any text extractor's output — probe fragments, not lines).
2. **Range sanity.** Every two-timestamp Time Frame parses and runs
start <= end (7,074 ranges, 0 disordered).
3. **Anomaly counters at zero**: no bad UUIDs, no unplaced runs, no
strays in the Name/Org windows.
Bugs the checks caught during development:
- **Wrapped header labels** ('Total Networks ' / 'Searched' on stacked
runs) made a whole column invisible to name-matching; its values landed
in the Org Name window and were silently overwritten. Fixed by merging
stacked header runs; guarded by the strays counter.
- **A false-alarm verification failure**: 43 spot-checks "failed" because
the probe assumed cells appear contiguously in pdftotext output. Glyph
level inspection proved the CSV was right and the check was naive.
Kept here as a reminder: verify the verifier before trusting a red
result.
Schema drift is real: of the three sibling exports, one has all 13
columns, two lack License Plate entirely, and Total Networks Searched
moves between its own column and being fused into the ID run. Headerless
mode takes each file's schema from its own header and reports what's
absent.