Status: Approved direction — 2026-05-26. Living document.
Context. Rosalind is a Rust genomics engine (alignment + germline/somatic variant calling) pitched for deterministic, bounded-memory, on-prem/edge genomics, built over a square-root-space evaluation framework (inspired by Williams 2025 Simulating Time With Square-Root Space and Cook–Mertz 2024 tree evaluation). A structured audit (2026-05) found genuine, well-built foundations but a large gap between positioning and implementation, plus several bugs that make outputs silently wrong on real data. The repo is gaining forks. This document defines the architecture we are rebuilding toward. Effort is not the constraint; the quality, uniqueness, and legibility of the engine for the people who build on it is the sole objective.
Rosalind is the bounded-memory, deterministic, streaming genomics kernel that edge builders compute on. Not another monolithic CLI that races GATK on accuracy, but the thing that exists where the ecosystem has a hole:
- a library-first engine whose core primitive — a streaming, CIGAR-aware pileup column stream — is a public, embeddable substrate;
- where memory is a contract you declare, not an outcome you hope for (decisive on edge devices with fixed RAM and no swap, where OOM means a dead run);
- where reproducibility is a verifiable artifact a third party can check without re-running;
- where confidence is calibrated and honest — the engine abstains rather than overcalls when evidence is thin (a confident wrong call is the worst outcome in the field, where you can't re-run or get a second opinion);
- over which anyone can compute arbitrary per-locus analytics — including on-device ML features — across a whole genome on a laptop, deterministically, within a fixed budget.
We win by being the trustworthy, embeddable, predictable kernel for genomics at the edge — for hospital laptops, portable sequencers (MinION in the field), classrooms, and researchers who want to build on a genomics engine rather than shell out to one. Variant calling is the first consumer of the kernel, not the whole product.
Sharpened core bet (2026-05-27). The differentiator is made precise: memory is a declared,
predictable, never-refusing, verifiable contract across the whole lifecycle, with the
square-root-space machinery (Williams 2025 / Cook–Mertz 2024; bound ~√t, up to lower-order factors) as the
continuous space/time knob a declared RAM budget selects — beachheaded on sublinear-space
index construction, the one stage where the framework genuinely bites. The research thesis, the
open problem (time-vs-space), and the intermediate-state-vs-input lens are in
docs/OPEN_PROBLEMS.md.
Non-goals (on purpose, so the promises are airtight): out-accuracy-ing GATK/DeepVariant on benchmarks; cloud/cluster-scale orchestration; supporting every exotic format.
These are the product. Today most are merely claimed; the rebuild makes each an enforced invariant.
| Promise | What it means | Enforcement |
|---|---|---|
| Deterministic | Byte-identical artifacts for identical inputs+config | Repeat-run byte-equality; shuffled-input → identical calls; (Phase D) --threads 1 vs N equality |
| Memory is a contract | You declare a budget; the engine honors it or refuses up front with a computed requirement. rosalind plan reports the working-set envelope before running |
Real-RSS gate while running the CLI on growing inputs: assert flat working set + that declared budgets are honored (replaces today's tautological counter). Honest caveat: index build is O(reference), documented separately |
| Standards-compliant | BAM/VCF the ecosystem accepts | samtools quickcheck / bcftools view / bcftools norm validate emitted artifacts in CI when available |
| Verifiable reproducibility | A content-addressed receipt (tool version + hashes of inputs, params, outputs) a third party can verify without re-running | JSON manifest emitted by every subcommand; hashed in the determinism test; a rosalind verify path re-checks a receipt |
| Embeddable / composable | Library-first; the pileup column stream is a public, documented, Python-exposed substrate for arbitrary (ML/QC/coverage) analytics; pipe-native CLI | Public-API test (no htslib types leak); a worked plugin + a Python streaming example; API-stability commitment on core types |
- Memory as a contract. Every streaming stage exposes a provable working-set bound and
accepts a
MemoryBudget; the engine adapts blocking to honor it or fails fast with the computed requirement.rosalind plananswers "will this run in 2 GB?" before you commit. - The pileup is an open substrate. Variant calling is a consumer of the
PileupColumnstream — coverage, QC, methylation, and ML feature extraction are equal citizens via the plugin/iterator API, all inheriting the memory + determinism guarantees. - Library-first, embeddable, pipe-native. Clean typed Rust API + a thin Python binding exposing the streaming primitive; the CLI is a thin client; subcommands compose over pipes without mandatory big intermediates (field/disk-scarce reality).
- Edge-data realism. Read-length-agnostic and robust to indel-rich long reads (Nanopore/PacBio — the actual edge-sequencing modality); transparent gzip/bgzf; honest handling of real references (N runs, IUPAC, many contigs).
- Honest, calibrated confidence over overcalling. Calls carry true Phred-scaled, well-calibrated confidence and an explicit FILTER vocabulary; below-evidence sites are flagged/abstained, never emitted as confident calls. (Mirrors the abstention-aware posture: don't pretend to predict where evidence is insufficient.)
- Verifiable reproducibility. A portable receipt makes "this artifact came from exactly these inputs, params, and tool version" checkable by anyone — the auditability story made real and unique.
The lingua franca is core; every other layer is built over it. Annotated KEEP /
REWRITE / NEW / SEPARATE.
core/ coordinate model + canonical records — shared by everything
locus Contig + Position; per-contig u32, global 64-bit-safe SA offset NEW
sequence 2-bit DNA + explicit IUPAC/N handling REWRITE (from compressed_dna)
record Read / AlignedRead: CIGAR, SAM flags, MAPQ, strand, tags REWRITE (from types.rs)
budget MemoryBudget + working-set model NEW
error typed library error taxonomy NEW
io/ the standards boundary (streaming, pipe-native)
fasta multi-contig, streaming REWRITE
fastq streaming, gzip/bgzf transparent decompression REWRITE
bam read/write, deterministic sort, .csi index KEEP sort + EXTEND
vcf header model + record model → one spec-valid emitter REWRITE
index/ FM-index/SA as a build-once → serialize → mmap-load artifact KEEP kernels + REWRITE persistence
align/ seed → chain → extend → alignment (real MAPQ, soft-clip, contigs) KEEP math + REWRITE (later phase)
pileup/ THE single streaming, CIGAR-aware, filtered, bounded engine REWRITE ← the kernel; public substrate
call/ pure scoring over columns: germline GL model; somatic LLR; (later) indels NEW germline + KEEP somatic (re-homed)
eval/ truth-set comparison (precision/recall, normalize, BED) KEEP (+ fix left-align)
provenance/ content-addressed run manifest + `verify` NEW (pulled early)
framework/ bounded map-reduce substrate (space + ledger + tree) KEEP — for PLUGINS, off the calling path
plugin/ GenomicPlugin extension surface over the column stream KEEP (elevated to a first-class substrate API)
bindings/py thin Python API exposing the streaming pileup primitive NEW (near phase)
theory/ Williams/Cook–Mertz √t Turing-machine simulation demo SEPARATE (feature `theory`, non-default)
cli (main) thin orchestration; typed errors → anyhow only here; pipe-native REWRITE (slim the 1645-line main.rs)
Public-API discipline (hard rule): no third-party types (e.g. rust_htslib::bam::Record)
appear in any public signature. Sources convert to owned core types at the boundary. core
types carry an API-stability commitment so external/Python/plugin consumers can rely on them.
Locus = (Contig, Position)— per-contigu32positions; the FM-index/SA over the concatenated genome uses a 64-bit-safe global offset (standard bwa/minimap2 model). Removes the single-contig blocker and the 4.29 Gbp ceiling structurally.AlignedRead(canonical) — contig-aware, CIGAR, SAM flags, MAPQ, strand, tags; SEQ stored forward-reference-oriented;end()CIGAR-derived. Read-length-agnostic (Illumina or long reads).PileupColumn— the public substrate type: one reference position with deterministically-ordered observations{allele, base_qual, strand, mapq}; size bounded by coverage. Designed to be consumed by callers, plugins, and (via the Python binding) ML pipelines; cheap aggregate views for featurization.MemoryBudget— declared cap; streaming stages reportworking_set_bound()and honor or refuse.Calltypes; typed library errors;anyhowonly at the CLI.
One engine: contig-aware, CIGAR-aware, read-filtered, strand-aware, deterministic, bounded,
budget-honoring, read-length-agnostic. Sources: sorted BAM now → IndexedReader::fetch
(Phase D) → in-memory sorted reads. Consumers: germline + somatic callers, plugins, Python.
Clean rewrite (not a generalization of htslib-coupled BamPileupStream). This is the component
we RSS-gate (promise #2), expose to Python (promise #5/embeddable), and let plugins extend
(tenet #2).
| Preserve (re-home/extend) | Rewrite (structurally wrong) | Separate / cut |
|---|---|---|
| SA-IS suffix array → 64-bit-safe | Pileup engine → one clean kernel | Theory layer → feature-gated theory/ |
| FM-index math → multi-contig, persistable | Germline caller → calibrated GL model | Unused deps ff, ark-poly → optional |
External merge BAM sort (sort.rs) |
VCF layer → header+record model | Tautological space counter → real RSS gate |
| Eval suite (+ fix left-align) | Core coordinate/record types (core/) |
Heuristic ledger.all_merges_complete() |
Somatic LLR → call/somatic |
Index persistence (serialize + mmap) | unimplemented!() stubs in util/ |
| Plugin trait (elevated to substrate API) | Read ingestion (stream, gzip, multi-contig) |
- Rebuild approach: evolve toward the target — rewrite the wrong layers; re-home/extend the correct tested kernels; each phase lands green; no throwaway of working code.
- Theory layer: KEEP and repurpose as load-bearing (revised 2026-05-27). The √t / Cook–Mertz
machinery (
algebra/ledger/machine/tree/space) becomes the knob that realizes the budget-tunable space/time curve — beachheaded on sublinear-space index construction (Phase D; seedocs/OPEN_PROBLEMS.md). It is not feature-gated away, andff/ark-polystay as core deps. (Supersedes the earlier "feature-gate thetheory/demo" decision.) - Guiding principle for all open design choices: maximize unique value to edge builders — the kernel/substrate, memory-as-contract, calibrated honesty, verifiable reproducibility, embeddability (§1, §3).
Reprioritized around the core contribution — memory as a declared, predictable, never-refusing, verifiable contract, beachheaded on sublinear-space index construction — and for the builders who need that capability first (edge / field / large-/meta-/pan-genome), ahead of generic performance. We commit to the optimal end-state rather than shippable intermediates. (The practical pillars B and C stand alone; D lands the space-complexity headline on top, de-risking the research bet.)
- Phase A — kernel + calling vertical + receipt. ✅ Done. Detailed in
2026-05-26-phase-a-unified-pileup-genotype-design.md. - Phase B — genome-scale kernel. Zero-copy persisted lean multi-contig index (build-once →
mmap), wired consumers, whole-genome pileup, pipe-native; lay the
MemoryBudget/ space-accounting hooks. (B1 streaming readers + B2 multi-contig FM-index ✅ done; B3 zero-copy persistence + B4 wiring remain.) Detailed in2026-05-27-phase-b-genome-scale-design.md. - Phase C — memory as a verifiable contract. Declared
MemoryBudgetacross every streaming stage + mmap-query;rosalind plan(envelope + space/time curve); honor-or-refuse + graceful degradation; real RSS gates in CI; deterministic (thread-count-invariant) execution + receipts +rosalind verify. - Phase D — sublinear-space index construction (beachhead). The √t-family knob: build the index under the declared budget across the full curve (in-RAM-fast → checkpointed ~√n → external-memory spill), time overhead characterized; theory layer load-bearing; erases the O(reference) build-RAM caveat. The headline space-complexity contribution.
- Phase E — reach + substrate. Fast deterministic-parallel aligner + germline indels + richer read QC; the Python/tensor pileup-feature substrate + pluggable models; rigorous eval + calibration validation + the open-problems benchmark. (Deterministic-parallel execution is part of C's contract; aligner-algorithm quality + micro-optimization are E.)
The open research problem and the intermediate-state-vs-input lens that order these phases are in
docs/OPEN_PROBLEMS.md.
Urgent correctness (A): reverse-strand double-complement (pileup_stream.rs:33-42,
somatic/model.rs:325-327); CIGAR-ignoring/read-dropping pileup (pileup.rs,
pileup_stream.rs:136, main.rs parse_cigar bail); discarded-posterior "caller"
(statistics.rs:70); hardcoded MAPQ (bwt_aligner.rs:206); non-conformant VCF (vcf.rs:6);
empty-position recursion (pileup_stream.rs:192). Foundational (B): single-contig
(main.rs:881); index rebuilt every run / zero SA samples persisted (index/io.rs:140);
O(reference) build RAM. Moat (D): tautological space counter (space/allocator.rs),
rss_budget caps 50 KB at 2 GB. Trust (E): vestigial theory layer; no clippy; Linux-only CI;
empty bench; Python = RNA-seq demo only.