Skip to content

v0.3.0 update to fix read cuts boundary bug and add soft-clip fields#8

Merged
Psy-Fer merged 7 commits into
mainfrom
dev
Jul 22, 2026
Merged

v0.3.0 update to fix read cuts boundary bug and add soft-clip fields#8
Psy-Fer merged 7 commits into
mainfrom
dev

Conversation

@Psy-Fer

@Psy-Fer Psy-Fer commented Jul 22, 2026

Copy link
Copy Markdown
Owner

[0.3.0] — 2026-07-21

Coordinate-math correctness pass, driven by a differential test against
bladerunner and aimed at letting bladerunner link
bedpull as a library.

Fixed

  • Boundary-coincidence bug in get_read_cuts — a read whose alignment began (or ended)
    exactly at a region boundary was mis-sliced or silently dropped. The start > 0 sentinel
    conflated "start not found yet" with "start found at read offset 0". Replaced with an explicit
    found_start flag plus an op-entry guard, and the op-level == ref_end shortcut no longer
    skips a mid-op ref_start crossing. This affected BAM/CRAM extraction (the PAF path already
    routed around it via read_pos_at_ref).

  • Spurious missing_left=1bp on fully-spanning BAM/CRAM readsget_bam_reads /
    get_cram_reads returned ref_start/ref_end in the 1-based CIGAR-walk frame while the
    header suffix compared them against 0-based BED coordinates, so every spanning read was
    mislabelled as missing 1bp on the left. The returned coordinates are now normalised to
    0-based (a pre-existing bug, also present in v0.2.0; extraction/sequences were never affected).

Changed

  • get_read_cuts signature is now
    get_read_cuts(cigar_ops, align_start, align_end, region_start, region_end). It takes the
    alignment end and clamps its fire-on boundaries to the alignment span internally
    (ref_start = max(region_start, align_start), ref_end = min(region_end, align_end)), so a
    partially-overlapping read yields a real slice instead of a read_end == 0 sentinel.
    region_start/region_end are the desired (flank-expanded) window; the caller no longer
    pre-clamps. align_end is exclusive (one past the last reference base) — callers using
    noodles' inclusive alignment_end() must add 1.
  • ReadCuts gains softclip_lead_start and softclip_trail_end (read offsets of the
    leading/trailing soft-clip runs; equal to read_start/read_end when no extension is
    available). Layout is now field-identical to bladerunner's ReadCuts.
  • noodles bumped 0.1100.111 to match bladerunner across the crate boundary.

Added

  • Regression battery for the boundary-coincidence cases and the soft-clip extension fields.

Psy-Fer added 7 commits July 21, 2026 14:01
…odles

Coordinate-math correctness pass driven by a differential test against
bladerunner, aimed at letting bladerunner link bedpull as a library.

- Fix boundary-coincidence bug in get_read_cuts: a read whose alignment
  began/ended exactly at a region boundary was mis-sliced or silently
  dropped (BAM/CRAM path). Replace the `start > 0` sentinel with an
  explicit found_start flag + op-entry guard; the op-level `== ref_end`
  shortcut no longer skips a mid-op ref_start crossing.
- get_read_cuts now takes align_end (exclusive) and clamps its fire-on
  boundaries to the alignment span internally, so partial reads return a
  real slice instead of a read_end == 0 sentinel.
- ReadCuts gains softclip_lead_start / softclip_trail_end, making its
  layout field-identical to bladerunner's ReadCuts.
- Simplify resolve_cuts: spanning is decided by align coverage, not by
  un-swapping sentinel fields. Real-data BAM/CRAM tests unchanged
  (40 spanning / 44 partial).
- Bump noodles 0.110 -> 0.111 to match bladerunner across the boundary.
- Add regression battery for the boundary cases and soft-clip fields;
  update CHANGELOG and version.
…odles

Coordinate-math correctness pass driven by a differential test against
bladerunner, aimed at letting bladerunner link bedpull as a library.

- Fix boundary-coincidence bug in get_read_cuts: a read whose alignment
  began/ended exactly at a region boundary was mis-sliced or silently
  dropped (BAM/CRAM path). Replace the `start > 0` sentinel with an
  explicit found_start flag + op-entry guard; the op-level `== ref_end`
  shortcut no longer skips a mid-op ref_start crossing.
- get_read_cuts now takes align_end (exclusive) and clamps its fire-on
  boundaries to the alignment span internally, so partial reads return a
  real slice instead of a read_end == 0 sentinel.
- ReadCuts gains softclip_lead_start / softclip_trail_end, making its
  layout field-identical to bladerunner's ReadCuts.
- Simplify resolve_cuts: spanning is decided by align coverage, not by
  un-swapping sentinel fields. Real-data BAM/CRAM tests unchanged
  (40 spanning / 44 partial).
- Bump noodles 0.110 -> 0.111 to match bladerunner across the boundary.
- Add regression battery for the boundary cases and soft-clip fields;
  update CHANGELOG and version.
get_bam_reads/get_cram_reads returned ref_start/ref_end in the 1-based
CIGAR-walk frame, but the |missing_left/right header suffix compares them
against 0-based BED coordinates — so every fully-spanning read was
mislabelled missing_left=1bp. Normalise the returned coordinates to
0-based at the reads-layer boundary (lengths unchanged, so the partial
coverage check is unaffected). Pre-existing since v0.2.0; only the header
text was wrong, never the extracted sequence.

Add an end-to-end regression test via the read_bed (+1-shifted) path.
@Psy-Fer
Psy-Fer merged commit 2c5ee75 into main Jul 22, 2026
2 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.

1 participant