Skip to content

Cover hydrate_for_rescore with a real test, replacing one that could not fail - #56

Merged
maccoss merged 2 commits into
mainfrom
fix/hydrate-rescore-coverage
Jul 14, 2026
Merged

Cover hydrate_for_rescore with a real test, replacing one that could not fail#56
maccoss merged 2 commits into
mainfrom
fix/hydrate-rescore-coverage

Conversation

@maccoss

@maccoss maccoss commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

The HPC per-file rescore worker had no real test coverage. The one test that named it,
hydrate_round_trips_a_synthetic_pair, was a placeholder: a #[test] containing a TODO
and the comment "Intentionally no assertions yet". It could not fail.

That is not a hypothetical gap. It is exactly why the format_version != 2 gate in
hydrate_for_rescore (fixed in #55) survived in main for nine days with CI green:
once the reconciliation envelope went to v3, that gate rejected every envelope the
pipeline could write, and the worker was dead on arrival. A test that cannot fail is
worse than no test, because it reads as coverage.

  • Replaced the placeholder with hydrate_for_rescore_accepts_a_current_format_envelope,
    which builds the real on-disk trio a worker is handed -- .scores.parquet, the
    first-pass FDR sidecar, and a current-format reconciliation.json -- then calls
    hydrate_for_rescore and asserts the reconstructed state: stubs from the parquet, the
    sidecar overlay actually landing (score and q-values that exist ONLY in the sidecar),
    the planner action joined from entry_id onto the stub's vec index, and the join-wide
    stem list the worker hashes for the merge node.
  • It pins RECONCILIATION_FORMAT_VERSION rather than a literal, so a future format bump
    cannot silently re-introduce the same class of gate.
  • The test lives in pipeline.rs because the parquet and sidecar writers are private to
    that module; rescore.rs keeps a pointer to it.

Test plan

  • cargo test --all-features - green
  • cargo clippy --all-targets --all-features -- -D warnings - clean
  • cargo fmt --check - clean
  • Negative control: reintroduced the old != 2 gate locally and confirmed the
    new test FAILS with "has format_version 3 (expected 2)", then passes with it
    removed. The test catches the regression rather than merely passing alongside it.

…not fail

The only test guarding the HPC per-file rescore worker was a placeholder: a #[test]
holding a TODO and the comment "Intentionally no assertions yet". It could not fail.
That is why a `format_version != 2` gate in hydrate_for_rescore -- which, once the
reconciliation envelope went to v3, rejected every envelope the pipeline could write
and left the worker dead on arrival -- sat in main for nine days with CI green.

Replace it with a test that builds the real on-disk trio a worker is handed (scores
parquet, first-pass FDR sidecar, current-format reconciliation.json), calls
hydrate_for_rescore, and asserts the reconstructed state: stubs from the parquet, the
sidecar overlay actually applied (score / q-value values that exist only in the
sidecar), the planner action joined from entry_id onto the stub's vec index, and the
join-wide stem list. It pins RECONCILIATION_FORMAT_VERSION rather than a literal, so a
future format bump cannot silently re-introduce the same class of gate.

Verified the test fails against the old gate (reintroduced it locally: "has
format_version 3 (expected 2)") and passes without it, so it genuinely catches the
regression rather than merely passing.

The test lives in pipeline.rs because the parquet and sidecar writers are private to
that module; rescore.rs keeps a pointer to it.
Copilot AI review requested due to automatic review settings July 13, 2026 21:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves confidence in the Stage 6/HPC rescore worker by replacing a non-asserting placeholder “hydration” test with a real on-disk round-trip regression test that fails when hydrate_for_rescore rejects the current reconciliation envelope format.

Changes:

  • Removed the placeholder hydration test in rescore.rs and replaced it with a pointer to the real coverage.
  • Added hydrate_for_rescore_accepts_a_current_format_envelope in pipeline.rs, constructing a realistic parquet + sidecar + reconciliation.json trio and asserting hydrated state.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
crates/osprey/src/rescore.rs Removes the placeholder test module and documents where hydration is now covered.
crates/osprey/src/pipeline.rs Adds an end-to-end regression test for hydrate_for_rescore against the current reconciliation format version.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/osprey/src/pipeline.rs
The test's comment claimed the overlay carries score / q-values / PEP, but the
assertions only checked score and run_precursor_qvalue on a single entry -- 2 of the
7 f64s the sidecar actually carries, on 1 of the 2 entries. A partial check misses
drift in whatever it skips, which is exactly the failure mode this test exists to
prevent.

Now asserts all seven overlaid fields (score, both run q-values, both experiment
q-values, PEP, run protein q-value) on BOTH entries.

Also writes the sidecar records in REVERSE order relative to the parquet stubs.
load_fdr_scores_sidecar matches by entry_id, not by position; with the reversal and
per-entry distinct values, a regression to positional overlay makes entry 7 wear
entry 9's numbers and the assertions catch it. That invariant is called out in the
loader's own comments and previously had no test.
@maccoss
maccoss merged commit 72fe3c6 into main Jul 14, 2026
3 checks passed
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