feat(sources): Wayback pass, automatic trimming, detention rule, ten cases re-sourced
Second and third rounds on the dossier pipeline, reviewed locally before commit. Retrieval - wayback_pass.py: retries every BLOCKED/DEAD/THIN/ERROR linked source through the Wayback Machine; blocks carry the snapshot date, archive URL and the live verdict. 143 slots retried: 93 fetched, 37 no snapshot, 12 script shells, 1 PDF. - fetch_sources.py: fetch_wayback() helper. - add_extra_sources.py: files staged extra_* finds (web search, not on the DB record) into the store with a provenance note. - Cases re-sourced by web search: 248 (Oslo: Document.no, Avisa Oslo, NRK; the linked Le Parisien piece is case 242 and is marked OFF-CASE), 258 Kharkiv, 260 Singapore, 358 Bangkok, 365 Las Vegas, 388 Verneuil-sur-Seine, 390 Zoersel, 430 Homestead. 341 of 364 cases now hold a fetched article; 11, 66 and 399 have no public text source (podcast, police video, direct victim report). Trimming - auto_trim.py: case-anchored furniture cut for UNTRIMMED blocks. Finds the body run that mentions the case, merges across subheadings and short furniture gaps, drops teasers, share bars, date/URL/caption lines and subscription pitches; refuses pages with no record term or almost no body. 302 blocks trimmed; 16 left on auto-trim-review.md (7 OFF-CASE suspects). Every cut is labelled AUTO-TRIMMED, UNREVIEWED in the dossier; the full extract stays in staging/. Decisions with anchors in trim-decisions-auto.json. - build_cases.py / verify_all.py: banners for auto-trimmed and archived blocks, staged-file check extended to Wayback blocks, flag legend under the record. Detention rule - README "Definitions": the DB field `kidnappings` is the DETENTION violence type (victim, guard, staff or relative held to force submission or execute the theft), distinct from the Kidnapping scenario (taken away and held). - detention-flag-review.md / detention-flag-corrections.json: 40 records reviewed with evidence; 30 set-to-1 proposals accepted by the owner on 2026-09-06 (listed in corrections-approved.md), 10 still open. - apply_detention_wording.py: "Violence Used" in the 70 reviewed summaries now names detention explicitly (62 of 70 labelled), supported by the summary's own text; idempotent; supersedes the batch scripts' wording. Worklist and docs - sofia-worklist.md: 248 decisions, detention rule item replacing the old "no abduction" item, fresh-search section for the textless cases. - README-START-HERE.md: progress notes, run commands, next steps. - Bug fixed in passing: Wayback blocks stored in-memory text with carriage returns; now stored as read back from disk. verify_all.py PASSES (2321 checks). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZZENdTLzNGsbNy4DyF1yt
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
499fd61f79
commit
439e83a4cb
@@ -296,7 +296,14 @@ def render_fetched(entries):
|
||||
for i, e in enumerate(keeps, 1):
|
||||
flag = {"PARTIAL": " [PARTIAL — PAYWALLED]",
|
||||
"UNTRIMMED": " [RAW EXTRACT — NOT YET TRIMMED]"}.get(e["verdict"], "")
|
||||
auto = e["verdict"] == "KEEP" and str(e.get("trimmed_by", "")).startswith("auto")
|
||||
if auto:
|
||||
flag = " [AUTO-TRIMMED — UNREVIEWED]"
|
||||
L += ["", "-" * 96, f"FETCHED ARTICLE {i}{flag}"]
|
||||
if auto:
|
||||
L += [" !! Furniture was cut by a script keyed on this case's record, not by a person.",
|
||||
" !! It may still carry passages about OTHER cases from a round-up article.",
|
||||
" !! The full page extract is kept in staging/ if a cut needs revisiting."]
|
||||
if e["verdict"] == "UNTRIMMED":
|
||||
L += [" !! This is the whole page as extracted. It still contains site furniture,",
|
||||
" !! and may contain teaser headlines for UNRELATED cases. Trim on read-through.",
|
||||
@@ -313,8 +320,14 @@ def render_fetched(entries):
|
||||
L.append(f" Trimmed {e['cut_note']}")
|
||||
if e.get("note"):
|
||||
L.append(f" Note {e['note']}")
|
||||
L += [" Caveat retrieved from a live page on the date above. Unlike text copied",
|
||||
" from the spreadsheet, there is no second copy to hash it against.",
|
||||
if e.get("archive_url"):
|
||||
L += [f" Archive {e['archive_url']}"]
|
||||
L += [" Caveat retrieved from a Wayback Machine snapshot, not the live page. The",
|
||||
" snapshot date is in the method line; the live URL was unreadable."]
|
||||
else:
|
||||
L += [" Caveat retrieved from a live page on the date above. Unlike text copied",
|
||||
" from the spreadsheet, there is no second copy to hash it against."]
|
||||
L += [
|
||||
"-" * 96, "",
|
||||
f"ORIGINAL ({e['language']}, verbatim as retrieved)", "", e["text"]]
|
||||
if e.get("translation"):
|
||||
@@ -331,6 +344,9 @@ def render(rec, arts, notes, fetched=()):
|
||||
L += ["=" * 96, hdr, "=" * 96, "", "DATABASE RECORD"]
|
||||
for f in FIELD_ORDER:
|
||||
L.append(f" {f:<20}{rec.get(f) if rec.get(f) is not None else '(null)'}")
|
||||
L += [" (flag legend: 'kidnappings' is the DETENTION flag — 1 when the victim was held against",
|
||||
" their will at any point, tied, locked in, held at gunpoint or taken away; it is NOT the",
|
||||
" same as the 'Kidnapping' scenario, which means taken away and held. See README, Definitions.)"]
|
||||
|
||||
ov = SUMMARY_OVERRIDES.get(str(rec["id"]))
|
||||
if ov:
|
||||
|
||||
Reference in New Issue
Block a user