Skip to content

Add a validate subcommand for post-export sanity checks#2

Open
gaurav wants to merge 4 commits into
initial-implementationfrom
add-validate-command
Open

Add a validate subcommand for post-export sanity checks#2
gaurav wants to merge 4 commits into
initial-implementationfrom
add-validate-command

Conversation

@gaurav

@gaurav gaurav commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds pubmed2db validate <dir>, which inspects a finished JSON export (a
directory of NDJSON shards) and writes an archivable, gated
validation_report.json. It answers "does this export make sense?" after an HPC
run. Targets initial-implementation so it can be reviewed after the base branch.

Four checks, split into an offline phase (fast, deterministic) and an
online phase (Entrez eutils cross-checks; skip with --offline):

  1. structure — every line parses as JSON and matches the exporter's exact
    10-field record shape (reuses export._document/month_to_abbrev); flags
    malformed lines, missing/extra fields, nulls, bad ids, invalid months, and
    cross-shard duplicate PMIDs. Per-shard reservoir sampling keeps memory bounded.
  2. coverage — exported count vs. two denominators: the live Entrez total
    (einfo, a loose sanity band) and the local latest_article count (a
    shortfall is an error — rows were dropped); plus drift vs. --previous-report.
  3. field_validation — a seeded sample re-fetched via batched efetch and
    compared field-by-field (fuzzy abstract via difflib). Journal name/abbrev
    are warning-only (different source); a sampled PMID PubMed no longer serves is
    an error.
  4. deletions — samples DB deleted_pmids not reinstated by a later version,
    confirms they're absent from the export and gone from PubMed.

Report & gating

The report leads with errors/warnings arrays that are empty on a clean
run
; the stdout summary is quiet on success and loud on findings. Exit is
non-zero on errors (--fail-on-warn extends to warnings) so it can gate a
pipeline. The DuckDB database and a previous report are optional inputs used
when present and left blank (noted in skipped_checks) when not.

Notes for review

  • All network funnels through one validate._eutils seam (rate-limited,
    retrying), which tests monkeypatch — the suite stays fully offline.
  • Deletion detection sources from the DB only; previous-report-based drop
    diffing is deferred (would need a PMID manifest sidecar) — see FUTURE.md.
  • No new dependencies (requests, lxml already present).

Testing

  • uv run pytest — 51 passing (34 existing + 17 new), no network.
  • Manually verified CLI output on a clean export (PASS, exit 0) and a broken
    export (FAIL with itemized errors, exit 1).

🤖 Generated with Claude Code

gaurav and others added 4 commits July 1, 2026 01:03
Offline structural validation of exported NDJSON shards plus optional
Entrez-backed coverage/field/deletion cross-checks, all funneled through a
single monkeypatchable _eutils seam. Produces a gated report dict whose
errors/warnings lead and whose optional (DB, previous-report, network) sections
stay blank when unavailable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reads a shard directory, uses the DuckDB DB only when it exists and has
articles, writes validation_report.json, prints a quiet-on-success summary, and
exits non-zero on errors (--fail-on-warn also fails on warnings).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers structural pass/fail, dual-denominator coverage, sampled field matches
and mismatches, missing-from-API detection, DB-sourced deletion confirmation,
and the CLI happy/fail paths — no network.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant