You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fgumi-raw-bam::overlap was moved to query-space overlap-clip arithmetic in #759 (fgumi #752, the fgumi side of fgbio#1090). Three sibling implementations in fgumi-sam still carry the superseded reference-space formula:
All three build a reference coordinate — the mate's soft-only unclipped end — by adding the mate's trailing soft clip, a query distance, to the mate's alignment end, then look up the read position there. With an indel near the read's 3' end that coordinate lands somewhere the read never sequenced: inside a deletion it has no read position at all, read_pos_at_ref_pos(..., false) returns 0, and read_length - 0 clips the entire read. An insertion moves it the other way and under-clips by the insertion's length.
Who is affected
clipper.rs's pair is reached from SamRecordClipper::clip_extending_past_mate_ends, which fgumi clip calls (src/lib/commands/clip.rs). So fgumi clip --clip-overlapping-reads-style usage still mis-clips read-through pairs with a terminal indel.
record_utils::num_bases_extending_past_mate is currently only reached from a #[cfg(test)] bridge in vanilla_caller.rs, so it has no production impact today, but it is public API and will drift further from the raw-byte path.
The consensus callers (simplex, duplex, codec) are not affected — they all route through fgumi-raw-bam::overlap, fixed in #759.
The correct algorithm needs the mate's CIGAR, not two derived reference boundaries. clipper.rs's two functions take mate_unclipped_start / mate_unclipped_end as parameters, so fixing them means changing a public signature and its call sites — a wider change than the consensus fix warranted, and one worth doing on its own so its own before/after clipping counts can be measured.
Suggested fix
Port the algorithm from bases_extending_past_mate in crates/fgumi-raw-bam/src/overlap.rs: take the last reference position both alignments cover (the first, for a negative-strand read), count the query bases each read has past it, and clip the read down to the mate's count.
Two disjoint-alignment cases come with it, and they are not the same case — see the comment below for why this distinction was worth pinning down:
The read's 3' end faces the mate but its alignment stops short. Keep the existing extrapolation: project the read's soft-clipped tail one query base per reference base against the mate's soft-only unclipped boundary. Do not answer 0 — read-through is entirely possible here, and answering 0 leaves adapter on the read. The estimate is capped by the read's own soft clipping, so it never takes an aligned base.
The read's 3' end faces away from the mate, i.e. the mate lies entirely on the far side. Clip nothing. That is an outward-facing template rather than read-through, and measuring it against the anchor like any other read counts all of the read's query bases as past the mate and takes its aligned ones — the destructive answer overlap clipping reproduces fgbio #1090's reference/query distance conflation #752 exists to remove.
Note that reading a deleted base as the last one before it (htsjdk's returnLastBaseIfDeleted = true) is not a fix — it replaces one reference-space answer with another and still carries the mate's soft clip in the wrong space.
overlap.rs's test suite is a ready-made oracle: the deletion-at-boundary pair, fgbio#1090's own insertion example, an ungapped control that must not move, test_disjoint_alignments_still_clip_their_read_through for the first bullet above, and test_num_bases_extending_past_mate_raw_read_entirely_past_mate for the second.
Summary
fgumi-raw-bam::overlapwas moved to query-space overlap-clip arithmetic in #759 (fgumi #752, the fgumi side of fgbio#1090). Three sibling implementations infgumi-samstill carry the superseded reference-space formula:SamRecordClipper::num_bases_extending_past_mate(crates/fgumi-sam/src/clipper.rs)SamRecordClipper::num_bases_extending_past_mate_raw(same file)record_utils::num_bases_extending_past_mate(crates/fgumi-sam/src/record_utils.rs)All three build a reference coordinate — the mate's soft-only unclipped end — by adding the mate's trailing soft clip, a query distance, to the mate's alignment end, then look up the read position there. With an indel near the read's 3' end that coordinate lands somewhere the read never sequenced: inside a deletion it has no read position at all,
read_pos_at_ref_pos(..., false)returns0, andread_length - 0clips the entire read. An insertion moves it the other way and under-clips by the insertion's length.Who is affected
clipper.rs's pair is reached fromSamRecordClipper::clip_extending_past_mate_ends, whichfgumi clipcalls (src/lib/commands/clip.rs). Sofgumi clip --clip-overlapping-reads-style usage still mis-clips read-through pairs with a terminal indel.record_utils::num_bases_extending_past_mateis currently only reached from a#[cfg(test)]bridge invanilla_caller.rs, so it has no production impact today, but it is public API and will drift further from the raw-byte path.The consensus callers (
simplex,duplex,codec) are not affected — they all route throughfgumi-raw-bam::overlap, fixed in #759.Why it was not fixed in #759
The correct algorithm needs the mate's CIGAR, not two derived reference boundaries.
clipper.rs's two functions takemate_unclipped_start/mate_unclipped_endas parameters, so fixing them means changing a public signature and its call sites — a wider change than the consensus fix warranted, and one worth doing on its own so its own before/after clipping counts can be measured.Suggested fix
Port the algorithm from
bases_extending_past_mateincrates/fgumi-raw-bam/src/overlap.rs: take the last reference position both alignments cover (the first, for a negative-strand read), count the query bases each read has past it, and clip the read down to the mate's count.Two disjoint-alignment cases come with it, and they are not the same case — see the comment below for why this distinction was worth pinning down:
Note that reading a deleted base as the last one before it (htsjdk's
returnLastBaseIfDeleted = true) is not a fix — it replaces one reference-space answer with another and still carries the mate's soft clip in the wrong space.overlap.rs's test suite is a ready-made oracle: the deletion-at-boundary pair, fgbio#1090's own insertion example, an ungapped control that must not move,test_disjoint_alignments_still_clip_their_read_throughfor the first bullet above, andtest_num_bases_extending_past_mate_raw_read_entirely_past_matefor the second.