Repository navigation
feat(clip): route the clip command onto the declarative chain builder - #897
Conversation
|
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (4)
Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour. WalkthroughChangesClip chain migration
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The clip command is routed through the declarative chain while preserving the single-threaded parity path, with targeted fixes and broad verification reported as passing; no actionable merge-blocking risk remains beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant Clip
participant ChainBuilder
participant ClipProcessCaptures
participant ClipParams
Clip->>Clip: Run preflight validation
Clip->>ChainBuilder: execute_chain
ChainBuilder->>ChainBuilder: add_clip
ChainBuilder->>ClipProcessCaptures: build_clip_process_step
ClipProcessCaptures->>ClipParams: clip_template
ClipParams-->>ClipProcessCaptures: Clipped records and metrics
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
Full details: Title checkExplanation The title follows the required Conventional Commit format. It uses the valid type Comment |
|
@coderabbitai pause |
✅ Action performedReviews paused. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #897 +/- ##
==========================================
+ Coverage 92.95% 93.05% +0.10%
==========================================
Files 299 299
Lines 150352 150178 -174
==========================================
- Hits 139760 139752 -8
+ Misses 10592 10426 -166 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
1e813a7 to
6cf06fa
Compare
Route `fgumi clip --threads N` through the declarative chain builder (`ChainSpec::single_stage(Stage::Clip, ...)` -> `build_for(spec)?.run()`), keeping the no-`--threads` single-threaded engine (`execute_single_threaded`) as the in-process parity oracle. Dispatch sits after the reader-free pre-flight (output-collision check, input/reference existence, clipping-option and `--metrics`/`--threads` validation) but before the banner/timer/reader, so `add_clip` — which re-emits those and opens its own source — does not double-log or pre-consume stdin. The legacy threaded engine (`execute_threads_mode`) is removed in this PR: once `--threads` dispatches to the chain it has no call site, and it was never the parity oracle, so its now-orphan `CollectedClipMetrics`/`ClipProcessedBatch` helpers are removed with it. Fixes two real defects the cutover exposed: - The dormant `add_clip` never enforced `require_query_grouped`, though both legacy clip paths do. Add the guard, gated on Clip being the source stage so a future fused group->clip chain is not wrongly rejected. - The dormant chain clip step reimplemented per-template clipping and had drifted from the canonical `ClipParams::clip_template` (its own KNOWN DIVERGENCE comment): it selected the primary pair by positional index (ignoring secondary/supplementary reads) and applied fixed clipping with "clip N more" instead of "ensure at least N including existing clipping" semantics, over-clipping reads that already carried clips. Expose `ClipParams`/`clip_template`/`from_clip` as pub(crate) and delegate the chain step to the exact code the oracle runs; drop the now-dead `update_mate_info_raw` (clip_template repairs mate info via set_mate_info_raw). Parity test across thread counts (full normalized header + records); the pre-existing threaded tests now exercise the chain, and the existing-clipping unit test that surfaced the over-clip bug now passes. A rejection test pins the `--metrics`/`--threads` fast-fail.
6cf06fa to
3124a47
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
What
Route
fgumi cliponto the declarative chain builder — R3.x. This is the clip half of the command-cutover campaign (filter #892, correct #893, simplex #894, codec #895, duplex #896).clip --threads Nnow runs throughexecute_chain→ChainSpec::single_stage(Stage::Clip, …)→build_for(spec)?.run(). The no---threadssingle-threaded engine (execute_single_threaded) is kept unchanged as the in-process parity oracle. The old threaded engine (execute_threads_mode) is removed in this PR: once--threadsdispatches to the chain it has no call site, and it was never the parity oracle, so it and its now-orphan helpers (CollectedClipMetrics,ClipProcessedBatchand itsMemoryEstimateimpl + unit test) are deleted here rather than carried dead into a follow-up.clipis a core, non-feature-gated command, so the dispatch is unconditional. clip writes no rejects (only--outputand single-threaded-only--metrics), so there is no rejects-header-provenance decision. The dispatch sits after the reader-free pre-flight (output-collision check, input + reference existence, the clipping-option validation, and the--metrics/--threadsfail-fast) but before the banner / timer / reader, soadd_clip— which re-emits those and opens its own source — does not double-log or pre-consume stdin.Two real defects the cutover exposed (fixed here)
require_query_groupedwas missing on the chain path. Both legacy clip paths enforce fgbio'sBams.requireQueryGrouped, but the dormantadd_clipdid not — so routing--threadsonto the chain would silently mis-clip coordinate-sorted input the legacy path rejected. Added the guard inadd_clip, gated onStage::Clipbeing the source stage so a future fusedgroup→clipchain (upstream stage orders the records) is not wrongly rejected.ChainSpecexposes onlysingle_stagetoday, so the guard always fires. Pinned by the already-parameterizedtest_clip_rejects_coordinate_sorted_input/test_clip_rejects_headerless_input, whose--threadscase now runs the chain.The chain clip step over-clipped reads with existing clipping. The dormant
build_clip_process_stepreimplemented per-template clipping and carried an explicitKNOWN DIVERGENCE — MUST be resolved in the clip wiring PRcomment: it selected the primary pair by positional index (ignoring secondary/supplementary reads, and skipping templates with ≥3 records) and applied fixed clipping with "clip N more" (clip_*_end_of_alignment) instead of the canonical "ensure at least N including existing clipping" semantics — over-clipping any read already carrying soft/hard clips. This surfaced immediately: the existing unit testtest_fixed_position_clip_counts_existing_clipping::case_2_multi_threadedexpects5S10M5S(existing 5′ clip counted, 5 new 3′ bases) but the chain produced8S7M5S. Fixed by exposingClipParams/ClipParams::from_clip/ClipParams::clip_templateaspub(crate)and delegating the chain step tocap.params.clip_template(records, &clipper, None)— the exact code the oracle runs (its docstring already declares it "the single shared implementation used by both threading paths"). Parity is now by construction, not a parallel implementation. This let the now-deadupdate_mate_info_raw(itself carrying a "resolve in the clip wiring PR" note) be removed;clip_templaterepairs mate info via the canonicalset_mate_info_raw. The--metricsdetailed collection is never produced under--threads, so the chain passesNoneand drives its atomicoverlap_clipped/extend_clippedcounters offclip_template's returned per-template flags.Tests
test_clip_chain_matches_single_threaded(#[case]threads 1/2/4): chain vs. single-threaded oracle, full normalized-header + record parity viaread_bam_output, with non-vacuous guards (all 16 records survive; at least one output CIGAR ≠ 8M, so clipping actually ran).test_clip_rejects_metrics_with_threads: pins the reader-free--metrics+--threadsfast-fail (expect_err, message contains "cannot be used with --threads"), so a regression can't silently drop the user's requested metrics file.--check-crchandling, stdin-once, andtest_clip_command_single_and_multi_threaded_outputs_match.test_fixed_position_clip_counts_existing_clipping::case_2_multi_threaded.Verification
cargo ci-fmt && cargo ci-lint && RUSTDOCFLAGS="-D warnings" cargo ci-doc && cargo ci-test— all green (9772 tests). The three feature legs compile (--no-default-features, default,--all-features, all--all-targets). No newunsafe.Risk: output changes for
clipthreaded execution, pinned by single-threaded parity tests and sharedClipParams::clip_template; unsafe changes: none; memory, queue, and backpressure policy changes: none.Threaded
clipexecution now uses the declarative chain builder. The single-threaded path remains the parity oracle. Reader-free validation runs before dispatch and rejects invalid query grouping, output collisions, clipping options, and--metricswith--threads.The chain path reuses
ClipParams::clip_template. This preserves existing clipping behavior, prevents over-clipping, and applies canonical mate repair. The obsolete threaded engine and helper code were removed.Integration tests cover thread-count parity, normalized headers, clipping behavior, validation failures, and existing threaded scenarios.