Skip to content

feat(sort): extract framework-light sort CLI into standalone crates (#446) - #447

Merged
nh13 merged 1 commit into
feat-runallfrom
446/nh/feat-sort-cli
Jun 22, 2026
Merged

feat(sort): extract framework-light sort CLI into standalone crates (#446)#447
nh13 merged 1 commit into
feat-runallfrom
446/nh/feat-sort-cli

Conversation

@nh13

@nh13 nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

Closes #446. Extracts the fgumi sort command and its pipeline steps into three new crates so a sort-only downstream (e.g. mako) can depend on the full sort command without compiling the UMI/consensus stack. This is a behavior-preserving extraction — no functional changes.

Previously the only way to reuse the sort command (clap struct + --order/--verify/memory wiring + @PG/header plumbing) was to depend on the umbrella fgumi crate, which pulls the entire dependency tree (~227 crates) regardless of features, because Sort::execute routes through the monolithic pipeline::chains::build_for that compiles every stage (group → fgumi-umi, simplex/duplex → fgumi-consensus, extract → fgumi-simd-fastq).

Approach — framework-light

fgumi sort still runs on the typed-step pipeline framework (fgumi-pipeline-core); it just builds a single-stage source → sort → sink pipeline directly via Pipeline::builder() instead of going through the all-stages ChainBuilder. That keeps the sort path on the unified execution model while dropping the heavy stage dependencies.

Three new crates:

  • fgumi-cli-common — shared CLI infrastructure: the Command trait, OperationTimer + logging helpers, the MemoryLimit/MemoryReserve/CompressionOptions + parse cluster, file validation, and memory detection.
  • fgumi-pipeline-io — generic BGZF source/sink steps (ReadBgzfBlocks, WriteBgzfFile) plus the typed-step sort steps (SortBamFile, SortAndSpill, SortMerge, SortSpillDecompress).
  • fgumi-sort-cli — the Sort command, SortOptions/MultiSortOptions, SortOrderArg, the finalize hooks, and the step factory. Its dependency graph is fgumi-pipeline-core + fgumi-pipeline-io + fgumi-sort + fgumi-bam-io + fgumi-sam + fgumi-cli-common + fgumi-cli-macrosno fgumi-consensus, fgumi-umi, or fgumi-simd-fastq.

The umbrella fgumi crate re-exports the moved items at their original module paths, so runall, chains, merge, and main.rs compile unchanged. The chains layer / runall import the sort step and step factory from the new crates one-way (umbrella → new crates); intermediate/fused sort within a chain is untouched.

Verification

  • cargo build --release, cargo ci-test (all pass), cargo ci-lint (clippy pedantic), cargo ci-fmt, and cargo check --workspace --no-default-features --all-targets all green.
  • Dependency-tree proof: fgumi-sort-cli resolves with zero of fgumi-consensus/fgumi-umi/fgumi-simd-fastq/nalgebra/matrixmultiply/statrs.
  • Byte-identical output: sorted BAMs are byte-identical to the pre-extraction binary across coordinate, queryname, queryname::natural, and template-coordinate orders, plus stdin; --verify runs through the new crate. (The git-augmented @PG VN/CL stamp is preserved via a process-global version override installed by the umbrella's main.)

Reading order

  1. crates/fgumi-cli-common/src/lib.rs — the shared leaf helpers.
  2. crates/fgumi-pipeline-io/ — the moved steps (mostly verbatim relocations; git shows them as renames).
  3. crates/fgumi-sort-cli/src/{sort.rs,chains.rs} — the Sort command and its direct pipeline builder.
  4. The umbrella shims (src/lib/commands/{command,common,sort}.rs, src/lib/pipeline/..., src/main.rs).

Follow-up

A stacked PR on top of this branch addresses the CodeRabbit findings (all of which are pre-existing — in fgumi-pipeline-core and in the verbatim-moved sort steps — and are intentionally kept out of this behavior-preserving extraction).

Summary by CodeRabbit

  • New Features
    • Added support for multiple BAM sort orderings: coordinate, queryname (lexicographic and natural), and template-coordinate.
    • Added sort verification mode to validate ordering without re-sorting.
    • Improved fgumi sort @PG metadata version reporting and added shared CLI option parsing (memory budgeting and compression level validation).
  • Bug Fixes
    • Generate the BAI index only after successful coordinate sort completion, preventing stale/incomplete indexes.
  • Refactor
    • Reorganized the sort pipeline and unified shared CLI parsing/validation (including byte-bounded buffering through the pipeline).

@nh13
nh13 temporarily deployed to github-actions June 20, 2026 03:07 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Three new workspace crates (fgumi-cli-common, fgumi-pipeline-io, fgumi-sort-cli) extract shared CLI utilities, BAM pipeline I/O steps, and the full fgumi sort command. Existing main-crate modules become re-export shims. Sort pipeline queues switch from count-bounded to byte-bounded, and SortMerge/SortSpillDecompress control flow is refactored for cooperative batching and efficient memory-chunk absorption.

Changes

Sort CLI and pipeline I/O crate extraction

Layer / File(s) Summary
Workspace and new crate manifests
Cargo.toml, crates/fgumi-cli-common/Cargo.toml, crates/fgumi-pipeline-io/Cargo.toml, crates/fgumi-sort-cli/Cargo.toml
Workspace members/dependencies extended; three new crate manifests added with pinned external deps and feature flags (clap derive/string, noodles BAM/SAM/BGZF, sysinfo system feature, parking_lot versioning).
fgumi-cli-common: shared CLI utilities
crates/fgumi-cli-common/src/lib.rs
Adds Command trait, FgumiError/FgumiResult, cgroup-aware detect_total_memory() and detect_cpu_count(), duration/rate formatting with OperationTimer, strict parse_memory_size() (rejects sci-notation and plain decimals), MemoryLimit/MemoryReserve with per-thread resolve_memory_budget() (includes overflow checks, min-per-thread enforcement, host-available capping), parse_bool() with aliases, and CompressionOptions (0–12 clamp).
fgumi-pipeline-core: ByteBounded queue for Single<T>
crates/fgumi-pipeline-core/src/handles.rs
build_single_queues routes Single<T> through build_branch_byte_aware instead of panicking path; regression test replaced with successful typed end-to-end push/pop validation.
fgumi-pipeline-io: crate root and flowing data types
crates/fgumi-pipeline-io/src/lib.rs, crates/fgumi-pipeline-io/src/types.rs
Crate root declares modules and re-exports pipeline surface; BgzfBlock, DecompressedBlock, RecordBatch (flat Vec<u8> backing + Vec<(u32, u32)> per-record ranges), and RecordBatchBuilder implement HeapSize (capacity-based) and Ordered (batch_serial) for byte-bounded queue depth tracking and global ordering.
fgumi-pipeline-io: ReadBgzfBlocks source step
crates/fgumi-pipeline-io/src/source/read_bam.rs
Serial sticky reader-affine source reads raw BGZF blocks without decompression, assigns monotonic batch_serial, validates uncompressed_size as u32 with InvalidData fallback. read_bam, read_bam_stdin, read_bam_auto convenience helpers parse BAM headers and construct step with configurable blocks_per_batch and byte-bounded output queue.
fgumi-pipeline-io: WriteBgzfFile sink step
crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
BGZF sink supports eager header write via new() or deferred via new_with_handle(HeaderHandle) gating header/EOF until resolution; persistent Mutex<Option<WriterState>> buffers output; Drop leaves empty file if handle never resolves, only flushes bytes if state taken (no BGZF_EOF on premature drop).
fgumi-pipeline-io: sort protocol and SortAndSpill byte-limit
crates/fgumi-pipeline-io/src/sort/protocol.rs, crates/fgumi-pipeline-io/src/sort/and_spill.rs
Protocol docs condensed and HeapSize import retargeted to fgumi_pipeline_core; all event variants now charge base size_of::<Self>() cost (previously AllAnnounced was zero). SortAndSpill replaces output_capacity: usize with output_byte_limit: u64 in struct, constructor, and profile() reporting QueueSpec::ByteBounded; state machine and runtime behavior unchanged.
fgumi-pipeline-io: SortMerge refactor with typed merging
crates/fgumi-pipeline-io/src/sort/merge.rs
Introduces MemoryChunksByKind typed accumulator and build_driver helper; Arc::try_unwrap on absorption avoids clone when possible; slot_set_complete gates setup→merging transition; unbounded drain in setup phase; emit_batches_cooperative() bounded by MAX_DRAIN_BATCHES_PER_LOCK returning Progress/Contention/Finished based on delivery and EOF; try_run flushes held output, conditionally absorbs setup, emits cooperatively, errors if input drained before setup complete.
fgumi-pipeline-io: SortSpillDecompress parallel decompression
crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
Parallel step registers SortMergeSlots on SpillReady, forwards events as SortPhase2Event; greedy try_fill_some_slot decompression bounded by PHASE2_DECOMP_CAP and MAX_BATCH_PER_CALL, sets queue_eof/decomp_error under slot lock; output backpressure via held/unpushed buffer; returns Progress/Contention/Finished/NoProgress based on work and EOF readiness.
fgumi-pipeline-io: SortBamFile exclusive step and sort module
crates/fgumi-pipeline-io/src/sort/mod.rs
SortBamFile exclusive step wraps RawExternalSorter, takes sorter once per try_run, invokes sort(&input, &output), publishes SortStats to mutex-protected slot, returns Finished; sort module declares submodules and re-exports step types.
fgumi-pipeline-io: integration tests rewired
crates/fgumi-pipeline-io/src/sort/tests.rs
Tests switched to crate::types::RecordBatch and fgumi_pipeline_core imports; drive_sort_pipeline threads caller's output_byte_limit into constructors; SortBamFile test creates shared stats_slot and validates output record count; new SortMerge fail-closed regression tests cover empty input and incomplete-setup error.
fgumi-sort-cli: command parsing and execution
crates/fgumi-sort-cli/src/sort.rs
SortOrderArg with coordinate/queryname/natural/template-coordinate and queryname::... sub-syntax; clap Sort command with --verify, --order, --write-index, SortOptions (memory/stage/temp tuning); parse_cell_tag returns Some(CB) for template-coordinate only; resolve_tmp_dirs CLI→env→empty precedence with blank filtering; execute validates flag combinations and dispatches to verify (raw record iteration + monotonicity check) or sort (pipeline path) mode.
fgumi-sort-cli: sorter configuration and finalization
crates/fgumi-sort-cli/src/chains.rs, crates/fgumi-sort-cli/src/sort.rs
build_sort_step configures RawExternalSorter (order, cell-tag, memory/threads/compression, @PG info, temp dirs); log_sort_start emits startup banner with paths/order/limits. SortFinalizeHook::finalize logs stats and output path; IndexBamFinalizeHook::finalize writes .bam.bai sidecar only on successful pipeline completion. OnceLock-based set_version_override/version_string for @PG records.
Main-crate re-export shims and builder wiring
src/lib/commands/*, src/lib/pipeline/chains/commands/sort.rs, src/lib/pipeline/steps/*, src/lib/pipeline/chains/builder.rs, src/main.rs
All extracted logic becomes pub use re-exports; detect_total_memory gated to #[cfg(test)]; moved tests replaced with cross-crate coverage comments. ChainBuilder::add_sort propagates effective_memory into SortStepCaptures and uses self.tuning.per_step_byte_limit for spill/decompress constructors. main() installs sort-cli version override at startup.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(100, 149, 237, 0.5)
        Note over Sort: fgumi-sort-cli
        participant Sort
        participant build_sort_step
        participant SortBamFile
    end
    rect rgba(60, 179, 113, 0.5)
        Note over ReadBgzfBlocks,WriteBgzfFile: fgumi-pipeline-io (streaming path)
        participant ReadBgzfBlocks
        participant SortAndSpill
        participant SortSpillDecompress
        participant SortMerge
        participant WriteBgzfFile
    end
    Sort->>build_sort_step: SortStepCaptures (effective_memory, order, threads)
    build_sort_step->>SortBamFile: RawExternalSorter + stats_slot
    Sort->>ReadBgzfBlocks: read_bam_auto (byte-bounded output queue)
    ReadBgzfBlocks->>SortAndSpill: BgzfBlock (output_byte_limit)
    SortAndSpill->>SortSpillDecompress: SortPhase1Event (SpillReady/MemoryChunk/AllAnnounced)
    SortSpillDecompress->>SortSpillDecompress: greedy per-slot decompression with backpressure
    SortSpillDecompress->>SortMerge: SortPhase2Event + decompressed slot blocks
    SortMerge->>WriteBgzfFile: RecordBatch (ByItemOrdinal, cooperative drain)
    WriteBgzfFile->>WriteBgzfFile: deferred HeaderHandle resolution then BGZF_EOF
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • fulcrumgenomics/fgumi#381: Introduced MemoryLimit/MemoryReserve and resolve_memory_budget in src/lib/commands/common.rs; this PR moves that exact implementation into fgumi-cli-common and re-exports it.
  • fulcrumgenomics/fgumi#399: Introduced SpillBlockDecompressor and SortMergeSlot that are consumed directly by the new SortSpillDecompress step in this PR.
  • fulcrumgenomics/fgumi#395: Implemented the fused streaming sort pipeline around SortAndSpill → SortSpillDecompress → SortMerge that is now extracted into fgumi-pipeline-io.

Suggested labels

fgumi sort, enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: extracting sort CLI into standalone crates.
Linked Issues check ✅ Passed All coding requirements from #446 are met: three new crates extract sort CLI, umbrella re-exports preserve compatibility, zero compilation of consensus/UMI/SIMD dependencies in fgumi-sort-cli.
Out of Scope Changes check ✅ Passed Changes are narrowly scoped to the extraction goal: new crates, re-exports in umbrella, and version-override wiring. No unrelated refactoring or feature-gating.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 446/nh/feat-sort-cli

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (feat-runall@7dd3c43). Learn more about missing BASE report.

Additional details and impacted files
@@              Coverage Diff               @@
##             feat-runall     #447   +/-   ##
==============================================
  Coverage               ?   93.99%           
==============================================
  Files                  ?      111           
  Lines                  ?    48654           
  Branches               ?        0           
==============================================
  Hits                   ?    45730           
  Misses                 ?     2924           
  Partials               ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resume

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-cli-common/src/lib.rs`:
- Around line 105-112: The docstring for the detect_cpu_count() function claims
it respects cgroup CPU quotas, but the underlying num_cpus crate has
inconsistent cgroup v2 handling that may cause it to return physical core count
instead of respecting quotas. Either update the docstring to soften the language
(e.g., change "Respects" to "May respect") to accurately reflect the limitation,
or consider replacing num_cpus::get() with std::thread::available_parallelism()
which has better cgroup v2 support and has been stable since Rust 1.59.

In `@crates/fgumi-pipeline-io/src/source/read_bam.rs`:
- Around line 126-127: The uncompressed_size field assignment uses
unwrap_or(u32::MAX) which silently masks invalid BGZF metadata by saturating to
the maximum value. Instead of this silent fallback, replace the
unwrap_or(u32::MAX) with proper error handling that returns an InvalidData error
when the conversion fails, ensuring that invalid uncompressed sizes are properly
rejected rather than masked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a5b4a8ff-84fd-4a4d-9d2f-252bee261225

📥 Commits

Reviewing files that changed from the base of the PR and between a5c77b3 and d4a2918.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (37)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-cli-common/src/lib.rs
Comment thread crates/fgumi-pipeline-io/src/source/read_bam.rs Outdated
@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resume

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/fgumi-pipeline-io/src/sort/tests.rs (1)

423-433: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Assert SortBamFile publishes stats.

This test would still pass if SortBamFile::try_run stopped filling stats_out; assert the shared slot after Pipeline::run because SortFinalizeHook consumes that contract.

Proposed test assertion
+    let stats_slot = Arc::new(parking_lot::Mutex::new(None));
     let step = SortBamFile::new(
         sorter,
         input.clone(),
         pipeline_out.clone(),
-        Arc::new(parking_lot::Mutex::new(None)),
+        Arc::clone(&stats_slot),
     );
@@
     let pipeline = builder.build().expect("Pipeline::build");
     pipeline.run(PipelineConfig { threads: 1, ..Default::default() }).expect("Pipeline::run");
+    let stats = stats_slot.lock().take().expect("SortBamFile should publish SortStats");
+    assert_eq!(stats.total_records, records.len() as u64);
+    assert_eq!(stats.output_records, records.len() as u64);
 
     let legacy_records = read_all_records(&legacy_out);

Based on supplied context, SortFinalizeHook reads this shared stats slot after Pipeline::run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fgumi-pipeline-io/src/sort/tests.rs` around lines 423 - 433, After the
pipeline.run() call in the test, add an assertion to verify that the stats_out
shared slot contains stats data. The stats_out variable (the
Arc<parking_lot::Mutex<Option<T>>> passed to SortBamFile::new) should be locked
and asserted to contain Some(...) rather than None to ensure that
SortBamFile::try_run actually published the expected stats before the pipeline
completed, since SortFinalizeHook depends on consuming this contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-pipeline-io/src/sort/merge.rs`:
- Around line 27-28: The MAX_EVENTS_PER_LOCK constant caps the input drain at 16
events which causes upstream queue cycling and contention. Remove or
significantly increase this constant and modify the drain loop logic (located
around the areas that check against this constant in the event processing) to
drain events unboundedly from the input queue, while keeping the existing bounds
on the producer and output queues. The issue appears in multiple locations where
the drain is capped by this constant (around lines 244-274 and 439-440), so
ensure all input drain loops are made unbounded.

In `@crates/fgumi-pipeline-io/src/sort/spill_decompress.rs`:
- Around line 153-160: In the profile method of the SortSpillDecompress struct,
the output_queues field currently uses QueueSpec::CountBounded which only limits
the number of events, but since SortPhase2Event::MemoryChunk retains sorted
record chunks that can consume significant memory, this needs to be byte-bounded
instead. Replace the QueueSpec::CountBounded variant with the appropriate
byte-bounded QueueSpec variant (using HeapSize accounting) to ensure memory
retention is capped by bytes based on configuration rather than event count.
Update the capacity parameter accordingly to use byte bounds instead of count
bounds.
- Around line 78-130: The current implementation only enforces a per-slot cap
(PHASE2_DECOMP_CAP) in the try_fill_some_slot method, allowing total
decompressed data to grow unbounded across all slots. Introduce a global shared
budget or capacity counter for total decompressed data across all slots, and
gate the read_blocks call to only proceed if there is remaining global budget.
Track the bytes/blocks added to each slot.decompressed queue against this global
budget when data is pushed, and ensure that SortMerge (the component consuming
this data) decrements the global budget as it drains decompressed batches to
maintain memory as a function of configuration rather than input size.

In `@crates/fgumi-pipeline-io/src/types.rs`:
- Around line 28-31: The HeapSize implementations are reporting logical vector
length instead of allocated capacity, which underestimates actual heap memory
when vectors are pre-allocated with capacity exceeding current length. This
causes memory backpressure to undercount and bypass configured limits. In the
heap_size method of the HeapSize impl for BgzfBlock (line 30), change
self.bytes.len() to self.bytes.capacity(). Apply the same fix to the other
HeapSize impls at lines 55 and 196 where Vec::len() is used. Additionally, check
for any ranges.len() calls and change them to ranges.capacity() to properly
account for allocated heap space rather than just populated elements.

In `@crates/fgumi-sort-cli/src/chains.rs`:
- Around line 137-142: The `resolve_memory_budget` function is being called
redundantly in `build_sort_step` (chains.rs) when it has already been computed
in `execute_sort` (sort.rs). To fix this, add an `effective_memory: usize` field
to the `SortStepCaptures` struct, pass the already-computed `effective_memory`
value from `execute_sort` to `SortStepCaptures`, and then use this field value
in `build_sort_step` instead of calling `resolve_memory_budget` again. This
eliminates the redundant syscall to `detect_total_memory()` and ensures
consistency between the logged and configured values.

---

Outside diff comments:
In `@crates/fgumi-pipeline-io/src/sort/tests.rs`:
- Around line 423-433: After the pipeline.run() call in the test, add an
assertion to verify that the stats_out shared slot contains stats data. The
stats_out variable (the Arc<parking_lot::Mutex<Option<T>>> passed to
SortBamFile::new) should be locked and asserted to contain Some(...) rather than
None to ensure that SortBamFile::try_run actually published the expected stats
before the pipeline completed, since SortFinalizeHook depends on consuming this
contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bfca4119-72b1-4f57-84d3-fc5660c2eece

📥 Commits

Reviewing files that changed from the base of the PR and between d4a2918 and 2ae025e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (37)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs

Comment thread crates/fgumi-pipeline-io/src/sort/merge.rs Outdated
Comment thread crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
Comment thread crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
Comment thread crates/fgumi-pipeline-io/src/types.rs
Comment thread crates/fgumi-sort-cli/src/chains.rs Outdated
@nh13
nh13 force-pushed the 446/nh/feat-sort-cli branch from 2ae025e to e382c96 Compare June 20, 2026 22:45
@nh13
nh13 temporarily deployed to github-actions June 20, 2026 22:45 — with GitHub Actions Inactive
@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai resume

@nh13

nh13 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-pipeline-io/src/sink/write_bgzf.rs`:
- Around line 159-168: The Drop implementation for WriteBgzfFile is writing
BGZF_EOF even when the sink is dropped abnormally (before the normal finish
path), which causes incomplete output to appear as valid BGZF streams. Remove
the lines that write BGZF_EOF (the write_all(&BGZF_EOF) and flush() calls) from
the Drop implementation. Instead, ensure BGZF_EOF is only written through an
explicit finish or drain method, not during cleanup. This prevents silent
corruption where partial output becomes seemingly complete.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1ad39900-5a7f-41a7-a561-86f059e53355

📥 Commits

Reviewing files that changed from the base of the PR and between 2ae025e and e382c96.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (39)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-core/src/handles.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/builder.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-cli-common/src/lib.rs`:
- Around line 548-549: The compression_level field in the struct is missing a
range validator to enforce the documented 1-12 bounds for valid compression
levels. Add a value_parser with range validation to the existing #[arg]
attribute on the compression_level field by specifying
value_parser!(u32).range(1..=12) to reject out-of-range inputs immediately at
parse time instead of allowing any u32 value to be accepted.

In `@crates/fgumi-pipeline-io/src/types.rs`:
- Around line 254-261: The assertion in the test function
`record_batch_total_bytes_sums_record_lengths` at line 260 uses a hardcoded
expected heap_size value that assumes Vec::with_capacity allocates exactly the
requested capacity, but allocators may allocate larger blocks. Instead of using
the hardcoded formula, capture the actual allocated capacities of the internal
backing and ranges vectors after constructing the RecordBatch object, then
calculate the expected heap_size based on those actual capacities. Follow the
same pattern demonstrated in the earlier test function
`heap_size_counts_allocated_capacity_not_logical_len` in this module, which
already shows the correct approach for handling variable allocator behavior.

In `@crates/fgumi-sort-cli/src/sort.rs`:
- Around line 474-477: The IndexBamFinalizeHook finalization is currently
running unconditionally when self.write_index is true, even if the pipeline.run
operation failed. The fix is to gate the finalization on successful sort
completion by ensuring IndexBamFinalizeHook { output_path }.finalize() is only
called when run_result is Ok. Modify the conditional logic to check
run_result.is_ok() in addition to self.write_index before executing the finalize
call, or use and_then to chain the operations so finalization only happens after
a successful pipeline run result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 47f40acc-ba6c-4eea-b094-74ac6a54b872

📥 Commits

Reviewing files that changed from the base of the PR and between e382c96 and 97f3e47.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (40)
  • .coderabbit.yaml
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-core/src/handles.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/builder.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-cli-common/src/lib.rs Outdated
Comment thread crates/fgumi-pipeline-io/src/types.rs
Comment thread crates/fgumi-sort-cli/src/sort.rs Outdated
@nh13
nh13 force-pushed the 446/nh/feat-sort-cli branch from 97f3e47 to 72aa4bd Compare June 21, 2026 10:10
@nh13
nh13 temporarily deployed to github-actions June 21, 2026 10:10 — with GitHub Actions Inactive
@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/fgumi-pipeline-io/src/sort/protocol.rs (1)

80-87: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Charge a non-zero heap cost for control events.

SpillReady can carry an empty path and AllAnnounced reports 0, so the byte-bounded event queues can still accept an unbounded count of control events. Add a fixed per-event cost while preserving the MemoryChunk payload accounting.

As per coding guidelines, no transport queue may grow without a byte/size bound; memory must be a function of configuration.

Proposed fix
 impl HeapSize for SortPhase1Event {
     fn heap_size(&self) -> usize {
+        let base = std::mem::size_of::<Self>();
         match self {
-            Self::SpillReady { path, .. } => path.as_os_str().len(),
-            Self::MemoryChunk { chunk, .. } => chunk.approx_heap_bytes(),
-            Self::AllAnnounced { .. } => 0,
+            Self::SpillReady { path, .. } => base + path.as_os_str().len(),
+            Self::MemoryChunk { chunk, .. } => base + chunk.approx_heap_bytes(),
+            Self::AllAnnounced { .. } => base,
         }
     }
 }
@@
 impl HeapSize for SortPhase2Event {
     fn heap_size(&self) -> usize {
+        let base = std::mem::size_of::<Self>();
         match self {
-            Self::SpillReady { path, .. } => path.as_os_str().len(),
-            Self::MemoryChunk { chunk, .. } => chunk.approx_heap_bytes(),
-            Self::AllAnnounced { .. } => 0,
+            Self::SpillReady { path, .. } => base + path.as_os_str().len(),
+            Self::MemoryChunk { chunk, .. } => base + chunk.approx_heap_bytes(),
+            Self::AllAnnounced { .. } => base,
         }
     }
 }

Also applies to: 100-107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fgumi-pipeline-io/src/sort/protocol.rs` around lines 80 - 87, The
HeapSize implementation for SortPhase1Event currently reports zero heap cost for
control events (SpillReady and AllAnnounced), allowing unbounded counts of these
events in byte-bounded queues. Modify the heap_size method to add a fixed
per-event cost for both SpillReady and AllAnnounced variants (for example, a
small constant overhead per control event) while preserving the existing
MemoryChunk payload accounting. This ensures control events contribute to the
byte budget and cannot accumulate unboundedly. Apply the same fix to any other
similar HeapSize implementations for related event types.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-cli-common/src/lib.rs`:
- Around line 648-674: Add `rstest = "0"` to the `[dev-dependencies]` section in
`crates/fgumi-cli-common/Cargo.toml`. Then refactor the
`test_compression_level_rejects_out_of_range` function by splitting it into two
separate test functions: create a new parameterized test using the `#[rstest]`
macro with the parameter values [0, 1, 6, 12] that tests the in-range
compression levels, and keep the default value assertion and out-of-range
rejection assertions in a separate standard test function. Use the `#[rstest]`
procedural macro pattern consistent with other tests in the workspace to
parameterize the compression level values rather than using a manual for loop.

---

Outside diff comments:
In `@crates/fgumi-pipeline-io/src/sort/protocol.rs`:
- Around line 80-87: The HeapSize implementation for SortPhase1Event currently
reports zero heap cost for control events (SpillReady and AllAnnounced),
allowing unbounded counts of these events in byte-bounded queues. Modify the
heap_size method to add a fixed per-event cost for both SpillReady and
AllAnnounced variants (for example, a small constant overhead per control event)
while preserving the existing MemoryChunk payload accounting. This ensures
control events contribute to the byte budget and cannot accumulate unboundedly.
Apply the same fix to any other similar HeapSize implementations for related
event types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1a39adec-3d38-49a3-b43b-ec801ce9fce7

📥 Commits

Reviewing files that changed from the base of the PR and between 97f3e47 and 72aa4bd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (39)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-core/src/handles.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/builder.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-cli-common/src/lib.rs Outdated
@nh13
nh13 force-pushed the 446/nh/feat-sort-cli branch 2 times, most recently from ebc5567 to 6af43c3 Compare June 21, 2026 19:27
@nh13
nh13 temporarily deployed to github-actions June 21, 2026 19:27 — with GitHub Actions Inactive
@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/fgumi-pipeline-io/src/sort/merge.rs (1)

442-450: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed when setup is incomplete at input drain.

If ctx.input is drained while is_ready_to_merge() is still false, this path still transitions to Merging, which can silently merge an incomplete setup. Return an error for any non-empty/partially-announced setup; only allow the explicit empty-input case.

Proposed fix
@@
         if matches!(&self.state, SortMergeState::WaitingForSetup { .. }) {
@@
             let absorbed = self.absorb_events_into_setup(ctx);
             if !self.is_ready_to_merge() {
                 if absorbed > 0 {
                     return Ok(StepOutcome::Progress);
                 }
                 if !ctx.input.is_drained() {
                     return Ok(StepOutcome::NoProgress);
                 }
+
+                let SortMergeState::WaitingForSetup {
+                    slots,
+                    memory_chunks,
+                    expected_slot_count,
+                    expected_memory_chunk_count,
+                    ..
+                } = &self.state
+                else {
+                    unreachable!("state checked above");
+                };
+
+                let saw_payload = !slots.is_empty() || memory_chunks.total_len() > 0;
+                let saw_expectations =
+                    expected_slot_count.is_some() || expected_memory_chunk_count.is_some();
+                if saw_payload || saw_expectations {
+                    return Err(io::Error::other(format!(
+                        "SortMerge: setup incomplete at drain (slots={}, chunks={}, expected_slots={expected_slot_count:?}, expected_chunks={expected_memory_chunk_count:?})",
+                        slots.len(),
+                        memory_chunks.total_len()
+                    )));
+                }
             }
             self.transition_to_merging();
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fgumi-pipeline-io/src/sort/merge.rs` around lines 442 - 450, When
ctx.input is drained but is_ready_to_merge() returns false, the code currently
transitions to merging unconditionally, which allows incomplete setups to
proceed silently. Instead of calling transition_to_merging() at the end of this
block, first check that is_ready_to_merge() is true before transitioning; if the
setup is incomplete when input is drained, return an error to fail closed rather
than allowing a partial merge to proceed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-cli-common/src/lib.rs`:
- Around line 542-550: The CompressionOptions struct derives Default, which
initializes compression_level to 0 instead of the intended default of 1, causing
a mismatch with the CLI default_value_t. Remove Default from the derive macro on
CompressionOptions, then manually implement the Default trait for
CompressionOptions to explicitly set compression_level to 1. Additionally, add a
test that verifies CompressionOptions::default().compression_level equals 1 to
prevent future drift between the programmatic and CLI defaults.

---

Outside diff comments:
In `@crates/fgumi-pipeline-io/src/sort/merge.rs`:
- Around line 442-450: When ctx.input is drained but is_ready_to_merge() returns
false, the code currently transitions to merging unconditionally, which allows
incomplete setups to proceed silently. Instead of calling
transition_to_merging() at the end of this block, first check that
is_ready_to_merge() is true before transitioning; if the setup is incomplete
when input is drained, return an error to fail closed rather than allowing a
partial merge to proceed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cdf56c48-9191-41b2-8062-ecca0b39132e

📥 Commits

Reviewing files that changed from the base of the PR and between 97f3e47 and 6af43c3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (39)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-core/src/handles.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/builder.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-cli-common/src/lib.rs Outdated
…446)

Extract the `fgumi sort` command and its pipeline steps into three new
crates so sort-only downstreams (e.g. mako) can depend on sort without
compiling the UMI/consensus stack:

- fgumi-cli-common: shared CLI infrastructure (Command trait,
  OperationTimer, memory/compression options, validation, memory
  detection).
- fgumi-pipeline-io: generic BGZF source/sink steps (ReadBgzfBlocks,
  WriteBgzfFile) plus the typed-step sort steps (SortBamFile,
  SortAndSpill, SortMerge, SortSpillDecompress).
- fgumi-sort-cli: the `Sort` command, options, finalize hooks, and step
  factory. `Sort::execute` builds the typed-step pipeline directly via
  `fgumi_pipeline_core::Pipeline::builder` rather than the umbrella's
  monolithic `ChainBuilder`, keeping the dependency graph free of
  fgumi-consensus, fgumi-umi, and fgumi-simd-fastq.

The umbrella `fgumi` crate re-exports the moved items at their original
paths so runall, chains, merge, and main.rs compile unchanged; the
chains layer imports the sort step and factory from the new crates
one-way. Sorted output is byte-identical across coordinate, queryname,
queryname::natural, and template-coordinate orders (and stdin), so this
is a behavior-preserving extraction.
@nh13
nh13 force-pushed the 446/nh/feat-sort-cli branch from 6af43c3 to b2a13ae Compare June 21, 2026 21:09
@nh13
nh13 temporarily deployed to github-actions June 21, 2026 21:09 — with GitHub Actions Inactive
@nh13

nh13 commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fgumi-cli-common/src/lib.rs`:
- Around line 615-620: The test_parse_memory_size_errors function is missing
test coverage for scientific notation rejection, which is handled in the
parse_memory_size function around lines 298-304. Add an additional assert
statement within test_parse_memory_size_errors that calls parse_memory_size with
a scientific notation input (such as "1e9") and verifies that it returns an
error using is_err(), consistent with the existing test assertions for empty
strings, negative numbers, and zero.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 53e7d820-e997-4d29-813c-d81f0a93190c

📥 Commits

Reviewing files that changed from the base of the PR and between 6af43c3 and b2a13ae.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (39)
  • Cargo.toml
  • crates/fgumi-cli-common/Cargo.toml
  • crates/fgumi-cli-common/src/lib.rs
  • crates/fgumi-pipeline-core/src/handles.rs
  • crates/fgumi-pipeline-io/Cargo.toml
  • crates/fgumi-pipeline-io/src/lib.rs
  • crates/fgumi-pipeline-io/src/sink/mod.rs
  • crates/fgumi-pipeline-io/src/sink/write_bgzf.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill.rs
  • crates/fgumi-pipeline-io/src/sort/and_spill/tests.rs
  • crates/fgumi-pipeline-io/src/sort/merge.rs
  • crates/fgumi-pipeline-io/src/sort/merge/tests.rs
  • crates/fgumi-pipeline-io/src/sort/mod.rs
  • crates/fgumi-pipeline-io/src/sort/protocol.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress.rs
  • crates/fgumi-pipeline-io/src/sort/spill_decompress/tests.rs
  • crates/fgumi-pipeline-io/src/sort/tests.rs
  • crates/fgumi-pipeline-io/src/source/mod.rs
  • crates/fgumi-pipeline-io/src/source/read_bam.rs
  • crates/fgumi-pipeline-io/src/types.rs
  • crates/fgumi-sort-cli/Cargo.toml
  • crates/fgumi-sort-cli/src/chains.rs
  • crates/fgumi-sort-cli/src/lib.rs
  • crates/fgumi-sort-cli/src/sort.rs
  • crates/fgumi-sort-cli/src/version.rs
  • src/lib/commands/command.rs
  • src/lib/commands/common.rs
  • src/lib/commands/sort.rs
  • src/lib/pipeline/chains/builder.rs
  • src/lib/pipeline/chains/commands/sort.rs
  • src/lib/pipeline/steps/sink/write_bgzf.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/mod.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/source/read_bam.rs
  • src/lib/pipeline/steps/types.rs
  • src/main.rs
💤 Files with no reviewable changes (4)
  • src/lib/pipeline/steps/sort/merge/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress/tests.rs
  • src/lib/pipeline/steps/sort/and_spill/tests.rs
  • src/lib/pipeline/steps/sort/spill_decompress.rs

Comment thread crates/fgumi-cli-common/src/lib.rs
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