Session focus: shipped two deterministic proofing passes, set the pre-1997 proofing
strategy (the ~104-fleet question), ran + resolved the P59 A/B, applied P59, and
started the segment-B Westlaw conversion. Read this top-to-bottom before resuming; the
one-line state lives in memory project_ongoing_work.md (2026-07-08 section).
- Corpus 19,807; invariants 29 ok / 2 known / 0 regressed; quick_check ok; FTS 19,807/19,807; 162 tests; footnote 176/0.
- EVERYTHING today is DB-APPLIED but UNCOMMITTED (git) and UNRELEASED. Last release
= v1.2.2 (P53–P58). Owe: git commit + release build (regenerates
opinions.db.zip) to make today's work live. Working tree is dirty (new scripts + docs + DB). - Backups made today:
opinions.db.bak-{ellipsis,roman-headings,p59}-2026-07-08. - Fleet cursor: P59 DONE → next P60 (
OFFSET 4990, after 2006 ND 210).
Built to stop the fleet re-discovering the same mechanical classes every 120-opinion window. Both dry-run by default, back up the DB, log one full-text changelog row per opinion (revertible).
scripts/normalize_ellipsis.py→ batchnormalize-ellipsis-modern-2026-07-08: 1,189 opinions / 3,991 spaced ellipses → compact (. . . .→....). MODERN (≥1997) only, 3/4-dot runs only (pre-1953 uses spaced omission-lines; >4-dot excluded). Safe by construction (only interior whitespace removed). Residual modern 3/4-dot runs after apply: 0.scripts/fix_roman_headings.py→ batchfix-roman-headings-2026-07-08: 1,037 opinions / 1,072 OCR-garbled headings (Ill→III1008,rv/TV/IY→IV,HI→III,YI→VI). Triple guard: blank-flanked heading slot + DB's own roman sequence pins the value + PDF has the target as a bare-line numeral. All 64 non-Illverified vs PDF, 0 false positives. 16 no-PDF cases skipped for manual.- DEFERRED (not built):
[[Image here]]— 639 opinions, heterogeneous (dropped list-items needing PDF text restoration vs genuine figures). SeeTODO-image-reconstruction.md. NOT a blind pass. User priority: recover lost text, few real images.
Investigation (background agent, evidence in memory + below). Verifying DB against the file it was ingested from is circular — it only catches ingestion defects, never source errors. Segmented by primary source:
| Seg | Source | N | % | Plan |
|---|---|---|---|---|
| A | West .doc (RTF, keyed from published reporter) |
7,234 | 57% | Deterministic .doc diff, NOT LLM fleets |
| B | archive.ndcourts.gov HTML | 4,769 | 38% | Convert via Westlaw, don't proof the HTML |
| C | N.W. OCR markdown | 584 | 5% | LLM+human + 2 deterministic pre-passes |
scripts/doc_db_diff.pyswept all 7,234: 96.7% byte-clean (6,997/7,234) vs the.doc.- The 237 "divergences" are NOT body defects — they're front-matter/metadata contamination:
leaked
# Case Namemarkdown headings,Decided/WL/reporter-cite preambles prepended totext_content, and West editorial blocks (Attorneys and Law Firms/Synopsis). The opinion bodies match the.doc. - CAVEAT / unfinished:
textutildrops spaces in some RTF runs (e.g.Kuklokv.NorthDakotaWorkers'), which made the word-level diff produce false body divergences. A whitespace-insensitive re-check was started but the char-levelfind_longest_matchtimed out (macOS has notimeout; usegtimeoutor cap iterations). TODO: finish the ws-insensitive body-clean confirmation to nail the true body-defect count (expected: near-zero), then build a deterministic front-matter-strip pass for the ~237 (strip leaked#headings + metadata preamble + West editorial blocks fromtext_content). Queue:triage/segA-divergences.tsv(+segA-diff-summary.txt). Pilot:triage/segA-pilot-ids.txt(24 op, 100% clean by both LLM fleet and the diff — validates the approach). scripts/verify_proofing_proposals_doc.py— the.docgate (thin clone of the PDF gate,textutilin place ofpdftotext). Ready if/when segment A needs proposal gating.
the 66%-of-C bare-*NNN star-page residue. Small; lowest priority.
Ran P59 twice on the same 120 opinions: current-method (reads PDF) vs --db-only (no source,
proposes from DB text; same deterministic PDF gate confirms both).
| Current | DB-only | |
|---|---|---|
| confirmed fixes | 676 | 505 |
| tokens | 7.47M | 5.80M (−22%) |
| recall vs current | — | 65.2% (441/676) |
−22% tokens for −35% recall = bad trade for authoritative text. Miss profile: DB-only missed
missing_text (11/12), whitespace (39/51), ocr_char (83/233) — all need the source; but MATCHED
split_join 218/251 (dehyphenation is inferable). Keep the PDF-reading method for modern. The
segment-A plan is unaffected (it's deterministic-diff, cheaper than DB-only and no recall loss).
gen_proofing_workflow.py gained --db-only (experiment flag; leave unused). Artifacts:
triage/p59-{current,dbonly}.results*.json.
Goal: replace non-authoritative archive HTML with West bound text, moving segment B into the
segment-A .doc regime AND upgrading the DB text to authoritative reporter text.
- Worklist:
triage/westlaw-batches/batch-001.tsv … batch-048.tsv(100 each, newest-first, all have an N.W.2d pull cite). - Batch 1 DONE (
westlaw-receive-2026-07-08): 98/98 promoted (0 ambiguous/errors, jaccard 0.85–0.91), now.doc-primary. 47 batches (~4,671 opinions) remain. - The loop: copy a batch's cites to clipboard (
cut -f4 triage/westlaw-batches/batch-0NN.tsv | tail -n +2 | paste -sd';' - | pbcopy) → user runs Westlaw Find&Print → drops zip in~/Downloads→ stage:mkdir -p ~/refs/nd/opin/westlaw-incoming/<date>+ copy + unzip, then rm the .zip from the incoming dir (the tool auto-extracts any .zip into a subdir → doubles the doc count; remove both the zip AND anyWestlaw Precision …/subdir so only loose.docs remain) →python -m ndcourts_mcp.receive_westlaw --incoming <dir>(dry-run) → review report (triage/westlaw-receive-<date>.tsv, want 98–100 promoted / 0 ambiguous) →--apply→ invariants. - Westlaw rejects = free citation QA on segment B. Track in
triage/segB-westlaw-rejects.tsv. Batch-1 reject rate: 2/100. Two kinds: (a) genuine wrong-page DB cite error → fix cite, re-pull; (b) unpublished disposition (§5) → keep+flag, no West text exists.
Batch-1's 2 rejects (id20421 Richmond v. Allstate Civil 960194; id20439 Kudrna v. Kudrna Civil 960091) are pre-1997 N.D.R.App.P. 35.1(a)(2) summary affirmances — user confirmed the N.W.2d pages are correct but the opinions were removed from the bound volume as not-designated- for-publication (advance-sheet list only). Full text (from archive.ndcourts.gov) IS present and complete (caption + disposition + rule + full panel). Class ≈ 11–36 (pre-1997 short archive per curiams).
- RECOMMENDATION (mine, pending user's ruling): KEEP + flag unpublished, do NOT delete. They're
real, complete, court-posted dispositions; the same type is published with a neutral cite post-1997;
CourtListener carries them; deletion is irreversible. Add a
precedential_statuscolumn (none exists — schema hascase_type/notes/disposition) and annotate the cite as table/NDP. - Corpus policy this sets: a Westlaw reject that is an unpublished summary disposition → keep+flag, don't pull West text, don't "correct" the cite. Only wrong-page rejects get corrected. Decide before mass-converting the remaining 47 batches.
- User ruling on §5 (unpublished dispositions) — gates the segment-B reject policy.
- git commit + release today's work (P59 + 2 passes + Westlaw batch 1) — it's all unreleased.
Prior release gotcha:
gh release createflaked HTTP 422 on the 740M zip; create with the 5 small DBs + tag first, thengh release upload opinions.db.zipseparately (memoryproject_server_deploy_ops). - Continue segment-B pulls (batches 2–48) — user pulls, I process per §4.
- Finish segment-A — ws-insensitive body-clean confirm + front-matter-strip pass (§2A).
- Fleet P60 (
OFFSET 4990) when returning to modern proofing — current PDF method. - Work the P59 flag queue (194 flags + 77 rejects,
triage/p59-current.results.*.json) — the high-value items (statute pinpoints,Sup.Ct.→Sup. Ct.spacing,¶6→¶ 6) PDF-verified.
[[Image here]] reconstruction (639, TODO-image-reconstruction.md); CL parallel-cite enrichment
pass; 62-item markup HOLD (triage/proofing-p1p41-HOLD-for-review.json); flag-review backlog
(malformed *Case**,* emphasis, case-name divergences); statutes #4 minor; full-corpus cite_extract
re-run under jetcite 2.5.3. Detail in memory project_ongoing_work.md §"Open queues".
- macOS has no
timeout— usegtimeoutor cap loop iterations (bit me on the seg-A ws diff). receive_westlawauto-extracts any.zipin the incoming dir into a subdir → doubles the doc count; remove the zip + any extracted subdir so only loose.docs remain before--apply.- Run everything via
.venv/bin/python. Fullingestre-scan resurrects §6-merged dups — use--incremental. apply_proofing_proposals.pylogsfield=text_content.<class>(NOT revertible viacleanup revert, which doesSET <field>=old_value); the deterministic passes +receive_westlawlogfield=text_contentfull old/new (ARE revertible). Backups (.bak-*) are the real safety net.- Preserve court typos present in BOTH sources ([[feedback_preserve_source_typos]]).