Skip to content

Correct pileup motif ownership, validation, and mask limits - #702

Open
SuhasSrinivasan wants to merge 7 commits into
nanoporetech:masterfrom
SuhasSrinivasan:codex/fix-pileup-motif-ownership
Open

Correct pileup motif ownership, validation, and mask limits#702
SuhasSrinivasan wants to merge 7 commits into
nanoporetech:masterfrom
SuhasSrinivasan:codex/fix-pileup-motif-ownership

Conversation

@SuhasSrinivasan

@SuhasSrinivasan SuhasSrinivasan commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review status: Author-reviewed and ready for ONT review.

Addresses #678 and the pileup motif-mask portion of #676.

Summary

This PR consolidates the related pileup motif correctness fixes in one review unit:

  • Motif discovery and ownership are invariant to interval chunking and FASTA preload mode.
  • Combine-strand motif geometry is validated before output creation or truncation.
  • Dense/repeated combined-motif scans have a bounded progression while retaining every valid overlapping pair.
  • Requests exceeding the eight motifs representable by the existing u8 membership mask fail clearly before output mutation.
  • Automatically added single-base motifs are deduplicated by primary base, so multiple requested modification codes for one base do not duplicate rows or falsely consume mask capacity.

Severity

High — scientific completeness, reproducibility, and output integrity.

Accepted commands could omit motif sites or change results solely with chunking/preload mode. More than eight matching motifs could be accepted while the ninth was silently unrepresentable. Invalid combine-strand geometry could be detected only after replacing an existing output file.

Root causes and fixes

  • Motif-hit ownership was tied to query chunks rather than a stable half-open genomic anchor interval. The lookup now fetches enough context for boundary-spanning motifs while assigning each anchor to exactly one owned interval.
  • Combined-strand partner discovery repeatedly expanded local closures. It now uses bounded geometric/disjoint scans and retains overlapping anchors exactly once.
  • Combine-strand geometry validation ran too late. Missing, multiple, non-palindromic, and negative-displacement configurations are rejected during preset determination, before the writer is constructed.
  • Motif membership uses a u8, but parsed and automatically added motif counts were not checked. Capacity is now validated both before and after automatic motif insertion.
  • The automatic-insertion loop previously used a fixed snapshot of represented primary bases. It now updates that set as it inserts, ensuring one added motif per missing primary base.

Reproduction and expected behavior

Case Before After
Motif spans a chunk/preload boundary Site can be omitted or duplicated depending on execution mode Anchor is emitted once in every tested mode
Invalid combine-strand geometry Error can occur after output truncation Error occurs before creating or changing output
Dense CGCG repeat Repeated closure expansion All 255 pairs / 510 anchors retained with bounded scan work
Nine matching motifs Command succeeds but the matching ninth CGCG label is absent Clear 8-versus-9 error before output mutation
Exactly eight matching motifs Supported Supported, with all eight distinct labels asserted
Seven C motifs plus two requested A modification codes Adds A 0 twice, duplicating affected output and consuming nine mask slots Adds one A 0 motif and remains within the eight-motif limit

The ninth-motif case was reproduced with installed modkit 0.6.4 on the included oligo fixture: it exited successfully, produced 11 rows, and reported only the first eight motif labels even though the ninth CGCG 0 motif occurs in the requested reference interval.

Testing

Tested exact head: ea331688225f262ca26bc6b844194b24da27885f (tree 19e43d07ee95e116e601274794370ff142e0f8f2).

  • cargo test --offline --locked -p mod_kit motif_capacity_accepts_eight_and_rejects_nine --lib -- --nocapture: 1 passed, 0 failed.
  • cargo test --offline --locked -p modkit --test test_pileup -- --nocapture: 23 passed, 10 ignored, 0 failed.
  • cargo test --offline --locked --workspace --all-targets --no-fail-fast -- --test-threads=1: 195 passed, 14 ignored, 0 failed.
  • The fixture matrix covers chunk sizes and preload modes, both strands, overlapping motifs, negative/zero/positive displacement, validation ordering, absent-output and sentinel preservation, dense-repeat count/work bounds, all eight supported labels, explicit/automatic ninth-motif rejection, and same-primary-base automatic-motif deduplication.
  • Installed modkit 0.6.4 parent-red check: the matching ninth motif is silently absent. Final-head regressions reject the request before creating/truncating output.
  • Automatic-motif parent/fixed comparison: installed 0.6.4 logs adding single-base motif: 'A 0' twice and produces 38 rows for seven C motifs plus m6A/2OmeA; the final head adds A 0 once and produces 23 rows with the expected eight distinct motif labels.
  • Changed-file rustfmt --check and git diff --check upstream/master...HEAD: passed. Whole-workspace formatting still reports unrelated pre-existing formatting differences in untouched files.
  • Independent Rust, bioinformatics, and supervisor reviews were performed on the exact final head before this PR was marked ready.

The large direct-RNA slice was not used for this change because the included genomic FASTA/motif fixtures provide exact boundary, coordinate, label, and count oracles. The added capacity checks run only during one-time preset construction, outside the per-read hot loop.

Output and compatibility

  • Previously omitted or duplicated affected coordinates intentionally change.
  • Unsupported ninth-motif requests now fail explicitly instead of returning incomplete output.
  • CLI options and bedMethyl schema are unchanged; valid requests with up to eight motifs retain the existing interface.
  • Documentation is updated to describe the interval and combine-strand behavior.

Non-goals

Reviewer guide

  1. Review the half-open owned-interval/context logic and bounded scan helpers in fasta.rs.
  2. Review preflight validation ordering in pileup/subcommand.rs, including the checks before and after automatic motif insertion.
  3. Review the boundary, combine-strand, dense-repeat, and eight/nine-motif oracles in test_pileup.rs.

Author checklist

  • The PR body contains the observed and expected behavior without requiring the linked issues.
  • Related motif changes are consolidated; unrelated MM-scanner and record-counting changes are excluded.
  • Parent-red and exact-final-head green evidence is recorded.
  • Focused, full-workspace, formatting, coordinate/count, and output-preservation checks are listed above.
  • The exact final head was independently reviewed before marking this PR ready.

@SuhasSrinivasan SuhasSrinivasan changed the title Preserve pileup motif ownership across chunks Correct pileup motif ownership, validation, and mask limits Aug 11, 2026
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