Coding Region Annotation From Templates
CRAFT is an evidence-aware functional annotation toolkit for long-read single-cell transcriptomes. It accepts isoforms from Pigeon/SQANTI3, IsoQuant, Bambu, FLAIR, FLAMES, or another caller and keeps four questions separate:
- How well is the transcript structure supported by molecules and orthogonal evidence?
- Which transcript ends—and therefore which CDS boundaries—were actually observed?
- What coding sequence is supported by the reference and by the observed sequence?
- Which RNA-surveillance rules could apply if that isoform exists in the cell?
That separation is the central v2 design decision. Cell recurrence is descriptive, not a probability that an isoform is real. An internal ATG is not silently promoted to the biological start. An NMD rule match is reported as predicted susceptibility, not observed degradation.
CRAFT never deletes input rows. It exposes evidence, ambiguity, censoring, and limitations so that filtering can be calibrated on positive and negative controls.
pip install -e ".[dev]"Python 3.10 or newer is required.
craft annotate \
--isoforms isoforms.gtf \
--reference gencode.annotation.gtf \
--genome genome.fa \
--classification pigeon_classification.txt \
--sqanti-classification pigeon_classification.txt \
--output-dir craft_outTo compute evidence from the alignment instead of importing it:
craft evidence \
--bam aligned.bam \
--isoforms isoforms.gtf \
--genome genome.fa \
--processes 24 \
--output-dir craft_evidenceThis writes junctions.tsv (dataset-wide read support, distinct start positions
and splice motif per junction), junction_pairs.tsv (support for adjacent
junction pairs), and isoform_evidence.tsv. Feed them back to annotate with
--junctions, --junction-pairs and --evidence-table.
Junction-level support is what makes sparse isoforms rankable: a novel isoform with three reads may be built entirely from junctions carrying thousands. Pair support is what stops that argument being abused: a chain assembled from junctions that were each seen thousands of times, but never seen together on one molecule, is not a supported isoform. Decoy calibration put that failure at FDR 1.000 with junction evidence alone and 0.002 once pair evidence and a read split were added.
Useful optional inputs:
--counts counts.h5adadds per-cell abundance and detection summaries.--classification FILEimports SQANTI3/Pigeon structural class and parent/gene hints.--sqanti-classification FILEreads that same classification table as evidence, mappingperc_A_downstream_TTS,RTS_stage,all_canonical,min_cov,within_CAGE_peakand the poly(A) columns onto CRAFT's evidence features. Columns SQANTI3 left empty stay absent rather than counting as negative evidence.--evidence-table FILEimports molecule-level and artifact evidence and emits a transparent, explicitly uncalibrated evidence score and tier. Combines with--sqanti-classification; the table wins on features both supply.--orf-comparator-gtf FILEcompares CRAFT with an independent CDS caller such as ORFanage without treating either answer as truth.--polya-atlas FILEadds curated cleavage/polyadenylation-site evidence, and--cage-peaks FILEis its 5' counterpart. Without both, novel transcript ends stay unscored and 5' fragments cannot be told from real alternative starts.--junctions/--junction-pairstake the tables fromcraft evidence.--gene-counts DIRwith--countstests whether an isoform's share of its gene varies with cell state. It promotes regulated isoforms; it never rejects.--pfam-hmm Pfam-A.hmmcompares predicted protein-domain content.--infer-alternative-startopts into downstream in-frame ATG inference for 5'-censored transcripts. It is off by default.
Run craft annotate --help for the complete option list.
| Input | Required | Purpose |
|---|---|---|
| Isoform GTF | yes | Exon structures keyed by transcript_id |
| Reference GTF | yes | Exons, CDS, and preferably explicit start/stop codons and tags |
| Genome FASTA | yes | Sequence reconstruction; indexed automatically when possible |
| Classification table | no | Structural categories plus parent/gene hints |
| Evidence table | no | Unique/ambiguous support, junction, end, artifact, and replicate evidence |
| Per-cell counts | no | Abundance, detection fraction, and group summaries |
| Independent ORF GTF | no | Caller agreement/disagreement analysis |
| Alignment BAM | no | Junction and junction-pair support via craft evidence |
| CAGE / poly(A) BED | no | Evidence for novel transcript ends |
| Per-cell gene counts | no | Gene-conditional cell-structure test |
All genomic inputs must use the same assembly and chromosome naming convention.
| File | Description |
|---|---|
per_isoform.tsv |
Versioned per-isoform annotation; optional inputs add populated evidence blocks |
per_isoform.json |
Same records with list-valued fields preserved |
per_element.tsv |
One row per isoform element (junction, junction pair, TSS, TTS) with its novelty, support and score |
report.html |
Self-contained exploratory report |
annotated.h5ad |
Annotation in var and, when supplied, per-cell counts in X |
per_celltype_consequence.tsv |
Molecule-weighted group summaries with --counts --group-by |
per_celltype_as_nmd.tsv |
Evidence-supported predicted AS–NMD candidates by group |
The authoritative field definitions are in docs/features.md.
What the evidence actually measured on a real dataset, including where it fails, is
recorded in docs/calibration_report.md.
craft fdr builds structurally plausible but known-wrong isoforms from your own
data, scores them through the same function as the real ones, and reports a
false-discovery rate per class of error instead of asking you to pick a cutoff:
craft fdr \
--isoforms isoforms.gtf --reference reference.gtf --genome genome.fa \
--junctions craft_evidence/junctions.tsv \
--junction-pairs craft_evidence/junction_pairs.tsv \
--cage-peaks cage_peaks.bed.gz --polya-atlas polya_sites.bed.gz \
--split-calibration --output-dir craft_fdrFamilies are reported separately, never pooled, because a score can control one class of error while being blind to another. On BD70 chr22 the same score sits at FDR 0.004 against antisense decoys, 0.002 against recombined intron chains, and 0.242 against fabricated 5' ends.
The main v2 fields are:
isoform_evidence_tier: independent structure evidence;insufficient_evidencemeans missing inputs, not a failed transcript.structural_support_score,limiting_element: the isoform's least supported novel element (junction, TSS or TTS), and which one it was. Elements shared with the annotation are not what a novel isoform is claiming; the novel ones are, and the isoform cannot be better supported than its weakest. This is what makes a three-read isoform built from well-supported junctions outrank a forty-read one carrying a single junction seen nowhere else.isoform_evidence_score: the minimum across four evidence axes (structure, ends, molecules, artifact), so it reports the weakest supported line of evidence rather than an average that hides it.isoform_evidence_limiting_axis,evidence_axes_available: which axis set the score, and how many of the four had anything to say. Check these before reading the tier; two agreeing axes out of four is a much weaker claim than four.parent_ambiguous,parent_match_margin: whether reference-parent assignment is unique enough to support propagation.orf_censoring:none,left,right, orboth; a missing transcript end is treated as censoring, not as a discovered CDS boundary.resolved_orf_status: includesleft_censoredandright_censoredrather than inventing a start or calling a terminal truncation a complete ORF.nmd_susceptibility,nmd_evidence_tier,surveillance_limitations: structural prediction, strength of supporting annotation, and reasons not to over-interpret it.nonstop_decay_candidate: a separate flag for right-censored/stopless molecules.
Example downstream selection:
import pandas as pd
df = pd.read_csv("craft_out/per_isoform.tsv", sep="\t")
# Structure-supported isoforms with a non-ambiguous propagated parent.
supported = df[
df["isoform_evidence_tier"].isin(["strong", "supported"])
& ~df["parent_ambiguous"].fillna(True)
]
# Predicted NMD candidates with complete ORF boundaries and stronger evidence.
nmd_candidates = supported[
(supported["nmd_susceptibility"] == "likely_sensitive")
& supported["nmd_evidence_tier"].isin(["high", "moderate"])
& (supported["orf_censoring"] == "none")
]Do not copy these cuts blindly. The evidence score is not calibrated to a target false-discovery rate; tune thresholds with spike-ins, technical replicates, reference holdouts, and plausible artifact decoys from the assay being analysed.
CRAFT ranks parent candidates using exact/contained intron-chain agreement, junction precision and recall, overlap, upstream parent/gene hints, and curated reference metadata. It reports ambiguity instead of hiding close ties. It projects the chosen reference CDS, reconstructs the observed spliced sequence, and records whether start and stop codons are observed or censored. Novel/orphan isoforms get a de novo ORF candidate, clearly labelled as such. Rule-based RNA-surveillance susceptibility is then reported with its evidence tier and limitations. Coding potential and Pfam provide supporting features, not ground truth.
See docs/v2_design.md for the scientific contract and
literature context, docs/migration_v2.md for changed
semantics, and docs/user_guide.md for operations.
- CRAFT annotates and ranks evidence; it is not itself a long-read aligner, transcript assembler, cell caller, or universally calibrated isoform filter.
- Reference annotation is evidence, not truth. Reference completeness, phase, APPRIS/MANE/CCDS/basic tags, and parent ambiguity are retained in the output.
- Structural rules cannot establish actual NMD. Validation requires perturbation, half-life, translation, or other orthogonal data.
- A 5'-truncated long read cannot by itself identify the true translation start.
- The supplied evidence score is transparent and useful for triage, but requires assay-specific calibration before being interpreted as a decision boundary.
The branch is v2.0.0 development code. The scientific semantics have intentionally changed from v1; read the migration guide before comparing old and new tables.
See CITATION.cff. CRAFT is MIT licensed; see LICENSE.