Resolve free-text scientific labels to ontology term IDs and validate
existing CURIEs against the EBI Ontology Lookup Service (OLS4). Covers the
concepts whose identifiers are routinely invented rather than looked up:
tissue, cell type, disease, phenotype, assay, chemical, organism, sex, and
developmental stage.
Two stdlib-only scripts, one per direction:
resolve_terms.py text -> ID, escalating exact -> token -> fulltext and
labelling every hit exact_label / exact_synonym /
partial, so a fuzzy guess cannot pass as a match
validate_terms.py ID -> verdict (not_found, obsolete + replacement,
label_mismatch, wrong_ontology, wrong_branch), exiting
non-zero so it works as a CI gate on a metadata file
Behaviour verified against the live service, and the reason the skill ships
scripts rather than a recipe:
- exact=true is exact *token* matching, not exact label: "liver" returns 161
hits in UBERON, 1 once queryFields is restricted to label
- /search never returns is_obsolete or term_replaced_by, even when they are
named in fieldList, so only term detail can answer whether an ID is current
- ontology=efo returns MONDO and CL hits, because ontologies import each other
- the obo_id index has holes: MONDO:0000001 is live and defined by MONDO but
unindexed, so an IRI fallback is needed to avoid a false not_found
- IRIs are not all OBO PURLs; EFO and Orphanet use their own namespaces
- OxO is retired and serves HTML with HTTP 200
- a branch check does not exclude cell types from anatomy, since CARO places
cell under anatomical structure
Tests: 59 in tests/ontology-term-resolution, 53 offline with stubbed network
plus 6 live smoke tests gated behind OLS_LIVE_TESTS=1 that pin the API
behaviour above.