Skip to content

Batch/lexical matching, OAK, SSSOM, gaps, ROBOT QC (part 4/9) - #18

Open
s28ale-maker wants to merge 7 commits into
pr-03-cl-issues-m1-m2from
pr-04-matching-sssom-robot
Open

s28ale-maker wants to merge 7 commits into
pr-03-cl-issues-m1-m2from
pr-04-matching-sssom-robot

Conversation

@s28ale-maker

Copy link
Copy Markdown
Collaborator

Part 4 of 9 in a stacked series (#15 → #16 → … ). Base is pr-03-cl-issues-m1-m2, so this diff shows only this part's 7 commits. Merge the earlier parts first; this PR then retargets toward main.

Summary

Commits 7862cc8..6005bbc (Milestone 4 / issue #6):

  • Batch and lexical CL matching with OR-group scoring (soulcap-match --batch [--lexical]).
  • OAK lexical and synonym search (soulcap-oak-match), SSSOM export (soulcap-sssom), and gap tracking (reports/gaps.tsv plus a mapping_gap issue template).
  • Candidate mappings: Monocyte family, Neutrophil, Eosinophil, B cell, and the T cell tree.
  • ROBOT QC CI together with its fixes: the setuptools pin, the SOULCAP: prefix registration, and the remaining ROBOT failures. They're grouped so this PR never shows a red ROBOT check.
  • Removed the CI job that merged our candidate mappings into CL and reasoned over them as if accepted. Mappings stay as SSSOM proposals for human review.

Test plan

Checked in isolation with the same steps as CI (uv sync --frozen --extra dev):

  • uv run pytest: 396 passed, 95.0% coverage
  • uv run ruff check ., ruff format --check ., and uv run mypy src/soulcap_cl_mapping pass
  • CI passes on this PR

🤖 Generated with Claude Code

s28ale-maker and others added 7 commits July 22, 2026 15:40
…idate mappings (issue #6)

- soulcap-match --batch scores every SOULCAP cell type against all CL terms
  in one pass; --lexical adds an independent OLS4 name-search candidate and
  writes a marker/lexical agreement report.
- Fix scoring: required-marker conflicts now disqualify a candidate instead
  of just subtracting points, so shared exclusion markers can no longer
  outrank a term that outright contradicts a required marker.
- Fix scoring: CL's "high"/"low" expression-level axioms are no longer
  treated as equivalent to "negative" (absent).
- Add extract_marker_clauses(): SOULCAP's `(A+|B+|C-)` OR-groups were being
  flattened into independently-required markers instead of "at least one
  must hold" — this silently suppressed good matches (e.g. CL:0000043
  mature basophil never scored for SOULCAP's Basophil profile). Clauses are
  now evaluated with proper OR semantics.
- Fix OLS4 lexical search: exact label matches were buried by OLS4's default
  relevance ranking; now promoted to rank 1 when present in a wider pool.
- Extend marker_map.py's PRO lookups (cytokines, MR1, TCR V-gene segments)
  via UniProt-accession-based PRO term fetch, closing most of the
  marker->gene mapping gaps tracked in issue #1.
- Add reports/candidate_cl_mappings.md (Milestone 4 draft mappings with
  rationale/evidence) and log a new CL gap (missing human-specific "group 1
  innate lymphoid cell" term, filed as issue #14).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- soulcap-oak-match: lexical/synonym CL search via OAK's sqlite:obo:cl
  adapter, an independent second opinion to soulcap-match --lexical (OLS4).
  Verified live: ranks exact label/synonym matches cleanly where OLS4's
  relevance ranking buries them behind subtype noise.
- soulcap-sssom: exports the curated Milestone 4 mapping decisions as SSSOM
  TSV instead of writing OWL axioms directly. confidence/comment are derived
  automatically from whether CL directly asserts the matched marker
  axiom(s), only has them by inference, or has none at all — not hand-typed.
- write_robot_template(): converts SSSOM rows into true logical OWL axioms
  (equivalentClass/subClassOf) for reasoner QC, since SSSOM's own OWL writer
  only emits skos:exactMatch as a non-logical annotation a reasoner ignores.
- reports/gaps.tsv + .github/ISSUE_TEMPLATE/mapping_gap.yml (gap label):
  tracks cases with no reasonable CL match, CL/marker conflicts, CL axiom
  gaps, and sheet data issues, separate from the mapping table.
- .github/workflows/robot-qc.yml: runs on every PR. One job audits upstream
  CL (cl-base.owl) standalone with robot report + robot reason (ELK); a
  second merges our SSSOM-derived mapping axioms into CL and re-checks, to
  catch problems our own proposed mappings would introduce.
- New skill: soulcap-cl-matching, encoding the cross-check workflow (never
  trust either method's rank-1 blindly, verify via direct OLS4 lookup when
  uncertain).
- Fix a pre-existing mypy gap in cl_match.py (_lexical_query_for_row typing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…pings

- 7 new mappings via the soulcap-cl-matching skill workflow, verified via
  OAK (soulcap-oak-match) after OLS4 REST search started timing out mid-run.
- Eosinophil -> CL:0000771 is a strong marker-confirmed match (CD193/CCR3
  directly asserted, not inferred, matching SOULCAP's defining marker).
- Monocyte subset terms (CL:0000860/0000875/0002393) are lexical-only
  matches; CL's own markers for them are already known-incomplete per repo
  issue #12, cited rather than re-flagged.
- Logged 4 new gaps.tsv entries: axiom gaps on the two more parent classes
  (Mono, B cell), and a malformed bracket shared across all four Monocyte
  rows' Required exclusion column (sheet data issue, not fixable here).
- Extended the SSSOM curated table with the same 7 entries.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y/naive tree)

- Covers 57 of 83 remaining T cell rows via the soulcap-cl-matching skill.
  Marker-axiom scoring was uniformly useless across this entire branch
  (top candidates were consistently unrelated myeloid/NK terms); every
  mapping is lexical, OAK-verified directly rather than trusted from the
  raw batch output, which was wrong or missing for a large fraction of
  this branch too.
- CD56+ T cell family (13 rows) ties back to repo issue #13: all Broad-match
  to CL:4052055, the term already flagged there for a pending rename to
  "CD56-positive T cell, human".
- iNKT -> CL:0000814, the other term from issue #13 (pending rename to
  "iNKT cell").
- Several real CL gaps found and grouped in gaps.tsv: CL's CD4/CD8
  vocabulary is alpha-beta-centric (no combined ab+gd, double-positive, or
  peripheral double-negative terms); no gamma-delta-specific or generic
  (non-CD4/CD8) differentiation-state terms; a likely CD8+-specific TEMRA
  term gap.
- 26 rows have NO Full Name in the sheet at all (Conv TCRab family,
  Th1/2/9/17-like, Tfh, Vg9/Vd1/Vd2/Vd3, etc.) -- not mapped, since
  inventing a name isn't this report's call. Also found what looks like a
  genuine sheet data bug: Th1-like/Th2-like/Th9-like/Th17-like each appear
  TWICE, one copy with markers that look copy-pasted from an unrelated
  iNKT row.
- SSSOM curated table extended with the 30 distinct (non-redundant) T cell
  mappings -> 80 total mappings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hon 3.11) and register SOULCAP: prefix in robot template

David's agentic review caught a real bug I'd missed: my local dev
environment runs Python 3.13, where uv.lock resolves eutils==0.6.1 (uses
importlib.metadata). CI's test matrix includes Python 3.11, where uv.lock
resolves the older eutils==0.6.0, which still does `import pkg_resources`
at module load -- and setuptools>=81 removed pkg_resources entirely,
so any import of oaklib (hence soulcap-oak-match) aborted pytest
collection outright on 3.11, running 0 tests.

Reproduced locally by installing Python 3.11 via uv and running the real
test suite against it (matching CI's exact failure). Fixed by pinning
setuptools<81 as a main dependency (not dev-only -- this also breaks the
real soulcap-oak-match CLI on 3.11, not just tests) and confirmed the fix
resolves cleanly under both 3.11 and the default 3.13 environment.

Also found and fixed a second real CI failure while investigating (ROBOT
ontology QC workflow, from actual run logs): `robot template` didn't know
the SOULCAP: prefix used by write_robot_template()'s output, causing
"UNKNOWN ENTITY ERROR could not interpret 'SOULCAP:NK'". Added
--prefix "SOULCAP: https://soulcap.org/cell_type/" to register it,
matching the CURIE_MAP already used in sssom_export.py.

Could not fully dry-run the ROBOT fix locally (no Java available in this
environment, and installing one hit a UAC prompt that can't be answered
non-interactively) -- the setuptools/eutils fix is fully verified against
a reproduced failure; the ROBOT prefix fix is a standard, well-documented
fix applied directly against the real CI error, but not further dry-run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The --prefix fix from the last commit got the merge step passing; robot
report on the merged ontology then surfaced two more real, verified issues
(pulled the actual merged-report.tsv artifact from the failed run rather
than guessing):

- ERROR duplicate_label: SOULCAP:Basophil_(PBMC) and SOULCAP:Basophil_(WB)
  both got the literal rdfs:label "Basophil", since build_robot_template_rows
  used subject_label (SOULCAP's own Full Name column, not guaranteed
  unique -- both preps share the same Full Name) instead of something
  derived from subject_id (always unique, by construction). Fixed by
  deriving the ROBOT template LABEL from subject_id instead. Added a
  regression test against the real CURATED_MAPPINGS table, not just a
  synthetic example.
- 3x ERROR missing_ontology_title/description/license: the merged.owl
  ontology declaration had no dc: metadata annotations. Added a chained
  `robot template ... annotate ...` step setting minimal title/description/
  license on the merged QC artifact.

Still can't fully dry-run locally (no Java in this environment) -- pushing
to let the real CI run verify, same as the last fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… over them

David flagged this as overstepping scope during review — proposing mappings
for human review is this repo's job, but unilaterally asserting them as true
OWL axioms and merging them into CL (even as an ephemeral CI artifact) is
not. Keep only the read-only upstream-CL audit job; drop the
ROBOT-template/OWL-axiom code path from sssom_export.py since nothing calls
it anymore.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant