CTA analysis APIs and identity conventions

The current released research selection is the four-assessment CTA funnel. Use the shared species API for current decisions. The 463-group 1.21.0 release and 343-group harmonized funnel retain their versioned comparison baselines. Earlier membership and threshold reports remain versioned comparisons; their results() accessors return frozen datasets, not the latest research selection.

Names and counting units

Field Meaning
gene_id / canonical_gene_id One canonical human Ensembl gene locus. canonical_gene_id is used in source-match audits.
source_gene_id, source_gene_name Original source identifiers, retained even if unresolved.
historical_ensembl_gene_ids Registered historical replacements; a JSON list, not a guessed name match.
alternate_ensembl_gene_ids Alternate-contig identifiers, kept distinct from historical replacements.
gene_name_synonyms Registered gene names and synonyms, including ambiguous names where applicable.
protein_equivalence_group_id Membership-based PEG: identifier for identical chosen representative proteins. It is not an ENSG or a sequence hash.
protein_group_label Display label such as NY-ESO-1 or XAGE1A/B; never the gene identity.
representative_gene_id A real member locus used for compatibility with existing measurement APIs. It does not represent an extra gene.
member_gene_ids, member_identities Complete expected group membership and identity bundles, including explicit unresolved registry members.
proteoform_key Existing compatibility display key from oncoref.proteoforms; new research tables also expose the explicit group ID.

“Protein equivalence” here means identical selected reference sequences. It does not establish equivalence of all splice isoforms, post-translational forms or gene-specific regulation. Reports always label whether counts represent loci or groups.

Processing boundaries

gene_matching handles identity, protein_equivalence handles additive measurements, cell_rna_policy handles context-specific RNA thresholds, and cta_funnel handles independent decision layers. Report scripts assemble these APIs and render artifacts. The current cta_selection_policy separates safety and CTA identity and reserves Unsafe for safety evidence; the earlier policy helpers below retain their own dated behavior. compartment_rna_policy.assess_contexts() applies the provisional cell-compartment tiers while retaining original anatomy, values and IHC grades. Its protein review API distinguishes explicitly Low staining from ungraded accepted positives; source access alone cannot clear a known positive.

from oncoref.gene_matching import match_reference_genes, harmonize_reference_expression
from oncoref.protein_equivalence import collapse_reference_expression
from oncoref.cta_funnel import EvidenceLayer, classify_layers

matches = match_reference_genes(
    source_table, id_column="Gene", symbol_column="Gene name",
    source="source-name", source_version="pinned-release",
)

collapsed = collapse_reference_expression(
    sample_matrix, sample_cols=sample_ids, source="cohort-name",
    source_version="pinned-release", unit="TPM", scope="genome",
)
values = collapsed.expression
coverage = collapsed.coverage
identity_audit = collapsed.identity_audit

decisions = classify_layers(
    group_facts,
    [EvidenceLayer("coverage", missing_coverage, no_unsafe_evidence),
     EvidenceLayer("normal RNA", above_candidate_ceiling, above_unsafe_boundary)],
    entity_column="protein_equivalence_group_id",
)

Identity matching quarantines conflicting stable IDs/names and ambiguous symbols. An explicit unknown ENSG is never replaced by a name-only guess. Duplicate aliases of one locus are not additive. The strict expression API raises on conflicts; the explicit quarantine mode excludes conflicting measurements and records why.

Sums require the same sample/context and the same native unit. Partial sums can establish positive lower bounds; incomplete member coverage cannot establish normal absence. Maxima, percentiles and ranked DICE slots are not additive inputs. sum_context_observations() supports long-form matched contexts; unpaired_rank_bounds() preserves ranked-sum uncertainty without pairing donors.

Staining evidence

ihc_observations.parse_staining_statement() extracts the reported result, intensity, localization and percentage bounds. It retains the source statement. external_ihc.assess_evidence() applies explicit evidence-quality requirements; reconcile_hpa() reconciles exact gene/tissue/cell/stage/specimen-state contexts. Collection, categorical result and validation confidence are separate concepts. An unreviewed negative is still a negative report, with explicit reasons it cannot yet clear an uncertainty hold. Cross-reactivity is retained as a source claim; control recommendations are not observations. Reagent resellers do not create independent evidence, and whole-organ negatives do not clear rare cell types.

All report writers share deterministic CSV/gzip output and SHA-256 helpers from scripts/cta_report_common.py. Source, input and output receipts accompany each stage. Figures are generated from the current decision tables, with no hardcoded candidate counts in the active report.

input_hashes() pins the shared writer as well as the stage inputs. Repository paths are relative to the checkout root; explicitly external source files retain their original paths. The full raw source-matrix hashes are also preserved.

To rebuild the current report from the pinned source inputs and registered matrices, run these commands from the repository root:

python scripts/cta_tumor_union.py docs/audits/cta-expanded-funnel-20261027/tumor-union
python scripts/cta_expanded_normal.py docs/audits/cta-expanded-funnel-20261027/normal
python scripts/cta_expanded_funnel.py docs/audits/cta-expanded-funnel-20261027
python scripts/cta_protein_group_normal.py docs/audits/cta-harmonized-20261027/group-normal
python scripts/curate_external_ihc.py
python scripts/cta_harmonized_report.py docs/audits/cta-harmonized-20261027
python scripts/cta_compartment_report.py
python scripts/build_cta_selection_release.py

The external curation command uses the checked-in categorical source extraction; scripts/collect_external_ihc.py refreshes that extraction when an intentional source update is required. Raw normal-expression downloads use the source registry and local caches. Different upstream releases require a new audit. The compartment report can reuse its streamed cluster replay with --reuse-clusters only after verifying implementation, source and output hashes. Its locus export reports current group decisions and explicitly labels the older locus-level decisions baseline_gene_category; it does not present those as a fresh locus-only analysis.

Group decision exports contain only group-level quantities. They do not copy a representative locus's old maxima or selection flags into group measurement columns. The locus and concordance exports rename the old expression fields to historical_expression_category, historical_expression_detail and historical_expression_forbidden. Their protein_group_category is current; the group table's category and hold: / unsafe: columns use the released policy.

The current policy uses unusable or disputed antibodies to disable negative-IHC threshold boosts, without an additional gene-wide antibody veto after the strict RNA fallback. Antibody concern and external_review_flag remain evidence flags even for selected groups. Reliable positive evidence still contributes exclusions. Clinical target-context evidence is recorded separately; an expression-policy Unsafe label does not establish treatment toxicity. The PRAME, CTAG2 and TBXT assessment exports the relevant measurements, thresholds and clinical-source denominators.