Skip to content

fix(summary): keep the summary when one cross-section reading fails - #409

Open
geiresx wants to merge 7 commits into
GeiserX:mainfrom
geiresx:pgp/rust-r2d
Open

geiresx wants to merge 7 commits into
GeiserX:mainfrom
geiresx:pgp/rust-r2d

Conversation

@geiresx

@geiresx geiresx commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

When one reading that joins two sections of summary.json fails, the Python collector loses the whole file. These readings are the repeat readings, Locityper's primary calls, CNVpytor's chrY filter, the CYP2D6 block and what was not assessed. They run after the per-section error handling. So one bad table there leaves no summary.json, and both reports fail with it. A reader that stops on an error the Python does not catch has the same effect. A telomere table with an infinite read length does this today: the Python stops with an OverflowError and writes nothing.

The fix

og summary collect without --only now writes the whole summary.json. It reads all 48 sections over one walk of the sample folder. Groups a, b and c are read at the same time. Then it runs the post pass:

  • the readings that join sections: the repeat readings with the traits and CPIC states, Locityper's primary calls with HLA, pypgx and Cyrius, and CNVpytor's chrY filter with the declared sex from step 33;
  • the CYP2D6 block;
  • not_run, not_assessed, recheck and new_since.

Each part of the post pass that changes a section runs on its own. When it fails, with an error or a panic, only that section becomes unreadable. Its note names the file. The other sections stay, and the summary is written. A previous run whose summary cannot be read leaves new_since out, as before. The comparison itself returns typed results, so a bug in it is no longer read as "no previous run".

The post pass is its own module, rust/crates/og-summary/src/collect/post.rs, so the next lane (the switch of the reports to og) builds on it without a merge. Groups a and b now read through a shared Folder (one walk, the run status and the manifest), so the whole collect walks the folder once.

Nothing calls the whole collect yet. The reports still read the summary that bin/collect_summary.py writes. No output file, key or column changes.

Decisions:

  • Without --only, --out is optional and defaults to summary.json in the sample folder, as the Python did. --only still needs --out, so a part of the sections never replaces the complete file.
  • In today's Python, no input we found makes the post pass itself crash: every key it reads is set by the section readers. The Rust post pass reads typed data and JSON values with defaults, so it has no such keys to miss. The containment is for errors and panics. The golden that shows a kept summary is the telomere reader's uncaught OverflowError, which loses the whole file in the Python.
  • The unreadable telomere note reads OverflowError: cannot round, the existing group c reader's note. It is unchanged here.

What differs from the Python on purpose (also in CHANGELOG): a post-pass part that fails makes only its section unreadable; a reader error the Python did not catch keeps the summary. The all-inf golden pins the second.

How it was checked

  • Red control: commit 888cd36 planted four faults: not_run kept the sections that are no step, a stale VNtyper reading counted as assessed, new_since took the current run's date, and a panic in a post-pass part was no longer caught. On the merged control head d3ff258, Guard's rust job failed in og-summary only, 97 passed and 5 failed, each on a planted fault. Guard's parity job reported 7 of 4,210 items different, all in the new whole-file cases and all from the faults ($.not_run lengths and $.new_since.previous_utc). Commit af005e2 removes the faults. The other runs on that head were cancelled.
  • Whole-file parity: 9 new summary-collect cases run og and bin/collect_summary.py at the oracle commit on the same folders, compared as JSON with key order and only generated_utc masked. The new generator tests/parity/gen/summary-collect-full.sh joins the three groups' fixtures into one folder, so the post pass reads across groups. It adds failed steps, an empty folder, kept runs in history/ (one half-copied, one that is no JSON), a database re-check, and the default output path. The oracle gets the real run_delta.py at the oracle commit. 8 cases agree; all-inf holds the golden. Locally, ORACLE_RUNNER=local scripts/ci/oracle-diff.sh summary-collect roh-summary: all 295 items in 74 cases agree, including the 62 older subtree cases after the refactor. oracle-diff.sh --self-test: all 15 checks passed.
  • Unit tests: cargo test -p og-summary --no-fail-fast, 102 passed. 13 are new, in collect/post/tests.rs: section order, not_run and its failed steps, each not_assessed line, containment of an error and a panic, the telomere case, the declared sex across groups, new_since with a half-copied folder and three unreadable previous runs, the re-check block, and the command line. rustfmt, clippy with -D warnings and cargo doc with -D warnings pass.
  • scripts/ci/check-images.sh, scripts/ci/check-personal-data.sh and shellcheck on the changed scripts pass.
  • Speed and memory, release build, /usr/bin/time -l, three runs each, same box. On the joined folder with every section: Python 0.15 to 0.21 s and 42 to 44 MB peak RSS; og 0.01 to 0.03 s and 15 to 16 MB. The same folder with a synthetic 1,000,000-record VCF: Python 1.53 to 1.54 s and 42 to 44 MB; og 0.44 to 0.62 s and 17 to 20 MB. Both outputs are the same JSON, with only generated_utc masked.
  • CI on the final head: all upstream fast checks passed on 7b0a8e7: Guard (ci-ok; its parity job ran every case of tests/parity/cases.tsv), Rust, Lint, Nextflow, Docs, Container Test and Personal data.
  • E2E: this PR adds no e2e case and no caller of the new command, so no e2e case runs its code. E2E stays with the landing train.
  • CodeRabbit: the PR's first push hit the review limit, and another PR's review request was within the hour, so no review was requested here.

Files outside the package list, each needed for the parity proof or for docs that stay true: rust/Cargo.lock (og-summary depends on og-runlog for new_since and recheck), rust/og/src/main.rs (one usage line), rust/README.md and docs/rust.md (one line each), tests/parity/cases.tsv (9 rows), tests/parity/gen/summary-collect-full.sh (new), tests/parity/golden/summary-collect/all-inf/ (new) and tests/parity/inputs/summary-collect/oracle.sh (one comment). The branch merges main once, for a CHANGELOG conflict.

Bead: pgp-bs9u.44. The T3 real-genome check is run by the orchestrator before landing.

…lect

Without --only, og summary collect reads all three groups over one walk
of the folder and runs the post pass: the readings that join sections,
the cyp2d6 block, not_run, not_assessed, the database re-check and
new_since. A part of the post pass that fails makes only its own section
unreadable; the summary is still written.

Bead: pgp-bs9u.44
Nine cases run og summary collect without --only against
bin/collect_summary.py on folders that join the three groups' fixtures,
with failed steps, an empty folder, kept runs in history/ and a database
re-check. A telomere table with an infinite read length stopped the
Python with an uncaught OverflowError; og keeps the summary with that
section unreadable (the all-inf golden).

Bead: pgp-bs9u.44
not_run keeps the sections that are no step, a stale VNtyper reading
counts as assessed, new_since takes the current run's date, and a panic
in a post-pass part is no longer contained. The next commit removes them.

Bead: pgp-bs9u.44
@coderabbitai

coderabbitai Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 3 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 039f11f and 7b0a8e7.

Files ignored due to path filters: 3

  • rust/Cargo.lock is excluded by !**/*.lock
  • tests/parity/cases.tsv is excluded by !**/*.tsv
  • tests/parity/gen/summary-collect-full.sh is excluded by !**/gen/**
Files selected for processing: 18

  • CHANGELOG.md
  • docs/rust.md
  • rust/README.md
  • rust/crates/og-summary/Cargo.toml
  • rust/crates/og-summary/src/collect.rs
  • rust/crates/og-summary/src/collect/group_b.rs
  • rust/crates/og-summary/src/collect/group_c.rs
  • rust/crates/og-summary/src/collect/group_c/repeats.rs
  • rust/crates/og-summary/src/collect/model.rs
  • rust/crates/og-summary/src/collect/post.rs
  • rust/crates/og-summary/src/collect/post/tests.rs
  • rust/crates/og-summary/src/collect/tests.rs
  • rust/og/src/main.rs
  • tests/parity/golden/summary-collect/all-inf/all-inf.json
  • tests/parity/golden/summary-collect/all-inf/exit
  • tests/parity/golden/summary-collect/all-inf/stderr
  • tests/parity/golden/summary-collect/all-inf/stdout
  • tests/parity/inputs/summary-collect/oracle.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Reverts 888cd36, the red control of this PR.

Bead: pgp-bs9u.44
The images check reads any image name in a tracked file as one that
bypasses versions.env; the fixture's DEEPVARIANT_IMAGE rows now hold
plain version text.

Bead: pgp-bs9u.44

This branch has not been deployed

No deployments
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.

2 participants