Accession verified two ways before starting: projects/paint/human-no-IBA-simple.csv:6472
gives human,Q8N7X0,ADGB, and UniProt returns Q8N7X0 / ADGB_HUMAN / Androglobin / 1667 aa /
reviewed. The four unreviewed human ADGB entries (A0A7P0T963 1667 aa, E5RGD1 220 aa,
E5RIM8 35 aa, H0YC38 71 aa) are TrEMBL fragments and are not used anywhere.
wc -l ADGB-goa.tsv -> 14 (13 data rows + header)
tail -n +2 ... | sort -u | wc -> 13 distinct
grep -c '^- term:' stub -> 13
13 = 13 = 13. No collapse, no duplicate TSV lines. Every GOA row has its own entry. The
GOAValidator.seed_missing_annotations key (GO ID, evidence_type, reference, negated,
qualifier) happens to be unique across this gene's rows, so the known under-seeding defect
does not bite here. Nothing was restored; nothing was merged.
The 13 rows come from exactly four pipelines and, ultimately, from one donor organism:
| pipeline | reference | rows |
|---|---|---|
| PAINT phylogenetic (IBA) | GO_REF:0000033 | 3 |
| InterPro2GO (IEA) | GO_REF:0000002 | 4 |
| Swiss-Prot SubCell (IEA) | GO_REF:0000044 | 1 |
| Ensembl Compara ortholog transfer (IEA/ECO:0000265) | GO_REF:0000107 | 5 |
There is not one experimental annotation on human ADGB. Given PE 1: Evidence at protein
level and a decade of published human biochemistry, that is an annotation gap, not an
absence of data — see "The real gap" below.
UniProt annotates, on the 1667-aa chain: a Calpain catalytic domain 70–411, a globin
domain split in two by circular permutation (C-terminal part 763–890, N-terminal part
936–968) with an IQ calmodulin-binding motif 906–935 between the halves, and a
coiled coil at 1588–1629. Heme-iron ligands are annotated at Q792 (distal) and
H824 (proximal).
PMID:22115833 and PMID:22115833
This is the setup for two opposite errors, and I tested both rather than assuming either.
They came out asymmetric: the calpain half fails, the globin half survives.
Reproducible analysis: ADGB-bioinformatics/adgb_sites.py, results in
ADGB-bioinformatics/RESULTS.md (regenerated by the script; a fresh run byte-matches the
committed file).
Method: align each of six reviewed reference calpains' catalytic domains to ADGB 70–411
under four substitution/gap models, and read the ADGB residue in each column anchored on
the reference's own UniProt Active site feature. Per-residue, not per-triad — a single
model's triad count is an alignment artefact at these identities (18–29%).
[file:human/ADGB/ADGB-bioinformatics/RESULTS.md "A papain-fold cysteine protease cannot catalyse without the general-base histidine that deprotonates the nucleophile. A retained Cys with no His and no Asn is a fold, not an active site."]
Caveat I must state rather than hide: ADGB carries zero UniProt Active site
features, so the lands_on_subject_annotated_site condition (the load-bearing second
condition from the AADACL2 rule) is vacuous in this half of the analysis and its all-"no"
column is not a measurement. That absence is itself the signal: UniProt annotates the
Calpain catalytic domain on ADGB but propagates no catalytic residues into it, while all six
reference calpains carry three each. The condition is live in Test 2.
Two independent lines agree, neither of which depends on my alignment:
CAUTION:ECO:0000305|PubMed:22115833), while the SIMILARITY line still places itC02.001/.002/.004/.018/.029/.031,C02.972. I did not assume the MEROPS numbering convention — the suffixes areSo GO:0004198 calcium-dependent cysteine-type endopeptidase activity (IEA from
InterPro:IPR001300) is domain-name-propagated-into-activity, and it is a REMOVE that
has been earned rather than asserted.
PMID:35700329
and UniProt records
[file:human/ADGB/ADGB-uniprot.txt "Interacts with septin SEPT10; contributes to in vitro"]
[file:human/ADGB/ADGB-uniprot.txt "proteolytic cleavage of SEPT10 in a calmodulin-dependent manner."]
Read past the abstract, though, and the authors are careful about exactly what was shown:
PMID:35700329
The assay is co-overexpression of both proteins in HEK293 cells. The perturbations were a
whole-IQ deletion and a whole-protease-domain deletion — not a catalytic-residue point
mutant, which is what would demonstrate catalysis, and which there is no confident triad to
build. No purified-protein cleavage assay, no protease inhibitor. The word "catalytic" does
not occur in the paper.
That is why GO:0006508 proteolysis is MARK_AS_OVER_ANNOTATED and not REMOVE: the route
that produced it (a catalytic signature ADGB does not fulfil) is unsound, but the conclusion
is not refuted, and it may yet be right by a different mechanism — ADGB targeting SEPT10 to a
different protease, for instance.
Here ADGB does have its own annotated sites, so the annotated-site condition discriminates.
Sequence identity does not predict alignment register here — CYGB at 18.0% is on-register for
both sites while HBB at 20.3% is on-register for neither, and HBB's apparent "distal His
match" (H830) lands on a histidine UniProt does not annotate. The annotated-site condition is
what separates them; residue identity alone would have manufactured a false conservation call.
Each paper numbers its own construct differently, so I pinned the register rather than
trusting the labels. Nie et al. report His62 proximal and residue 30 distal; UniProt
reports H824 and Q792. 824 − 792 = 32 = 62 − 30. Their construct therefore begins at
UniProt 763 — which is exactly the start of UniProt's annotated Globin; C-terminal part
domain — and their Cys3/Cys25 disulfide maps to real cysteines C765/C787. The mapping is
independently consistent at four positions.
Note a trap inside PMID:39719941: its Results body text says "Asn30 in helix E is
located in the heme distal position", while the figure legend of the same figure and the
concluding sentence of the same paragraph both say Gln30. The sequence settles it — UniProt
position 792 is a Gln, read directly from the Swiss-Prot sequence. Quoting the body sentence
alone would have propagated a typo.
The FUNCTION line says ADGB is a
[file:human/ADGB/ADGB-uniprot.txt "Probable chimeric globin with a bis-histidyl six-coordinate"]
heme-iron protein — but bis-histidyl requires two histidines, and UniProt's own feature
table annotates the distal ligand as Gln792. Three independent primary papers say Gln. The
FUNCTION prose predates the 2024–2026 biochemistry; the feature table is right.
GO:0019825 oxygen binding wrong? No, and this is where I nearly made the mirror error.The tempting move is to knock the oxygen term down: ADGB is a hexacoordinate globin whose
distal site is occupied, autoxidation is extreme, and the primary paper says so outright:
PMID:38725499
But read the term's definition, not its label. GO:0019825 is defined as, in full,
"Binding to oxygen (O2)." It asserts nothing about transport, delivery or physiological role.
And the same paper did observe an oxy complex and measure its association rate constant
(~0.8 × 10⁶ M⁻¹ s⁻¹; the oxyform is observable transiently by stopped-flow). So the term is
true as chemistry and demonstrated, and the correct action is KEEP_AS_NON_CORE, not
removal.
What the autoxidation data do refute are the physiological terms that GOA does not
carry: GO:0005344 oxygen carrier activity ("Binding to oxygen and delivering it to an
acceptor molecule or a specific location") and GO:0019826 oxygen sensor activity. Neither is
annotated, neither should be, and the "except for the possibility of oxygen sensing" clause is
an authors' hedge, not evidence for the second.
The measured chemistry points elsewhere:
PMID:38725499
PMID:38725499 — unusual in the globin
superfamily and characteristic of NO-storage hemoproteins — and
PMID:38725499
Nitrite reductase activity is replicated by a second, independent laboratory:
PMID:39719941
(kcat/KM 0.046 → 0.102 mM⁻¹ s⁻¹ on CaM binding), and a third paper mutates the distal Gln and
finds Q12Y further enhances it PMID:42372363. Honest caveat, from the authors themselves:
PMID:39719941 — every
measurement is on the isolated recombinant globin domain, not the full-length protein.
WITH/FROM on the three IBA rows is MGI:MGI:3605549|PANTHER:PTN002922607 (the two CC rows)
and MGI:MGI:3605549|PANTHER:PTN002922608 (the BP row).
MGI:MGI:3605549 resolves to G3UZ78 = ADGB_MOUSE, Swiss-Prot reviewed, 1657 aa — thePTHR46298PTHR46298:SF1. (The family's cached total is 1670 proteins| propagated to human | donor's own evidence | reference |
|---|---|---|
| GO:0007286 spermatid development | IMP | PMID:36995441 |
| GO:0097225 sperm midpiece | IDA | PMID:35700329 |
| GO:0097227 sperm annulus | IDA | PMID:35700329 |
No downward MODIFY is warranted, and no upward over-reach occurred.
- Complex-projection check (the ACTR8 test): negative, in the benign direction.
PMID:35700329 annotates 8 annotations over 2 distinct entities (mouse Adgb and mouse
Sept10) and PMID:36995441 5 over 3 (Adgb, Cfap69, Spef2) — both fully returned, not
paginated. Critically, the functional/phenotype terms stay on the perturbed gene:
GO:0007283 IMP and GO:0007286 IMP are on Adgb alone. The terms that spread are
GO:0005515 (which is what IPI means) and co-localisation CC rows. This is parallel
per-protein curation, not a complex-level projection.
GO:0007286, GO:0097225 and GO:0097227 are each annotated twice on human ADGB — once
by PAINT (IBA) and once by Ensembl Compara (GO_REF:0000107, ECO:0000265) — and all six rows,
plus the two Compara-only rows (GO:0007283, GO:0036126), trace to the same mouse Adgb
experimental annotations from the same two papers. That is 8 of 13 rows resting on two
publications and one ortholog. Not an error, and I have not marked it as one, but anyone
counting independent support for this gene should know the effective N.
Pharos: Tdark, and human ADGB has zero experimental GO annotations — yet:
Three MF terms are therefore proposed as NEW: GO:0005516 calmodulin binding,
GO:0070026 nitric oxide binding, GO:0098809 nitrite reductase activity. GOA carries no
calmodulin-binding annotation for ADGB in either species, which is the clearest miss.
GO:0050421 nitrite reductase (NO-forming) activity was checked and rejected: its
definition is cytochrome-c-specific ("nitric oxide + Fe(III)-[cytochrome c] + H2O = ..."),
i.e. the cd1-type enzyme, not a deoxyferrous globin reducing nitrite. GO:0098809 is the
correct level. Read the definition, not the label.
The GO:0031514 motile cilium IEA comes from UniProtKB-SubCell:SL-0117 ("Flagellum"), whose
own GO mapping is GO:0031514. It is independently well supported, and not only in sperm:
PMID:33453283 and
PMID:33453283;
in Tetrahymena ADGB is a central-apparatus subunit
PMID:34083607,
though notably PMID:34083607 — a
useful negative that argues ADGB is not a core motility component of the projection.
gates_passed: True, faith_pct 100.0, 9 citations, all numeric PMIDs (no PMID:bio_*
preprint ids), all nine resolving to the correct papers by title. Retraction / erratum /
expression-of-concern check run over all nine via CommentsCorrections/RefType on each
article's own record plus a Crossref updated-by lookup for the null-PMID corrigendum
case: all clean (the checker was break-tested against the campaign's three known positive
controls and against a clean control; the one Crossref 404, on the 2026 Chinese-language
paper, is reported rather than silently swallowed).
But affinage's recall was the problem, not its precision. A PubMed search for
androglobin OR ADGB[tiab] returns 59 records; affinage cited 9 and missed the two papers
that decide this review:
PMID:38725499 (Chem Sci 2024) — the only full biophysical characterisation of thePMID:42372363 (Spectrochim Acta 2026) — names and mutates the distal Gln in its title.It also missed PMID:33453283 (the FOXJ1/ciliogenesis primary paper — affinage cited the
secondary PMID:37158461 instead) and PMID:38786048. Lesson for the brief: a provider
record that passes its gates can still have a recall failure that inverts a verdict. Here,
relying only on affinage's set, GO:0019825 oxygen binding had no experimental anchor either
way and the distal-Gln finding was invisible.
| # | term | evidence | action |
|---|---|---|---|
| 1 | GO:0007286 spermatid development | IBA | ACCEPT |
| 2 | GO:0097225 sperm midpiece | IBA | ACCEPT |
| 3 | GO:0097227 sperm annulus | IBA | ACCEPT |
| 4 | GO:0004198 Ca-dependent Cys endopeptidase | IEA InterPro | REMOVE |
| 5 | GO:0006508 proteolysis | IEA InterPro | MARK_AS_OVER_ANNOTATED |
| 6 | GO:0019825 oxygen binding | IEA InterPro | KEEP_AS_NON_CORE |
| 7 | GO:0020037 heme binding | IEA InterPro | ACCEPT (core) |
| 8 | GO:0031514 motile cilium | IEA SubCell | ACCEPT |
| 9 | GO:0007283 spermatogenesis | IEA Compara | ACCEPT |
| 10 | GO:0007286 spermatid development | IEA Compara | ACCEPT |
| 11 | GO:0036126 sperm flagellum | IEA Compara | ACCEPT |
| 12 | GO:0097225 sperm midpiece | IEA Compara | ACCEPT |
| 13 | GO:0097227 sperm annulus | IEA Compara | ACCEPT |
| + | GO:0005516 calmodulin binding | IPI | NEW |
| + | GO:0070026 nitric oxide binding | IDA | NEW |
| + | GO:0098809 nitrite reductase activity | IDA | NEW |
Queried QuickGO for what the four characterised human globins already carry, because a term
choice defended only from definitions is weaker than one that also matches what curators do.
The PR reviewer's fair objection to the first version of this table: it was pasted from an
ad-hoc query and so unverifiable from the checkout, since the comparator genes are not in this
repo. It is now generated by ADGB-bioinformatics/globin_term_precedent.py, which additionally
asserts the three claims below, so a change at QuickGO fails loudly rather than leaving
stale numbers sitting in prose. Regenerate and diff with
uv run python globin_term_precedent.py --check-notes.
| GO:0019825 O2 binding | GO:0020037 heme | GO:0098809 nitrite reductase | |
|---|---|---|---|
| neuroglobin Q9NPG2 | IBA + IDA + IEA | IEA | IDA |
| cytoglobin Q8WWM9 | IDA + IEA | IBA + IEA | IDA |
| myoglobin P02144 | IEA | IEA | IBA + IDA |
| haemoglobin alpha P69905 | IEA | IBA + IEA | - |
Three consequences, each of which strengthens a call I had made on other grounds:
GO:0019825 KEEP is precedented, not idiosyncratic. Neuroglobin and cytoglobin areGO:0098809 is the term GOA actually uses for globin nitrite reductase activity - allGO:0050421GO:0070026 is precedented for this exact chemistry. The five human holders, withNull part of the same survey, recorded rather than dropped: no individual monomeric globin
carries GO:0070026; only the haemoglobin complex does. So ADGB would be the first
single-chain globin with the term. That is a consequence of how unusual its NO chemistry is
rather than a reason to withhold it, but it should be visible to whoever curates it.
ROLE_CONFLATION between the protease and globin halves — not found as aart.iter("ArticleId"), whichPubmedData/ArticleIdList; the discrepancy was the bugUNPARSED for CAPN1/2/3 because theirjust validate silently de-duplicated cache/go/terms.csv. Computing the change asGO:0097227, genuinely new — origin/mainGO:0001675 andGO:0009566. Those are exactly the two pre-existing duplicates the campaign brief says togit checkout HEAD -- cache/go/terms.csv (the merge base, notorigin/main, which has since moved and would have imported four rows from siblingjust validate and+/--heavy diff, because every reordered line appears as both..claude/hooks/validate_ai_review_pretool_hook.py computesproject_root from its own file location, so it validated my YAML against the sessiongenes/human/ADGB/ entirely, and holds different publications/ copies — severalGO:0006508 and GO:0019825 calls rest on. The file was therefore written via a stagingjust validate human ADGBcheckquotes.py (72 quotes, 0 problems) and the committed audit script.2ee73035c was pushed directly to paint/ADGB by the repository owner while I was preparing my
own fix for the same two points, and it is the better wording, so it stands unchanged. It
resolved the reviewer's blocking item (GO:0007283 ACCEPT -> KEEP_AS_NON_CORE, because the
row's own summary already described the term as non-core while ActionEnum defines ACCEPT as
retaining a core function) and the reviewer's second non-blocking item (hedging the flat
"has nitrite reductase activity" in description to the isolated-domain, millimolar-K_M caveat
the annotation row already carried).
I verified the reviewer's blocking point independently before conceding, and it is exactly
scoped: GO:0007283 was the only ACCEPT row whose term is absent from core_functions,
and the converse check is also clean - no non-ACCEPT row holds a term that core_functions
uses.
That invariant is now genuinely enforced, and the first version of this sentence was false.
Round 3 of the PR review caught it: I had written "enforced rather than checked by eye" while
the check itself lived only in a throwaway patch script under /tmp, which ran once and was
never committed. audit_adgb_review.py had not been modified since the first commit and
contained no occurrence of core_functions at all. The verification had genuinely happened -
the scoping result I reported was correct - but a verification you performed is not a
verification that exists, and a durable artifact was left claiming otherwise.
It is now check F in audit_adgb_review.py, in both directions: every ACCEPT row's term must
appear in core_functions, and every core_functions term must be backed by a row whose action
is ACCEPT or NEW. The reverse direction is the one that would otherwise have gone unwritten,
and unwritten is not the same as passing. Both are break-tested, plus a third mutation for a
core_functions term with no annotation row at all.
The decisive check is not the self-test but a regression test against history: running check F
against the YAML at commit 6ff257b6f - the version the reviewer blocked - fires on exactly
GO:0007283, and against the current file it is clean. That is what makes the enforcement claim
checkable rather than another assertion.
Rule, from three genes in one night: when you write that something is enforced, the same push
must contain the enforcement, and you confirm it by grepping the script - not by remembering
that you ran the check.
Process lesson for the campaign brief. My worktree's HEAD had silently advanced to the
maintainer's commit. I discovered it only because a patch script asserted its anchor was
present and refused to run when it was not - the string it expected had already been
rewritten. Without that assertion the edit would have been a silent no-op, or worse, a
replace() on stale text that reverted someone else's fix. This is the anchor-assertion rule
paying off against a case it was not written for: it was written to catch my drift, and it
caught someone else's concurrent commit. Before any fix round, re-read the file from disk and
git fetch; do not assume your branch is where you left it.