ToxCast Assay Coverage of the Pathograph

In progress NAMENVIRONMENTAL_EXPOSUREPATHOPHYSIOLOGYFEASIBILITY_ANALYSIS

ToxCast Assay Coverage of the Pathograph

ToxCast and Tox21 are the US EPA's high-throughput screening programs. Each assay endpoint in them is one measurement: a named readout, in a named system, aimed at a named molecular target. EPA publishes a structured description of every endpoint, saying which gene it targets, what kind of target that is, which process it reads, which species and tissue it ran in, what method it used, and which way a hit reads.

That description is what could let an assay be tied to a dismech pathophysiology node without reference to any chemical. This project measures how far it reaches into the pathograph as it stands, and tests whether dismech can represent such a tie at all, before any is committed.

It is tracked in issue #12858. The parent is #12682, which owns everything about chemical results: which chemicals were active in which assay, at what potency. None of that is in scope here, and nothing on this page depends on a chemical having been tested.

What "coverage" means on this page

An endpoint is a candidate for a node when both name the same gene. The counts below are counts of candidates.

They are not counts of mappings, and the difference is the reason for the word. Issue #12858 sets out three ways an endpoint and a node can share a gene and still not correspond: the node names a lesion where the assay reads a state, the two run in opposite directions, or they measure different quantities of the same gene product. None of those is visible to a join on gene. So every figure here is an upper bound on what could be mapped, and the share of candidates that survive a closer reading is not yet known.

The join itself:

The snapshot

Assay annotations retrieved from the EPA CTX API on 2026-09-30; kb/disorders/ as of commit 0aedd3ce8e. Both move: the panel with each EPA release, the pathograph with every curation pull request. The complete tables are in the coverage report, and just toxcast-coverage regenerates them, so treat the numbers below as dated and the recipe as current.

a) Pathograph node coverage

Count Share
Assay endpoints in the panel 1,570
Endpoints declaring a gene target 1,108 70.6% of the panel
Candidate endpoints 552 35.2% of the panel
Pathophysiology nodes in kb/disorders/ 22,403
Nodes naming an HGNC-bound gene 4,021 17.9% of nodes
Candidate nodes 452 2.0% of nodes
Candidate endpoint-node pairs 2,080

Half of the endpoints that declare a gene are candidates, and they converge on a small part of the pathograph: 452 nodes, or 11.2% of the nodes that name any gene. The 2,080 pairs are the size of the job if every candidate were read.

Three things about where the candidates fall:

b) Gene coverage

The panel names 496 distinct gene targets: 433 human and 63 ortholog-only.

Where the target sits in kb/disorders/ Human Ortholog-only All
On a pathophysiology node 180 14 194
Bound in a genetic: record, on no node 31 4 35
Bound elsewhere in an entry only 0 0 0
Bound nowhere 222 45 267

So 39% of the panel's targets are on the pathograph, 7% are known to dismech without being on it, and 54% are not bound in any disease entry.

The third row is empty in this snapshot, not broken: every panel target that any entry binds sits either on a node or in a genetic: record. The row exists because a gene can be bound in other places, such as a treatment's target_gene or a subtype's gene list, and a target found only there would land in it.

The panel is very uneven, and so is the pathograph, in different places. 247 targets appear in exactly one endpoint and twelve appear in ten or more. Of those twelve, nine are on a node: ESR1, AR, PPARG, NR3C1, ESR2, THRA, THRB, NR1H4 and TP53. PGR (14 endpoints) and NR3C2 (10) are bound nowhere, and CXCL8 (10) only in genetic: records. Going the other way, the targets that reach the most entries are cancer drivers with few assays behind them: TP53 is on 38 nodes in 36 entries from 10 endpoints, STAT3 on 21 nodes from 2, and PIK3CA on 18 nodes from a single endpoint. ESR1 has 40 endpoints and 8 nodes.

ESR1 is worth a note of its own. When #12858 was written it was the most heavily assayed target in the panel and no node named it. The 8 nodes in 7 entries it has now are the work of the estrogen signalling project (projects/ESTROGEN_SIGNALLING.md), which was opened to close that gap.

c) Disease coverage

Entries in kb/disorders/ Count Share
All entries 3,256
Carry at least one candidate node 317 9.7%
Bind a target somewhere, on no node 189 5.8%
Bind no target 2,750 84.5%

317 entries carry a node a ToxCast endpoint could be considered for. In 230 of them it is a single node; 77 have two or three, and 10 have four or more.

The 189 entries in the middle row already bind a panel target, typically in genetic:, without any node naming it. Whether the gene belongs on a node is a curation question for each entry and not a backfill, because a gene on a node asserts that it drives that mechanism in that disease.

What the counts leave out

Worked example: one THRB endpoint against the thyroid-hormone-resistance entries

The counts above say how many candidates exist. This says what writing one down costs, using the endpoint #12858 names for the purpose.

The endpoint. TOX21_TRB_COA_Antagonist_Followup_ratio, EPA assay endpoint 2247. A GST-tagged TR-beta ligand-binding domain, a terbium-labelled anti-GST antibody and a fluorescein-labelled SRC-2 coactivator peptide report coactivator recruitment by resonance energy transfer, normalized against DMSO-only and T3 control wells. EPA annotates the target as human THRB, the direction as loss, and the format as cell-free; a hit is a chemical that reduces recruitment. Its method paper is PMID:31566444, which describes the reaction as 5.0 nM of GST-tagged TR-beta ligand-binding domain with a 200 nM coactivator peptide — no cell of any kind.

The node the gene join offers. THRB is named by exactly one pathophysiology node in the corpus: THRB Dominant-Negative Receptor Formation, the trigger node of Generalized_Resistance_to_Thyroid_Hormone. It is MOLECULAR, and its genetic_context records functional_impact_category: DOMINANT_NEGATIVE with a missense allele.

The node the join cannot offer, and which the assay actually matches. The next node down that chain is Impaired Corepressor Release at Thyroid Hormone Response Elements. Its molecular function is GO:0001222 transcription corepressor binding and its description says the mutant receptor "fails to release the nuclear receptor corepressor complex (NCoR/SMRT) ... and correspondingly fails to recruit coactivators". Failure to recruit coactivators is precisely what endpoint 2247 measures. That node names no gene, so the gene join is blind to it.

This is a limit on candidate discovery, not on the mapping vocabulary. This entry names THRB once, on the trigger node, and describes the consequences downstream without repeating it, so the join reaches the lesion and not the state. How general that is has not been measured, and it is not universal — only 64 of the 452 candidate nodes carry a genetic_context at all. But where it holds, the count on this page and the mapping a curator would actually want point at different nodes.

The record the schema requires

Written out in full, against the trigger node the join proposes. This block validates: schema, ontology terms, and the quoted snippet against the cached method paper.

experimental_models:
- name: TOX21_TRB_COA_Antagonist_Followup_ratio
  experimental_model_type: OTHER
  description: >-
    EPA ToxCast/Tox21 assay endpoint 2247. A cell-free TR-FRET assay in which a
    GST-tagged TR-beta ligand-binding domain, a terbium-labelled anti-GST
    antibody and a fluorescein-labelled SRC-2 coactivator peptide together
    report coactivator recruitment by the receptor, with the signal normalized
    to DMSO-only wells for 0% and T3 wells for 100% activity. EPA annotates the
    intended target as human THRB, the format as cell-free, the function type
    as a ratio and the signal direction as loss, so a hit is a chemical that
    reduces recruitment.
  organism:
    preferred_term: human
    term:
      id: NCBITaxon:9606
      label: Homo sapiens
  culture_system: Cell-free TR-FRET reaction; no cultured cells
  publication: PMID:31566444
  modeled_mechanisms:
  - target: THRB Dominant-Negative Receptor Formation
    relationship: MEASURES
    fidelity: LOW
    model_scale: MOLECULAR
    description: >-
      The endpoint reads transcriptional output of TR-beta in the same
      direction the node's mechanism produces, so a hit is informative about
      the receptor's coactivator-recruitment capacity.
    limitations: >-
      The assay reads a wild-type receptor ligand-binding domain whose
      coactivator recruitment has been reduced by a chemical. The node names a
      heterozygous missense allele whose product keeps DNA binding and RXR
      heterodimerization while failing to release corepressor. Target gene and
      signal direction agree; the mechanism does not.
    readouts:
    - name: TR-FRET 520:495 coactivator-recruitment emission ratio
      target: THRB Dominant-Negative Receptor Formation
      direction: DECREASED
      interpretation: >-
        Less coactivator peptide recruited to the receptor gives less resonance
        energy transfer and so a lower emission ratio. This is the quantity the
        endpoint reports, and the direction EPA annotates as a loss signal.
      evidence:
      - reference: PMID:31566444
        reference_title: "Limited Chemical Structural Diversity Found to Modulate Thyroid Hormone Receptor in the Tox21 Chemical Library."
        supports: SUPPORT
        evidence_source: IN_VITRO
        snippet: "A time-resolved fluorescence resonance energy transfer (TR-FRET) signal was indicative of coactivator recruitment"
        explanation: >-
          States that the measured TR-FRET signal is the coactivator-recruitment
          readout this entry records.
    divergences:
    - divergence_type: CAUSE_UNREPRESENTED
      materiality: INVALIDATING
      description: >-
        The node's lesion is a heterozygous missense THRB allele. There is no
        allele in this assay: the reduction in coactivator recruitment is
        imposed pharmacologically on a recombinant wild-type ligand-binding
        domain.
    - divergence_type: BOUNDARY_OMISSION
      materiality: INVALIDATING
      description: >-
        The node's mechanism requires a co-expressed wild-type receptor to
        compete with, thyroid hormone response elements to occupy and an
        NCoR/SMRT corepressor complex to retain. The assay contains one
        receptor ligand-binding domain, a coactivator peptide and an antibody:
        no second allele, no DNA and no corepressor.
    evidence:
    - reference: PMID:31566444
      reference_title: "Limited Chemical Structural Diversity Found to Modulate Thyroid Hormone Receptor in the Tox21 Chemical Library."
      supports: SUPPORT
      evidence_source: IN_VITRO
      snippet: "were used to determine TR activity in a cell-free functional assay"
      explanation: >-
        Establishes that the endpoint is a cell-free biochemical measurement of
        receptor activity rather than a cellular or organismal model.

Four things about that block are worth reading closely.

The vocabulary does fit, and it fits well. MEASURES says the assay reads the node without modelling the disease. CAUSE_UNREPRESENTED is defined as the lesion not being encoded, with "the mechanism is imposed phenomenologically instead of arising from the allele" — a taxonomy derived from the computational-model caveats already in the corpus, extending here unchanged. BOUNDARY_OMISSION carries the rest, and ExperimentalReadout takes the measured quantity — the 520:495 emission ratio — with its direction, exactly as it would for a readout of an organoid or an animal. #12858's claim that dismech already has the words for this holds up.

Both divergences come out INVALIDATING. Neither is a caveat on an otherwise usable link; each says on its own that the claim should not transfer. The dominant-negative mechanism is a claim about two alleles competing in one nucleus. No endpoint in the panel expresses a disease allele — ToxCast screens chemicals against wild-type receptors — so the third node in the chain, Dominant-Negative Inhibition of Wild-Type Receptor Transcription, is unreachable from any of THRB's 13 endpoints, cell-based or not.

The scale audit reports no gap, and that is not reassurance. The link's model_scale is MOLECULAR and the node's biological_scale is MOLECULAR, so just model-scale-audit sees nothing to flag. CLAUDE.md's instruction to read an aligned result as "no scale gap" and never as "good model" has a live instance here.

ExperimentalModel has little to say about this assay. The class describes a cultured system. #12858 proposes four correspondences onto it — organism, tissue, cellShortName and the citation PMID — and for this endpoint two of them carry nothing, because tissue and cellShortName are both NA. experimental_model_type is not among those four and has no value that fits either: a cell-free reaction falls to OTHER, which records nothing, and

12858 already flags that 481 of the panel's endpoints are biochemical or

cell-free — a different 481 from the candidate count in the report's own summary table, which happens to take the same value. Its sibling endpoint 2240, TOX21_TRB_BLA_Antagonist_Followup_ratio, is the same receptor in the same direction from the same paper, and is cell-based in HEK293T cells, so it fills all four and types as CELL_LINE. Two endpoints a curator would reach for together need different treatment — and the tissue the cell-based one records is kidney, which appears nowhere in this disease.

What it costs to say it twice

ModelMechanismLink.target resolves only within one disease file, and an ExperimentalModel is nested inside one Disease. So the record above lives in Generalized Resistance to Thyroid Hormone and nothing else in the repository knows the endpoint exists.

The paralogs make the cost concrete. THRB has 13 endpoints and THRA has 13, and four are the same four: TOX21_TR_LUC_GH3_Agonist, TOX21_TR_LUC_GH3_Antagonist and their two followups each name THRA and THRB together in rat GH3 pituitary cells, so a hit there does not say which paralog it acted through. Those two dismech entries exist to separate the paralogs — Generalized Resistance to Thyroid Hormone for THRB, and Resistance to Thyroid Hormone Alpha, whose own trigger node is THRA Dominant-Negative Receptor Variant. Writing a paralog-blind endpoint into both means two records that each look paralog-specific, with no shared identity, and nothing that could later correct them together.

Species compounds it. Of THRB's 13 endpoints, 11 name the human gene; two (ATG_zfTRb_XSP1 and ATG_zfTRb_XSP2) name zebrafish thrb while running in human cells, so they join to this node only because the upper-cased symbol collides.

Resistance to Thyroid Hormone Alpha already carries a real ExperimentalModel: primary erythroid progenitors from 11 genotyped RTH-alpha patients, linked PARTIALLY_RECAPITULATES at HIGH fidelity to the erythroid node. Put the ToxCast record beside it and the class is being asked to hold two different kinds of thing — a patient-derived culture carrying the patients' own variants, and a purified receptor domain with a chemical on it.

Nothing from this example is committed to kb/. The block above validates but every divergence on it is INVALIDATING, and whether a link like it belongs in dismech at all is the next deliverable's question, not this one's.

How this compares with the figures in #12858

The issue reported 170 targets on 368 nodes at commit fc510ecca0. This page reports 194 on 452. The two are not comparable figure for figure: the issue read genes[] alone, this count also reads gene and genetic_context, and the pathograph has been curated in between. The endpoint totals agree, at 1,570 endpoints and 1,108 with a usable gene target. The issue's 497 distinct targets are 496 here because FOS|JUN is split into two genes that the panel already names separately.

Regenerating

just toxcast-refresh                                 # cache the annotations; needs CTX_API_KEY
just toxcast-coverage                                # the full report, to stdout
just toxcast-coverage --json                         # the headline figures
just toxcast-coverage --format tsv --table targets   # one row per gene target
just toxcast-coverage --format tsv --table nodes     # one row per candidate node
just toxcast-coverage --format tsv --table endpoints # one row per assay endpoint
just toxcast-coverage --format tsv --table diseases  # one row per entry with a candidate node
just toxcast-coverage --out docs/reports/toxcast-pathograph-coverage-<date>.md

The annotations are cached under data/toxcast/ and gitignored; data/toxcast/MANIFEST.yaml records when they were retrieved. The coverage recipe is offline once they are cached, reports only, and exits 0. The epa-ctx-api skill covers the API itself and the ways the gene field misleads.

Still to come on this page

Issue #12858 asks for more than the measurement and the example above. Each of these is its own piece of work and none is started here: