Phase B4: bounded whole-genome variants --index over the persisted index - #19
Merged
Conversation
…persisted index Reprioritized ahead of the aligner (builder value: BYO-aligner -> BAM; Rosalind's edge is the reproducible/BOUNDED/abstention-aware caller). variants --index calls germline variants across all contigs in BOUNDED memory: a StreamingBamSource (record-at-a-time, no full-BAM Vec) + coordinate-sort monotonicity guard; per-contig reference via ReferenceView (B4a); reuses call_germline_region + write_germline_vcf (multi-contig ##contig) + manifest. Surfaces the memory contract: realized peak RSS + engine working-set in the receipt + record-only --memory-budget-mb (no enforcement = Phase C). Sub-stages: (1) streaming sorted source, (2) bounded multi-contig drive + variants --index + multi-contig VCF, (3) memory receipt. Deferred: aligner, somatic --index, on-demand ref_base, SAM streaming, enforcement. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…persisted index 4 tasks: (1) StreamingBamSource (record-at-a-time + (contig_id,pos) sort guard; shared record mapping), (2) call_germline_region_tracked + call_germline_whole_genome drive (per-contig PerContig adapter + ReferenceView decode; reuses the per-contig caller), (3) variants --index CLI (BAM-only, XOR --reference, multi-contig VCF + manifest), (4) memory receipt (peak RSS + max working set) + record-only --memory-budget-mb. CLI tests via index->align->sort->variants (no rust-htslib dev-dep). Derived from the committed spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Streams one record at a time via bam::Reader::read (no full-file Vec), sharing the Record->AlignedRead mapping (extracted as record_to_aligned_read) with read_bam_as_core_reads. A (contig_id,pos) monotonicity guard rejects unsorted / index-order-mismatched BAMs. The bounded-reads foundation for whole-genome variants --index. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ ReferenceView call_germline_region_tracked surfaces the max pileup working set (call_germline_region delegates, signature unchanged). call_germline_whole_genome makes a single sorted pass, partitions per contig (PerContig adapter), decodes each contig's reference from ReferenceView, and reuses the per-contig caller — peak ≈ largest contig + working set, independent of input size. Verified == the per-contig union. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ersisted index variants takes --index XOR --reference (exactly one). --index loads the index (ContigSet + ReferenceView), streams a sorted BAM (StreamingBamSource), runs call_germline_whole_genome, and writes a multi-contig VCF + manifest (inputs = .idx + BAM). --chrom/--region-start error under --index; SAM under --index errors with guidance. Parity with --reference on a single contig; self-contained (no FASTA). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure formatting (1-line -> 4-line assert! reflow) so src/call/whole_genome.rs is fmt-clean; no semantic change. Folded out of the Task 3 commit, which kept to its two named files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…get-mb Surfaces the bounded-memory contract: the run reports realized peak RSS + the engine's max pileup working set (to stderr and the manifest params). --memory-budget-mb flags overage against the realized peak but never aborts (enforcement is Phase C). Operationalizes Rosalind's differentiator — predictable, verifiable memory — on the flagship whole-genome workload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wires Rosalind's germline caller onto the persisted multi-contig index, delivering the flagship capability:
This operationalizes Rosalind's core differentiator — memory as a declared, predictable, verifiable contract — on the workload builders actually run (align elsewhere, arrive with a sorted BAM, call with Rosalind). No FM-index/aligner is needed on this path: only the reference (self-contained from the
.idxvia B4a) + the streaming pileup + the abstention-aware caller shipped in Phase A.Spec:
docs/superpowers/specs/2026-05-27-phase-b4-variants-index-design.md· Plan:docs/superpowers/plans/2026-05-27-phase-b4-variants-index.md.What's in it (by commit)
StreamingBamSource(src/io/bam.rs) — a boundedReadSourcethat pulls one BAM record at a time viabam::Reader::read(never materializes the file), sharing theRecord → AlignedReadmapping (record_to_aligned_read) withread_bam_as_core_reads. A(contig_id, pos)monotonicity guard rejects unsorted /@SQ-order-mismatched input (accepts equal positions for depth; rejects strict descent).call_germline_region_tracked+call_germline_whole_genome(src/call/) — the per-contig caller now also surfaces the engine's max pileup working set (call_germline_regiondelegates, signature unchanged → existing call sites untouched). The whole-genome drive makes a single sorted pass, partitions it per contig with a peekingPerContigadapter, decodes each contig's reference from the B4aReferenceView, and reuses the tested per-contig caller. Peak ≈ largest contig's reference + the pileup working set — independent of BAM size.variants --index(src/main.rs) —--indexXOR--reference(exactly one).--indexloads the index (ContigSet+ReferenceView), streams the sorted BAM, runs the drive, and writes a multi-contig VCF (one##contigper contig) + manifest.--chrom/--region-starterror under--index; SAM under--indexerrors with guidance (the legacy SAM reader is single-contig).--memory-budget-mb— the run reports realized peak RSS + the engine's max pileup working set (stderr + manifest params).--memory-budget-mbflags overage against the realized peak but never aborts (enforcement is Phase C). Makes the bounded contract visible + verifiable, not just claimed.Test plan / gates (spec §8 — all verified)
variants --indexstreams the BAM (no full-fileVec); structurally usesStreamingBamSource, neverread_bam_as_core_reads/BamSource..idxalone — integration test deletes the source FASTA and still calls.--indexrecords ==variants --reference <same fa>on the same sorted BAM.call::whole_genomelib test asserts the drive == the per-contig union; multi-contig##contigheaders..idx+BAM BLAKE3 + realized peak RSS + max working set;--memory-budget-mb 0flagsEXCEEDEDbut the run completes (record-only).Full suite green: 154 lib + all integration binaries, 0 failed; zero warnings on the feature surface. Final whole-branch review verdict: READY TO MERGE (no Critical/Important findings).
Known follow-ups (out of B4's scope by design)
record_to_aligned_readdoes a per-record linearContigSet::by_nameon the streaming path →O(reads × contigs). Correct, and a pre-existing pattern (the materializing reader does the same); B4 scoped perf out ("deliver the capability, perf follows"). Clean fix: a one-timetid → contig_idtable inStreamingBamSource.--region-start 0isn't distinguishable from the default under--index(harmless); twoclippy::type_complexitywarnings (project doesn't gate clippy).align --index/somatic --index) → Phase E; SAM streaming; on-demandref_base(O(window) reference); budget enforcement +rosalind plan/verify→ Phase C.🤖 Generated with Claude Code