A computational framework for CRISPR guide-RNA design and population-aware off-target analysis. Input a sequence variant; receive a ranked, uncertainty-annotated set of candidate guide designs across SpCas9, base-editor, and prime-editor chemistries, each with a population- and haplotype-aware off-target profile. AlleleForge is an analysis and design tool that wraps existing published open-source software behind one typed interface.
Research and educational use only. AlleleForge is computational. It produces ranked, explicitly uncertain predictions for research and method development. It contains no wet-lab protocols and no synthesis instructions. Off-target nominations are computational and must be experimentally validated. It is not a medical device and provides no medical advice.
This is the authoritative build specification for AlleleForge, written to be executed step by step by a capable engineering agent. Each phase lists:
- Context — why the phase exists and how it fits the whole.
- Deliverables — the concrete files, modules, and tests that must exist when the phase is "done."
- Defaults & decisions — every open choice is pre-filled with a default and an override path.
Build the phases in order. Phases 0–5 establish the spine (data model, genome access, off-target engine, scoring interfaces) before any chemistry-specific or ML code. Do not begin Phase 6 (machine learning) until Phases 1–5 have green CI.
Guiding principle: wrap, don't rebuild. Where excellent open-source tools exist (PRIDICT2.0, BE-Hive, BE-DICT, inDelphi, CRISPRitz, Cas-OFFinder), AlleleForge integrates them behind a unified, typed interface and adds value at the seams: a single variant-first UX, calibrated uncertainty, population-aware off-target analysis for every chemistry, and a reproducible benchmark. New models are trained only where public coverage is genuinely missing.
A phase is "done" only when its deliverables exist, ruff and mypy --strict pass on the new code, and its
tests are green in CI.
| Field | Value |
|---|---|
| Name | AlleleForge |
| Python package | alleleforge |
| CLI command | aforge |
| Tagline | "Variant in, ranked design out." |
| Repository | github.com/clay-good/alleleforge |
| License | MIT (all code, schemas, benchmark, and any first-party model weights) |
| Primary language | Python ≥ 3.11 |
| Performance extensions | Rust via PyO3 / maturin |
| Sister deliverable | crispr-bench (CRISPR-Bench) — benchmark, splits, leaderboard |
| Versioning | SemVer 2.0; 0.x until the three launch chemistries pass acceptance |
| Code of conduct | Contributor Covenant 2.1 |
The project is fully open source under a single permissive MIT license to maximize reuse, redistribution, and academic adoption. Third-party tools and models that AlleleForge wraps retain their own upstream licenses; the registry records each one and refuses to bundle any component whose license is incompatible with redistribution, fetching it at runtime with the user's consent instead.
The CLI name aforge is short and, at time of writing, unclaimed on PyPI/conda — verify availability as the
first action of Phase 1 and fall back to alleleforge as the command if aforge is taken.
Give any researcher — from a bench scientist with a ClinVar accession to an ML engineer building a pipeline — a single, reproducible, well-typed path from a sequence variant to a ranked, uncertainty-annotated set of candidate guide designs, with a population-aware off-target profile for each.
- Input: a variant as a ClinVar accession, dbSNP rsID, HGVS (genomic/coding/protein), VCF record, raw genomic coordinates, or a raw target sequence.
- Three launch chemistries:
- SpCas9 nuclease guide design (and HDR-template suggestion where applicable).
- Base editing (ABE and CBE families).
- Prime editing (pegRNA design: PBS, RTT, nicking guide, epegRNA motifs).
- On-target efficiency prediction with calibrated uncertainty for each chemistry.
- Outcome prediction: indel spectra (nuclease), bystander distribution (base editing), intended-vs- byproduct distribution (prime editing).
- Off-target nomination that is reference-genome, population-aware, and haplotype-aware (gnomAD / 1000G / HGDP), and optionally personalized from a supplied VCF — for all three chemistries.
- Ranked candidate selection across chemistries, recommending the most promising design for a variant.
- Outputs: machine-readable (JSON/TSV/Parquet), cloning-ready oligo sequences, and an HTML/PDF report.
- Three interfaces: importable Python library,
aforgeCLI, and a web UI. - CRISPR-Bench: a versioned benchmark with frozen splits and a public leaderboard.
- Cas13 / RNA targeting — v0.2.
- CRISPRa / CRISPRi / epigenome editing — v0.2.
- Cas12a/Cpf1 as a first-class design chemistry — v0.3 (off-target search supports its PAM from day one).
- Wet-lab automation, oligo-ordering vendor APIs, LIMS integration — explicitly deferred; v1.0 is computational only.
- Library-scale tiling-screen design — v0.3.
- Any clinical decision-making authority. AlleleForge is a research tool; it produces hypotheses and rankings, not medical advice. This is stated in the README, the docs, and every generated report.
- AlleleForge will not be a closed SaaS, will not gate features behind payment, and will not transmit user sequences anywhere. All computation runs locally or on user-controlled infrastructure.
- AlleleForge will not silently substitute predictions for experimental validation. Every report states that off-target nomination is computational and must be experimentally confirmed.
- Variant-first. The canonical user journey starts from a variant, not a guide. Chemistry selection, guide enumeration, and scoring are all downstream of "what is the variant and what design addresses it."
- Honest uncertainty. Every numeric prediction ships with a calibrated interval. A confident wrong answer is worse than a wide honest one. No scorer returns a bare float.
- Population-aware by default. Reference-genome-only off-target analysis is a known blind spot: a minor allele can create a de novo PAM that a reference-only scan misses. AlleleForge searches population variation by default and stratifies results by ancestry.
- Wrap, don't rebuild. Integrate the best existing tools behind one typed interface; add new ML only at genuine coverage gaps.
- Reproducible to the byte. Pinned environments, versioned datasets (DVC), deterministic seeds, and content-hashed model checkpoints. A result must be re-derivable from its provenance block.
- Three audiences, one core. The library is the source of truth; CLI and web are thin shells. No business logic lives in the CLI or web layers.
- Typed and tested.
mypy --strict,ruff, and property-based tests on all core logic. Sequence biology is full of edge cases (strandedness, ambiguity codes, indels at boundaries); the type system and Hypothesis catch them. - Cite everything. Every dataset, model, and scoring function carries a literature citation and a version, in code and in output provenance.
AlleleForge is layered. Lower layers know nothing about higher ones.
┌───────────────────────────────────────────────────────────────┐
│ Interfaces │
│ ┌────────────┐ ┌────────────┐ ┌───────────────────────┐ │
│ │ Python │ │ aforge │ │ Web UI (FastAPI + │ │
│ │ library │ │ CLI │ │ React/Next.js) │ │
│ └─────┬──────┘ └─────┬──────┘ └───────────┬───────────┘ │
│ └────────────────┴──────────────────────┘ │
├─────────────────────────┼───────────────────────────────────────┤
│ Orchestration │
│ ┌──────────────────────▼──────────────────────────────────┐ │
│ │ Designer: variant → chemistry routing → candidate menu │ │
│ │ → scoring → outcome → off-target → ranking → report │ │
│ └──────────────────────┬──────────────────────────────────┘ │
├─────────────────────────┼───────────────────────────────────────┤
│ Domain services │
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ Variant │ │ Guide │ │ Scoring │ │ Off- │ │
│ │ resolver │ │ enumerators │ │ (efficiency, │ │ target │ │
│ │ (HGVS, │ │ (cas9, base, │ │ outcome, │ │ engine │ │
│ │ ClinVar) │ │ prime) │ │ uncertainty) │ │ (pop/hap)│ │
│ └─────┬─────┘ └──────┬───────┘ └──────┬───────┘ └────┬─────┘ │
│ └──────────────┴────────────────┴──────────────┘ │
├─────────────────────────┼───────────────────────────────────────┤
│ Foundations │
│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ Genome │ │ Data │ │ Model │ │ Core │ │
│ │ access │ │ registry │ │ registry / │ │ types & │ │
│ │ (FASTA, │ │ (DVC, gnomAD,│ │ zoo (ckpt │ │ schemas │ │
│ │ index) │ │ ClinVar...) │ │ hashing) │ │ │ │
│ └───────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└───────────────────────────────────────────────────────────────┘
Rust extensions (PyO3): FM-index off-target search, k-mer
hashing, haplotype walking — called from the off-target
engine and the enumerators.
The Designer is the only component that sees the full pipeline. Each domain service is independently testable and usable. The foundations are pure infrastructure with no CRISPR-specific logic except the core types.
alleleforge/
├── pyproject.toml # build config, deps, tool config (ruff, mypy, pytest)
├── README.md
├── LICENSE # MIT
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CHANGELOG.md
├── environment.yml # conda environment (lockable via conda-lock)
├── dvc.yaml / .dvc/ # data versioning
├── Dockerfile # multi-stage, multi-arch (amd64 + arm64)
├── docker-compose.yml # web UI + API
├── .github/workflows/ # CI: lint, type, test, build, docs, benchmark
├── docs/ # mkdocs-material site
├── rust/ # PyO3 crate: aforge_native
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── bwt.rs # FM-index search
│ ├── kmer.rs # k-mer hashing
│ └── haplotype.rs # haplotype walking
├── src/alleleforge/
│ ├── __init__.py
│ ├── _version.py
│ ├── config.py # global config, defaults, paths
│ ├── types/ # Phase 1: core domain types & schemas
│ │ ├── sequence.py # Strand, DNASequence, GenomicInterval, ambiguity codes
│ │ ├── variant.py # Variant, HGVS, VCF record models
│ │ ├── guide.py # Spacer, PAM, Guide, PegRNA, BaseEditWindow
│ │ ├── edit.py # EditOutcome, Chemistry, EditStrategy, EditIntent
│ │ ├── offtarget.py # OffTargetSite, OffTargetReport
│ │ ├── prediction.py # Prediction[T] with uncertainty
│ │ ├── candidate.py # DesignCandidate, RankedMenu
│ │ └── provenance.py # Provenance, ToolVersion, DatasetVersion
│ ├── genome/ # Phase 2: reference access, FM-index, coordinates
│ ├── data/ # Phase 3: registry, clinvar, gnomad, 1000G, hgdp, dbsnp, annotations
│ ├── variant/ # Phase 4: resolver, hgvs adapter, effect (VEP)
│ ├── offtarget/ # Phase 5: engine, scoring, population, haplotype, cas-offinder adapter
│ ├── enumerate/ # Phases 7-9: cas9, base_editor, prime
│ ├── scoring/ # Phases 6-9: base, uncertainty, backbone, per-chemistry scorers
│ ├── model_zoo/ # Phase 6: registry, cards/
│ ├── design/ # Phase 10: designer, routing, ranking
│ ├── report/ # Phase 11: builder, pdf, html, oligos
│ ├── cli/ # Phase 12: Typer app
│ └── web/ # Phase 13: api/ (FastAPI) + frontend/ (Next.js)
├── tests/ # mirrors src/ ; pytest + hypothesis
├── benchmark/ # CRISPR-Bench (own sub-package)
│ └── crispr_bench/ # datasets/, splits/, tasks.py, metrics.py, runner.py, leaderboard.py
└── examples/
├── 01_clinvar_to_design.ipynb # the canonical journey
├── 02_population_offtarget.ipynb # reference-bias off-target validation case
└── 03_batch_vcf.ipynb
These hold across all phases unless a phase overrides them explicitly.
Reference & coordinates. Default reference GRCh38/hg38. T2T-CHM13 v2.0 is a first-class optional reference, auto-recommended when a target lies in a segmentally duplicated, centromeric, or otherwise hg38-ambiguous region (the genome layer flags these). mm39 ships for mouse; other Ensembl species download lazily. All internal coordinates are 0-based, half-open ("BED-style"); convert to 1-based only at I/O boundaries that demand it (HGVS, VCF, human-readable reports), and label every coordinate with its system.
Strand. Always explicit (Strand.PLUS / Strand.MINUS); no "default strand." Spacers are stored 5'→3'
on their own strand with the genomic strand recorded.
PAM defaults. SpCas9 = NGG (primary), with NAG reported as a low-stringency off-target PAM. NG
(SpCas9-NG) and NRN/NYN (SpRY) are opt-in, auto-suggested only when no NGG exists within ±30 bp of the
target base. Cas12a = TTTV (off-target search support).
Off-target search defaults. Mismatches ≤ 4; DNA bulges ≤ 1; RNA bulges ≤ 1. Report any site with CFD ≥ 0.20 or MIT ≥ 0.10. Population search includes variants with MAF ≥ 0.001 in any queried population by default. De-novo PAM creation and seed-region mismatch changes from population variants are always evaluated.
Base-editing defaults. Default editing window = protospacer positions 4–8 (1-based, PAM-distal end = position 1), configurable per editor. Default editors: ABE8e (A•T→G•C) and CBE4max/evoCDA1 (C•G→T•A). Bystander editing is always predicted and reported.
Prime-editing defaults. Default architecture = PE5max + epegRNA (tevopreQ1 3' motif); add a PE3b nicking guide when a seed-disrupting ngRNA exists, else PE3. Default search ranges: PBS 8–17 nt, RTT 7–34 nt (extended to cover the edit + ≥ 5 nt homology 3' of the edit). MMR context is a model input where available.
Uncertainty contract. Every efficiency/outcome prediction returns a Prediction[T] carrying a point
estimate, a calibrated interval (default 80% predictive interval), the method used, and an in-/out-of-
distribution flag. No scorer may return a bare float.
Randomness. Global seed default 20240501, threaded through every stochastic step; recorded in provenance.
Provenance. Every top-level result embeds a provenance block: AlleleForge version, all tool/model versions and checkpoint hashes, all dataset versions, reference build, config snapshot, seed, and UTC timestamp.
Licensing of wrapped components. Each wrapped tool/model records its upstream license in its card; the registry refuses to bundle a component whose license is incompatible with redistribution and fetches it at runtime with the user's consent.
Build in order. Each phase is self-contained.
Context. Before any sequence logic, establish a repository that is reproducible, typed, linted, and CI-gated from commit one.
Deliverables.
pyproject.tomlusing hatchling, declaring thealleleforgepackage and theaforgeconsole script.- Dependency groups:
core,genome,variant,ml,web,docs,dev. ruffconfig (line length 100; rules E,F,I,UP,B,D; pydocstyle on public API),mypyconfig (strict = true),pytestconfig (coverage gate ≥ 85% on core).environment.ymlfor the scientific stack (pysam, pyfaidx, cyvcf2, mappy), lockable via conda-lock.- Rust workspace
rust/with amaturin-built PyO3 crateaforge_nativeexposingversion()to prove the toolchain end to end. A test asserts the native version matches the Python package version. .github/workflows/ci.yml: matrix over Python 3.11/3.12 on Linux + macOS; jobs for lint, type-check, test, Rust build, and a docs build. Multi-arch Docker build on tags.Dockerfile(multi-stage),docker-compose.ymlstub.README.mdwith the mission, the "research tool, not medical advice" disclaimer, and a quickstart.LICENSE(MIT),CITATION.cff,CODE_OF_CONDUCT.md(Contributor Covenant 2.1),CONTRIBUTING.md,CHANGELOG.md(Keep a Changelog format).src/alleleforge/_version.py(single-source versioning);config.pydefining default paths (XDG-compliant cache dir), the global seed (20240501), and a typedSettings(pydantic-settings) overridable by env vars and~/.config/alleleforge/config.toml;__init__.pyre-exporting__version__andSettings.
Defaults & decisions. Build backend = hatchling. Config = pydantic-settings. CLI framework (declared now,
used in Phase 12) = Typer. Docs = mkdocs-material. Test = pytest + Hypothesis. Rust binding = PyO3 +
maturin. Cache root = $XDG_CACHE_HOME/alleleforge (fallback ~/.cache/alleleforge). No CRISPR logic yet.
Ensure ruff check, mypy --strict src, pytest, and the Rust build all pass locally and in CI. Commit in
logical chunks with conventional-commit messages.
Context. The vocabulary the whole system speaks. Get strandedness, coordinate systems, ambiguity codes, and the uncertainty contract right here, once.
Deliverables. Everything under src/alleleforge/types/:
sequence.py:Strandenum;CoordinateSystemenum;DNASequence(validates ACGT + IUPAC ambiguity codes, knows its alphabet, ambiguity-aware reverse-complement, slicing that preserves coordinate metadata);GenomicInterval(chrom, start, end, strand, 0-based half-open, explicit coordinate system).variant.py:Variant(normalized: chrom, pos, ref, alt, build, optional HGVS strings and source IDs);VariantClass(SNV, MNV, insertion, deletion, indel, complex); typed wrappers for ClinVar accession and dbSNP rsID.guide.py:PAM(IUPAC pattern),Spacer,Guide(spacer + PAM + placement + cut site),BaseEditWindow,PegRNA(spacer, scaffold, RTT, PBS, optional 3' motif, optional nicking guide),NickingGuide.edit.py:Chemistryenum (CAS9_NUCLEASE, BASE_ABE, BASE_CBE, PRIME);EditOutcome(distribution over resulting alleles with probabilities);EditStrategy(binds a Variant to a Chemistry + reagent(s));EditIntent(correct, knock-out, install, revert).offtarget.py:OffTargetSite(locus, mismatches, bulges, score, score method, the population/haplotype context that produced it, ancestry annotation);OffTargetReport(aggregate + per-site list + summary stats + ancestry stratification).prediction.py: the uncertainty contract — genericPrediction[T]withvalue: T,interval: tuple[float, float],interval_level: float = 0.80,method: UncertaintyMethod,in_distribution: bool,calibrated: bool, plus a helper to combine independent predictions.candidate.py:DesignCandidate(one chemistry + reagent(s) + efficiencyPrediction+ outcomePrediction+OffTargetReport+ flags);RankedMenu(ordered candidates + ranking rationale + provenance).provenance.py:ToolVersion,DatasetVersion,ModelCheckpoint(content hash),Provenance.
Defaults & decisions. pydantic v2 for everything serializable (validation + JSON Schema for free);
frozen dataclasses only for tight inner-loop value objects that never cross an I/O boundary. Coordinates
0-based half-open internally; add a to_one_based() boundary helper, never used internally. Reverse-
complement must be ambiguity-aware (R↔Y, W↔W, N↔N). All enums are str-valued for clean JSON. Prediction
default interval level = 0.80; forbid bare-float results by making scorers return Prediction. PegRNA
validates that RTT covers the edit plus required 3' homology and that PBS length is within range. Emit JSON
Schema for every public model into docs/schemas/ via a small script wired into the docs build. Keep this
module free of I/O, genome access, and model code.
Tests. Exhaustive unit plus Hypothesis property tests: reverse-complement is an involution; ambiguity-
aware RC round-trips; coordinate conversions round-trip; Variant normalization is idempotent; Prediction
intervals always contain the point estimate and respect the level. Target ≥ 95% coverage on types/.
Context. Nearly everything needs fast, correct access to reference sequence and a searchable index.
Deliverables. src/alleleforge/genome/:
reference.py:ReferenceGenomewrappingpyfaidxfor random access; a registry of built-in builds (hg38, T2T-CHM13 v2, mm39) and lazy, checksum-verified download of additional Ensembl species into the cache;fetch(interval) -> DNASequencethat is strand-aware, bounds-checked, and N-pads contig ends while flagging the result. Never auto-download without the caller's consent flag.index.py: build/load an FM-index (delegating to the Rustaforge_nativemodule) for PAM-anchored candidate search; content-addressed and cached on disk; memory-mapped by default with an opt-inin_memory=True. Document the expected on-disk size for hg38 and warn before building it.coordinates.py: liftover between builds (chain files) andflag_ambiguous_regions()that detects segmental-duplication / centromeric / hg38-difficult loci and recommends T2T-CHM13. Wire the recommendation into the Phase 1 result types.
Defaults & decisions. FASTA via pyfaidx. Index memory-mapped on disk by default. Downloads verified by
checksum and recorded as DatasetVersion. T2T auto-recommendation on by default. Contig-end policy: pad with
N and flag, never crash.
Tests. Strand-aware fetch against known loci using tiny bundled synthetic FASTAs (never full genomes in CI); FM-index round-trip; liftover round-trips; ambiguous-region flagging on a curated fixture. Mock all network downloads. Add a non-gating pytest-benchmark for index query latency.
Context. Population- and variant-aware analysis requires disciplined, versioned access to ClinVar, gnomAD, 1000G, HGDP, dbSNP, GENCODE, and ENCODE tracks.
Deliverables. src/alleleforge/data/:
registry.py: aDatasetRegistryon DVC; each dataset is a typed descriptor withsource_url,license,citation,version,sha256, andredistributable: bool; access returns data plus aDatasetVersion. If not redistributable, never vendor it — fetch to the user's cache at runtime with explicit consent.clinvar.py: parse the monthly ClinVar release into normalizedVariants;get(accession),by_rsid(rsid),by_gene(symbol),in_region(interval); carry clinical significance and review status.gnomad.py:frequencies(interval, populations=...)against gnomAD v4.1, caching tabix slices.thousand_genomes.py,hgdp.py: phased haplotypes and per-population MAF;common_haplotypes(interval, min_freq)returning enumerable haplotype sequences for Phase 5.dbsnp.py: rsID↔locus via tabix (build 156+).annotations.py: GENCODE v47 gene models and ENCODE track access (DNase/ATAC/CTCF/H3K27ac) as per-locus signal lookups for later chromatin-aware scoring.
Defaults & decisions. DVC with a user-configured remote (default local cache; optional S3/GCS). gnomAD default = v4.1. 1000G = phase 3 high-coverage. ClinVar = latest monthly snapshot, pinned per run. Default population MAF threshold = 0.001. ClinVar/gnomAD/1000G/HGDP/GENCODE are open; the registry blocks any non-redistributable source from being vendored.
Tests. Parse tiny synthetic fixtures (never real multi-GB files in CI); verify normalization, frequency
lookups, haplotype enumeration, and that a non-redistributable dataset is never written into the repo or
image. Mock all network I/O. Document every dataset's version/license/citation in docs/data.md.
Context. The front door of the variant-first journey. Any accepted input must normalize to a single
canonical Variant with its genomic placement, coding/protein consequence, and working window.
Deliverables. src/alleleforge/variant/:
resolver.py:resolve(input, *, build="hg38", window=100, transcript="MANE_SELECT") -> ResolvedVariantaccepting ClinVar accession, dbSNP rsID, HGVS (g./c./p.), VCF record, raw coordinates, or a raw sequence with a marked target position. Left-aligns and trims indels, validates the asserted ref against the reference (hard error on mismatch — likely wrong build), and returns the canonicalVariantplus a workingGenomicInterval(default ±100 bp, configurable).hgvs_adapter.py: wrap thehgvslibrary for parsing/validation and projection between c./g./p. using MANE Select (fallback RefSeq canonical). Handle intronic offsets, UTR coordinates, and protein-level inputs that map to multiple codons (return all, flagged).effect.py: VEP integration (REST default, local optional) returning a structured consequence (missense/nonsense/splice/frameshift/…) used by the Phase 10 router. Cache responses by variant + transcript set.
Defaults & decisions. Transcript default = MANE Select. Indels left-aligned and parsimonious (bcftools-norm semantics). Default window ±100 bp. VEP via REST by default.
Tests. Round-trip a curated set of variants (SNV, splice, small indel) from each input form to the same
canonical Variant; left-alignment on tricky repeat-region indels; reference-mismatch detection; mock VEP
and hgvs network calls. Property test: resolution is idempotent and input-form-invariant.
Context. AlleleForge's safety core and a primary point of novelty: population- and haplotype-aware off-target nomination for every chemistry. Build it before the chemistries so each plugs into one engine.
Deliverables. src/alleleforge/offtarget/:
engine.py:search(spacer, pam, *, reference, mismatches=4, dna_bulges=1, rna_bulges=1, populations=ALL, maf=0.001, patient_vcf=None) -> OffTargetReport. Five stages: (1) reference candidate search via the Rust FM-index; (2) population augmentation — inject gnomAD/1000G/HGDP variants to find de novo PAMs and altered-mismatch sites; (3) haplotype-aware evaluation walking common haplotypes (Rusthaplotype.rs); (4) optional patient VCF pass; (5) scoring and aggregation. Returns anOffTargetReportthat is ancestry-stratified by default. Every threshold is a parameter.scoring.py: CFD (with the published Doench mismatch/PAM weight tables, cited in code), the MIT specificity score, and a Cas12a CFD analog; aScorerprotocol so Phase 6 ML scorers can be swapped in.population.py: takes a window's variants and enumerates the off-target sites they create or modify, each annotated with the causal allele, the populations carrying it, and the frequency. Reproduce the published reference-bias validation case (BCL11A enhancer /rs114518452): a minor allele creating a de-novo NGG PAM that yields a high-CFD off-target, reported with ancestry-stratified frequency. Ship as an integration test, citing Cancellieri & Pinello, Nat Genet 2023.haplotype.py: Python orchestration over the Rust haplotype walker.cas_offinder_adapter.py: optional cross-check against Cas-OFFinder when installed; flag disagreements.
Defaults & decisions. Defaults as in §6 (≤4 mismatch, ≤1 DNA + ≤1 RNA bulge, CFD≥0.20 or MIT≥0.10, MAF≥0.001, all populations). Native Rust engine is primary; Cas-OFFinder is the cross-check. Every site records whether it exists in the reference, arises from a population variant (which allele, which populations, frequency), or arises from the patient's VCF. Reports are ancestry-stratified by default.
Tests. Exact and tolerant search correctness on synthetic genomes; CFD/MIT against published worked examples; population augmentation finds de-novo PAMs in fixtures; haplotype walking enumerates expected haplotypes; patient-VCF personalization; the reference-bias integration test passes. Non-gating genome-wide search benchmark on a synthetic reference.
Context. The reusable ML substrate before any chemistry-specific predictor.
Deliverables.
model_zoo/registry.py: register, download, verify (content hash), and load checkpoints; each has a required model card (cards/*.yaml: name, version, chemistry, training_data, metrics, intended_use, out_of_scope_use, license, citation, known_failure_modes, checkpoint_sha256). Loading fails loudly if the card is missing or the license forbids the use. Surface every checkpoint as aModelCheckpoint.scoring/backbone.py: aSequenceEmbedderprotocol withembed(sequences) -> Tensor,context_window,name,version. Default adapter = Nucleotide Transformer v2 (500M); Caduceus and Evo 2 adapters behind the same protocol (interface mandatory, full impl optional). Cache embeddings by sequence hash.scoring/uncertainty.py:DeepEnsemble(N=5 default), an evidential-regression head, quantile heads, and isotonic post-hoc calibration withexpected_calibration_error(...);to_prediction(...)packaging outputs into the Phase 1Prediction(80% interval, method tag,calibratedflag, and anin_distributionflag from embedding-space density vs. a stored training reference).scoring/base.py: theScorerprotocol returningPredictionand exposing its model card; a guard (test + runtime assert) that noScorerreturns a bare float.
Defaults & decisions. Default backbone = NT v2 500M. Uncertainty default = deep ensemble (N=5) with
isotonic calibration; evidential is the single-model fallback. Default interval = 80%. OOD via embedding-space
density. Inference supports torch.compile and optional ONNX export.
Tests. Registry rejects missing/forbidden-license cards and corrupted checkpoints; embedder produces stable cached embeddings on a tiny stub model (no real 500M weights in CI); ensemble intervals contain the mean and widen on held-out OOD inputs; calibration reduces ECE on a synthetic miscalibrated fixture; OOD flag fires on far-from-training embeddings. Gate real-weight tests behind an opt-in marker.
Context. The most mature chemistry and the right one to prove the full vertical slice (enumerate → score → outcome → off-target → candidate).
Deliverables.
enumerate/cas9.py:enumerate_cas9(resolved, intent, *, pam="NGG", allow_ng=False, allow_spry=False) -> list[Guide]. Enumerate all PAM-anchored spacers whose cut site (default 3 bp 5' of PAM) can achieve the intent within the actionable window; place each strand-aware. Propose an HDR donor template for precise correction intent. Emit NG/SpRY guides only when no NGG guide is actionable and the flag is set.scoring/cas9_efficiency.py: an on-target efficiencyScorerwith (a) a Rule Set 3 adapter as a no-large-download baseline and (b) a backbone-fine-tuned deep-ensemble model from the model zoo. Both return calibratedPredictions. Include the tracrRNA-aware feature from DeWeirdt-Doench (Nat Commun 2022); cite it.scoring/cas9_outcome.py: adapters to inDelphi, Lindel, X-CRISP, each returning anEditOutcomedistribution over indel alleles; an ensemble mode that reports inter-model agreement as an uncertainty signal. License-gate via the model zoo.
Defaults & decisions. Primary PAM NGG; suggest NG/SpRY only when no NGG places a cut in the actionable window. Default efficiency = backbone-fine-tuned ensemble; Rule Set 3 always-available baseline. Default outcome = inDelphi, with Lindel and X-CRISP available and an ensemble/agreement option. Cut site default = 3 bp 5' of PAM. All scores carry 80% intervals and an OOD flag.
Integration + tests. Wire enumeration → efficiency → outcome → Phase 5 off-target into a cas9 path that
yields DesignCandidates. End-to-end test on a curated ClinVar variant producing a ranked set with
intervals, outcomes, and ancestry-stratified off-target. Enumeration completeness/strandedness; efficiency
baseline matches Rule Set 3 within tolerance; outcome adapters reproduce published example distributions; OOD
fires on non-human input. CI weight-free with stubs.
Context. The hard part is predicting the window outcome: which target base(s) get edited and what bystanders ride along. Mature predictors (BE-Hive, BE-DICT) exist and should be wrapped.
Deliverables.
- A declarative
BaseEditorregistry (deaminase, edit chemistry, default window, PAM, motif preferences) seeded with ABE8e, CBE4max, evoCDA1; adding an editor is a data change, not code. enumerate/base_editor.py:enumerate_base_edits(resolved, *, editors=DEFAULTS, window=(4,8)) -> list[BaseEditWindow]. For the required chemistry, find spacers placing the target base in-window per editor; annotate clean vs. bystander-present and the in-window base composition.scoring/base_outcome.py: wrap BE-DICT (default) and BE-Hive (optional) behind one interface returning anEditOutcomeover alleles; computep_intended_exactandbystander_burden, each a calibratedPrediction; a cross-editor recommendation maximizing clean-edit probability. License-gate via the model zoo.
Defaults & decisions. Window 4–8 (configurable per editor). Default editors ABE8e (A→G) and CBE4max/evoCDA1 (C→T). Outcome default = BE-DICT, BE-Hive optional, ensemble/agreement option. The designer prefers the editor/guide combination maximizing P(exact intended allele) while minimizing bystander burden, and surfaces the tradeoff explicitly.
Integration + tests. Enumeration → outcome → Phase 5 off-target → DesignCandidates. End-to-end test on
a splice-donor or missense variant correctable by ABE, asserting a ranked set with explicit bystander
tradeoffs and ancestry-stratified off-target. Window placement correctness across editors; bystander
detection; outcome adapters reproduce published distributions; recommendation prefers higher clean-edit
probability. CI weight-free with stubs.
Context. The chemistry where AlleleForge contributes the most: no open-source tool today combines variant input + ML efficiency + chromatin context + population-aware off-target + outcome/byproduct prediction. PRIDICT2.0 is SOTA for efficiency but has no variant front-end and no off-target module; PrimeDesign/PrimeVar give ClinVar→pegRNA but only rule-based scoring and reference-only off-target; CRISPRme does population off-target but designs no pegRNAs. AlleleForge stitches these together and fills the seams.
Deliverables.
enumerate/prime.py: full pegRNA enumeration: choose nick site and spacer; enumerate PBS (8–17 nt) and RTT (7–34 nt, covering edit + ≥5 nt 3' homology); select a PE3/PE3b nicking guide (prefer a seed-disrupting PE3b ngRNA); attach the tevopreQ1 epegRNA 3' motif by default; respect MMR context. Emit validatedPegRNA+NickingGuidepairs (Phase 1 structural checks).scoring/prime_efficiency.py: wrap PRIDICT2.0 as the default efficiencyScorer; integrate ePRIDICT for chromatin-context adjustment using Phase 3 ENCODE tracks when a cell context is supplied; add a DeepPrime/GenET cross-check adapter. Return calibratedPredictions and setin_distribution=Falsewhenever the target/cell context is unlike PRIDICT's HEK293T/K562 training distribution — surface this prominently. License-gate all wrapped models.scoring/prime_outcome.py: predict intended-vs-byproduct distribution (scaffold incorporation, partial RTT, indels) as anEditOutcomewith uncertainty.- Off-target: run the Phase 5 engine on both the pegRNA-induced nick and the ngRNA nick; merge into one
ancestry-stratified
OffTargetReport.
Defaults & decisions. Default architecture PE5max + epegRNA(tevopreQ1) + PE3b-when-possible. PBS 8–17 nt, RTT 7–34 nt — search both and let the efficiency model rank. Efficiency default = PRIDICT2.0; apply ePRIDICT when a cell context is given; always show the OOD flag. Off-target run for both nicks by default.
Integration + tests. Implement examples/01_clinvar_to_design.ipynb showing a variant flowing to a
ranked pegRNA design with calibrated efficiency, predicted byproducts, and an ancestry-stratified off-target
report. End-to-end test asserting a structurally valid top pegRNA with all four axes populated. PBS/RTT
boundaries; PE3b preference logic; motif attachment; efficiency adapter reproduces PRIDICT2 example values
within tolerance; OOD fires on non-HEK/K562 contexts; both nicks searched. CI weight-free with stubs.
Context. The orchestrator that realizes the variant-first promise: from one variant, decide which chemistries are eligible, generate candidates from each, score them on one footing, and return a ranked, explained menu.
Deliverables. src/alleleforge/design/:
routing.py:eligible_chemistries(resolved, intent) -> list[Chemistry]as transparent, inspectable rules driven by variant class + VEP consequence + intent (e.g., a single-transition SNV correctable in-window → base editing eligible; any precise small edit → prime eligible; disruption intent → nuclease eligible). Document each rule's biological rationale; make adding a rule trivial.designer.py:design(input, *, intent, populations, patient_vcf=None, ...) -> RankedMenu. Resolves the variant (Phase 4), routes, enumerates and scores candidates per eligible chemistry (Phases 7–9), runs off-target (Phase 5), assemblesDesignCandidates, ranks them, and attaches full provenance. Runs any subset of eligible chemistries and degrades gracefully if one chemistry's model is unavailable (reports why, continues).ranking.py: multi-objective ranking over (calibrated efficiency, outcome cleanliness, off-target safety with ancestry-aware penalty, simplicity). Default = a transparent weighted sum and a Pareto-front view. Compute the safety term against the worst-affected ancestry, not the average. Human-readable rationale per candidate, stored in the menu.
Defaults & decisions. Default intent inferred from variant + a user hint (correct by default). Default
weights: efficiency 0.35, outcome cleanliness 0.30, off-target safety 0.30, simplicity 0.05 — all
overridable and shown in output. Ranking always exposes the Pareto front. Safety penalty uses the
worst-affected ancestry so a design that is safe on average but dangerous in one population is correctly
down-ranked.
Tests. Routing yields the correct eligible set on curated SNV/indel/splice fixtures; design(...)
returns a populated RankedMenu with provenance on an end-to-end case; ranking is stable, weight-sensitive
in the expected direction, and the ancestry-worst-case penalty down-ranks a reference-biased guide relative
to a population-safe alternative. Property test: every candidate has efficiency, outcome, and off-target
populated or an explicit reason it does not.
Context. Bench scientists need cloning-ready sequences and a shareable report; pipelines need clean machine-readable output. Every report carries the research-use disclaimer and full provenance.
Deliverables. src/alleleforge/report/:
oligos.py: emit cloning-ready oligos per candidate — Cas9 sgRNA oligos with vector-appropriate overhangs; base-editor sgRNA oligos; pegRNA + ngRNA oligos for standard pegRNA cloning (scaffold + tevopreQ1 motif). Parameterize by vector/overhang scheme with named defaults. Validate that round-tripping the oligos reconstructs the intended spacer/RTT/PBS.builder.py: assemble aRankedMenuinto a structured, serializable report model (candidates, scores with intervals, outcome distributions, ancestry-stratified off-target tables, provenance, disclaimer).html.py/pdf.py: render the report. HTML embeds interactive Plotly off-target/outcome plots and an optional JBrowse 2 context view; PDF is static and print-ready. Both lead with the disclaimer and end with provenance.- Machine-readable export: JSON (validated against the Phase 1 schemas), TSV, and Parquet.
Defaults & decisions. Default machine output = JSON + TSV; Parquet for batch. Default human output = HTML with a PDF option. Oligo output defaults to common vector schemes, naming the scheme explicitly.
Tests. Oligo round-trip correctness per chemistry and vector scheme; the report includes disclaimer + provenance on every render; JSON validates against schema; HTML and PDF render without error on an end-to-end menu fixture; off-target tables are ancestry-stratified.
Context. A thin, reproducible, config-driven CLI over the library. No business logic here.
Deliverables. src/alleleforge/cli/main.py (Typer):
aforge design— variant → ranked menu; accepts every input form; flags for intent, chemistries, populations, patient VCF, reference build, output formats, ranking weights;--config run.tomlwith CLI overrides; the resolved config is echoed into provenance.aforge offtarget— standalone population/haplotype-aware off-target for a supplied spacer.aforge resolve— show the normalized variant + consequence for any input (debugging aid).aforge data— manage datasets (status, fetch, refresh, show versions/licenses).aforge bench— run CRISPR-Bench tasks (Phase 14).- Global:
--seed,--reference,--cache-dir,--verbose, and machine-readable--jsoneverywhere.
Defaults & decisions. Typer for ergonomics + auto-help. Every command can emit JSON. Runs are reproducible from the echoed config + seed. Long operations show progress and write a provenance sidecar next to outputs. Meaningful, distinct exit codes for input errors, missing data, and unavailable models.
Tests. Invoke each subcommand via Typer's test runner on fixtures; reproducibility (same config + seed → identical output modulo timestamp); JSON mode is schema-valid; exit codes. Generate a CLI usage page from the Typer app.
Context. The accessible front door for users who will not touch a terminal. A FastAPI backend exposes the library; a Next.js frontend provides the variant-first journey visually. Sequences never leave the user's deployment.
Deliverables.
src/alleleforge/web/api/: a FastAPI app exposingresolve,design,offtarget,data, andbenchas async endpoints; long runs go through a task queue with progress and a job-status endpoint; OpenAPI auto-generated; the Phase 1 schemas validate request/response. No business logic beyond orchestration.src/alleleforge/web/frontend/: a Next.js + React app implementing the journey — variant entry (all input forms) → eligible chemistries → ranked candidate menu with interactive Plotly efficiency intervals and outcome distributions → an ancestry-stratified off-target browser with embedded JBrowse 2 → oligo/report export. Prominent research-use disclaimer; state that no sequence data is transmitted externally.docker-compose.yml: one-command local deploy of api + frontend + worker, wired to a local cache volume.
Defaults & decisions. Backend FastAPI; frontend Next.js (Streamlit acceptable for a v0 internal preview). Plotly for charts, JBrowse 2 for genome context. All compute local/user-controlled; no telemetry, no external sequence transmission. Async job model with progress. Auth is deployment-optional (default local deploy is single-user, no auth).
Tests. API endpoint tests (httpx/async) including schema validation and the async job lifecycle; a minimal frontend e2e (Playwright) covering variant entry → candidate menu; verify no outbound call carries sequence data (assert against a mock egress). Heavy models stubbed in CI.
Context. The sister deliverable and a field-level contribution: versioned datasets, frozen splits, standard metrics, and a public leaderboard — a common yardstick for guide design. Independently valuable and publishable even before AlleleForge is feature-complete.
Deliverables. benchmark/crispr_bench/:
datasets/: license-aware, provenance-stamped ingestion of the major public datasets — Rule Set 3 validation, DeepHF/DeepSpCas9 (Cas9 efficiency); FORECasT, inDelphi, Lindel (nuclease outcomes); BE-Hive, BE-DICT (base-edit outcomes); PRIDICT2 Library-Diverse (pegRNA efficiency); CRISPRsql / GUIDE-seq aggregates (off-target). Each with provenance, license, and citation; non-redistributable parts fetched at runtime via the Phase 3 registry.splits/: frozen, content-hashed train/val/test splits, including deliberate cross-cell-type and cross-context test splits to measure generalization. A loader verifies the hash on read; changing data requires a new split version.tasks.py: the five tasks (Cas9-efficiency, Cas9-outcome, BE-outcome, PE-efficiency, off-target-classification) with fixed input/label contracts.metrics.py: Spearman/Pearson (efficiency), KL/top-k (outcomes), AUROC/AUPRC (off-target), plus calibration (ECE) as a first-class required metric on every task.runner.py: evaluate any object implementing theScorerprotocol against a (task, split) pair and emit a signed, provenance-stamped result JSON. Wireaforge benchto it.leaderboard.py: a submission format requiring a model card; a static leaderboard site (HuggingFace Spaces / Polaris compatible) displaying metrics, calibration, and split version per entry.
Defaults & decisions. Splits immutable once published and content-hashed; a new split is a new version.
ECE required on every task. The leaderboard accepts external submissions with model cards. Hosting target:
HuggingFace + Polaris. benchmark/README.md documents datasets/licenses/citations, the split philosophy, how
to submit, and the lab-outreach launch plan.
Tests. Split hashes stable and verified on load; metrics match hand-computed values on tiny fixtures; the runner scores a stub model end to end; a sample submission renders on the leaderboard. All datasets in CI are small synthetic fixtures.
Context. A tool only contributes if people can use, trust, and cite it.
Deliverables.
docs/(mkdocs-material): concept guide (variant-first philosophy, the uncertainty contract, population-aware safety and why ancestry stratification matters), tutorials, full API reference (mkdocstrings), the data-provenance page, the CLI reference (from the Typer app), a deployment guide, and a prominent ethics/scope page (research-use-only; not medical advice; off-target requires validation; responsible/dual-use note).examples/:01_clinvar_to_design.ipynb(the canonical journey across all three chemistries),02_population_offtarget.ipynb(reproduce the reference-bias /rs114518452ancestry-stratified off-target finding from a documented fixture; cite Cancellieri & Pinello, Nat Genet 2023),03_batch_vcf.ipynb(cohort-scale design).- Release: PyPI + bioconda + multi-arch Docker images + a tagged GitHub release with a Zenodo DOI;
CITATION.cfffinalized; a short methods-preprint outline indocs/paper/.
Defaults & decisions. Docs = mkdocs-material + mkdocstrings, built in CI, deployed on tag. Examples run in CI against stub models. First public release is v0.1.0 (three chemistries end to end with the benchmark); v1.0.0 is reserved for after external validation and the methods preprint. A Zenodo DOI is minted on the first tagged release.
- A ClinVar variant, supplied by accession, flows end to end to a ranked candidate menu spanning every eligible chemistry (nuclease, base, prime), each candidate carrying a calibrated efficiency interval, a predicted outcome distribution, and an ancestry-stratified population/haplotype-aware off-target report, with complete provenance — reproducible from config + seed.
- The reference-bias /
rs114518452off-target case is reproduced as an integration test and a documented example. - The prime-editing path demonstrably unifies all four axes (variant input, ML efficiency with OOD honesty, outcome/byproduct prediction, population-aware off-target).
- CRISPR-Bench publishes at least the Cas9-efficiency, PE-efficiency, and off-target tasks with frozen splits, calibration metrics, and a working leaderboard.
- Library, CLI, and web all exercise the same core; CI is green across lint, type, test, Rust, docs, and benchmark smoke; v0.1.0 is on PyPI + conda + Docker with a Zenodo DOI.
- A competitor ships an integrated four-axis tool first. Pivot emphasis to CRISPR-Bench (benchmark + model zoo + leaderboard) as the primary contribution; it is valuable regardless and harder to duplicate.
- Foundation models prove strongly zero-shot for guide efficiency. Refocus on FM + lightweight adapters;
the swap-able
SequenceEmbedder/Scorerinterfaces already allow this. - Cross-cell-type generalization stays poor. A field-wide reality, not an AlleleForge failure; lean harder on honest OOD flagging and calibrated uncertainty, and make the cross-context benchmark splits a headline feature.
- Upstream model licenses block bundling. The model-zoo license gate already prevents vendoring; fall back to runtime fetch with consent and document the constraint in the model card.
0 → 1 → 2 → 3 → 4 → 5 (safety core) → 6 (ML substrate) → 7 (prove the vertical slice on nuclease) → 8 (base) → 9 (prime — the flagship) → 10 (designer) → 11 (report) → 12 (CLI) → 13 (web) → 14 (CRISPR-Bench, can run in parallel from Phase 6 onward) → 15 (docs + release).
Phases 0–5 must precede all chemistry work. Phase 6 must precede 7–9. CRISPR-Bench (14) can begin once Phase
6's Scorer protocol is stable. Do not promise clinical applicability anywhere; AlleleForge generates
rigorously uncertain hypotheses, and that honesty is the product.
End of specification. This document is the contract. When a decision not covered here arises, prefer the option that maximizes reproducibility, honest uncertainty, and population-aware safety — in that order.