Supersedes HANDOFF-2026-07-08.md. This session: Westlaw seg-B batches 2–7, the
unpublished-disposition ruling, finished segment A by promoting the West .doc over the
diverged Lexis/archive text (~232 opinions), recovered P59 gate-rejects, uncovered a
corpus-wide leaked-preamble class (5,516 opinions), and shipped release v1.3.0. One-line
live state: memory project_ongoing_work.md (2026-07-11 section). Read top-to-bottom before resuming.
- Corpus 19,822; invariants 29 ok / 2 known / 0 regressed; quick_check ok; FTS 19,822/19,822.
- RELEASE v1.3.0 SHIPPED (main @
d78fb4a; https://github.com/jet52/ndlaw/releases/tag/v1.3.0). All this session's work + the 2026-07-08 P59 work is committed on main and live in the release (all 6 DB assets incl. the 740 MB opinions.db.zip). Working tree clean. - NOT deployed to the live MCP server yet (
ndconst:/srv/ndcourtsviaupdate-db.sh— separate step, [[project_server_deploy_ops]]). The running server still serves the pre-v1.3.0 DB.
Full deep-dive done; NOT applied — needs a user decision + a pipeline forward-fix. See
TODO-leaked-frontmatter.md + memory project_leaked_frontmatter_preamble + CHANGELOG-data.md.
- 5,516 opinions (68% of modern 1997+) begin
text_contentwith a synthesized header:# <Case Name>\n\nNorth Dakota Supreme Court\n<cites>\nDecided <date>\n\n[## Opinion]. - Confirmed a fabricated ingest header — not in ANY current source file (verified across 1997–2022;
all
ND-markdown-primary, sources start at the cite/caption). Redundant with the case_name/court/ citations/date_filed columns. Even 2022 opinions have it → the live scraper pipeline still adds it (forward-fix needed, generator not yet pinned — it's in the scraper's text_content construction). - Safe to strip, no text loss: dry-run over all 5,516 = 5,494 clean strips + 22 held (all just very
long multi-party captions, also correct); 99.7% keep their
[¶]markers; the 14 that don't are legit unnumbered 1997 orders / 2005 COA. Use the regex-strip (scripts/strip_segA_frontmatter.py, PREAMBLE regex, id list frozentriage/leaked-preamble-all-ids.txt) — NOT re-ingest (would discard the proofing corrections). - Open policy calls before applying: (a) for ~3,900 "straight-to-author" variants the leaked
#heading is the ONLY full-party caption in text_content (stays in case_name/case_name_full cols + sources) — strip it or preserve a caption? (b) also strip the follow-on## Opinionmarker (795) / dup court header (677) / dup cite (124)? (c) FTS surface changes. Raise the strip guard to ~12K for the 22 long-caption cases.
Archive-HTML text → authoritative West bound .doc, via the tested receive_westlaw pipeline.
7/48 done, 41 remain (~4,071 opinions). All: 100/100 promoted, 0 rejects, jaccard mean ~0.87 (big
multi-party captions pull overall jaccard to ~0.45; bodies match — verify the low outliers each batch).
Batches share the date-derived changelog batch westlaw-receive-2026-07-11. Westlaw limits daily
downloads — resume tomorrow. NEXT: batch-8 (triage/westlaw-batches/batch-008.tsv). The loop:
cut -f4 triage/westlaw-batches/batch-0NN.tsv | tail -n +2 | paste -sd';' - | pbcopy→ hand cites.- User runs Westlaw Find&Print → drops the zip in
~/Downloads(newest*.zip). - Cross-check the batch:
cut -f3 batch-0NN.tsvfirst names vsunzip -l ZIP. - Stage:
INC=~/refs/nd/opin/westlaw-incoming/<date>-bNN; mkdir -p "$INC"; unzip -q -o "$ZIP" -d "$INC"; find "$INC" -name '*.zip' -delete(TRAP: a.zipleft in the incoming dir gets auto-extracted → doubles the doc count; confirmsubdirs: 0, only loose.docs). - Dry-run
receive_westlaw --incoming "$INC"; verify any jaccard<~0.60 by opening the.doc(cite+docket+panel). --apply→invariants(want 29/2/0) + quick_check + FTS parity.
- Unpublished dispositions — DECIDED (keep+flag).
opinions.precedential_statuscolumn (NULL=presumed published, not backfilled; SCHEMA Contract 4). id20421 Richmond (Rule 35.1(a)(6),(7)), id20439 Kudrna (35.1(a)(2)) flaggedUnpublished. Policy: a Westlaw reject that's a Rule 35.1 table disposition → keep+flag, don't delete/"correct".scripts/flag_unpublished_dispositions.py. Pre-1997YYYY ND Ncites are SYNTHETIC ([[project_pre1997_neutral_cites_synthetic]]). - Segment A diverged set RESOLVED.
scripts/segA_ws_confirm.py(substring-tile; never SequenceMatcher on long prose — autojunk fabricates false runs) found many diverged had non-.doc text mislabeled westlaw.scripts/promote_segA_westdoc.pypromoted ~232 to West.doc(53 syllabi recovered); post-promotiondoc_db_diff97.4% byte-clean (7336/7532). 8 holds verified: promoted id7180/4863/4829; kept id1031 (bound-PDF-confirmed complete; leaked-# now STRIPPED), id2473+id2125 (merged records), id20479 Larson + id9768 Schirado (different order at shared cite → task below). LESSON: merged-record risk isn't captured by shrink% alone (id2473 lost a companion at 8% shrink) — guard on >1 N.W. parallel / >1 westlaw .doc. Parser note:strip_synopsis_editorialrarely misclassifies a large facts-under-"Synopsis" block (id1031-type); shrink guard catches it; harden before pre-1950 promotes. - P59 gate-reject recovery. Recovered 16 genuine fixes the gate rejected on formatting: OCR justice names (DALE Y.→V. SANDSTROM, EAPSNER→KAPSNER, MAKING→MARING, ROBERT 0.→O. WEFALD), statute em-dash→hyphen, AG-cite 0→O glyphs ×7, marker [1112]→[¶12], rendered-page-verified 65-05-83→33, statute 25-03.1-18.1(1)(a).
- Fleet P60 (
OFFSET 4990, after 2006 ND 210) — modern proofing, PDF method. ~18 fleets to P78. - P59 residual reject queue (
triage/p59-current.results.reject.json): ~49 genuinely-unverifiable (need manual page reading) + correct-rejects + id14702 parallel-cite strip (belongs to the CL-parallel-cite pass). The recoverable ones are DONE. - Larson/Schirado order identity (task): id20479 (460 N.W.2d 716) & id9768 (396 N.W.2d 729) — the West
.docis a different order than the DB; no on-disk N.W.2d bound page to arbitrate. Keep DB; low priority. [[Image here]](636,TODO-image-reconstruction.md); CL parallel-cite enrichment/strip pass; 62-item markup HOLD (triage/proofing-p1p41-HOLD-for-review.json); full-corpus cite_extract re-run under jetcite 2.5.3; statutes #4 minor; reversion-recovery opinion-BODY re-validation.- Deploy v1.3.0 to the live server if/when desired.
- macOS has no
timeout— usegtimeoutor cap iterations. - Never
sqlite3.connect()a backup path that may not exist — it silently creates a 0-byte file (clobbered the 194-promote backup). Checkos.path.existsfirst. receive_westlawauto-extracts any.zipleft in the incoming dir → remove it (only loose.docs).- Run everything via
.venv/bin/python; fullingestre-scan resurrects §6-merged dups (use--incremental). - FTS is trigger-maintained (
opinions_au) — text_content updates auto-sync; no manual rebuild. - Release =
scripts/make_release.sh --publishfrom main (ff-merge the data branch first, bump pyproject version, commit). 740M opinions.db.zip 422-flakes on one-shot upload → create with the 5 small DBs + tag, thengh release upload opinions.db.zipseparately (done for v1.3.0).