diff --git a/.claude/skills/pro-marker-species-support/SKILL.md b/.claude/skills/pro-marker-species-support/SKILL.md new file mode 100644 index 0000000..ce4805b --- /dev/null +++ b/.claude/skills/pro-marker-species-support/SKILL.md @@ -0,0 +1,151 @@ +--- +name: pro-marker-species-support +description: Check whether a CL PRO marker asserted on a cell type is actually validated in the literature as human, mouse, or both, and record the verdict in reports/pro_marker_species_support.tsv with a verbatim-quoted source. Invoke when asked to find literature support (or the lack of it) for the species-specificity of CL's general PRO markers, or to extend/refresh reports/pro_marker_species_support.tsv (issue #11). +--- + +You check whether a CL PRO marker that's asserted on a cell type *without* +stating a species applies to human, mouse, or both — grounded in real, +verbatim-quoted literature, never inferred from the marker name alone. This +is issue #11's workflow, proven across all 77 in-scope pairs in one run: 59 +resolved to `human` (mostly by reusing already-vetted M2 literature quotes), +18 honestly recorded as `insufficient_evidence` rather than forced. See +[CLAUDE.md](../../../CLAUDE.md) for repo conventions and +[reports/pro_marker_species_support.tsv](../../../reports/pro_marker_species_support.tsv) +for the current output. + +## When to invoke + +The user asks to find literature support for a CL PRO marker's species +applicability, work through issue #11, or extend/refresh +`reports/pro_marker_species_support.tsv` after new SOULCAP rows or CL marker +axioms are added. + +## Tools + +- `soulcap_cl_mapping.pro_species_support.get_scoped_pairs(tsv_path, curated_cl_ids)` + / `group_by_cell(pairs)` — scopes the question to (CL cell type, PRO marker) + pairs that actually matter: `asserted=True` rows in + `reports/cl_pro_relationships.tsv`, restricted to the CL IDs in + `CURATED_MAPPINGS` (`sssom_export.py`), excluding markers whose `pr_label` + already carries a species qualifier (`(mouse)`/`(human)`). Run via + `uv run python -c "..."` or a short driver script — not a CLI (deliberately; + see the module docstring). +- `reports/literature/*.md` — the Milestone 2 literature reports. **Check + these first, always.** They already contain verbatim-quoted, hook-validated + evidence for 7 cell families (NK, ILC, DC, iNKT, T cell, B cell, gdT/MAIT), + and reusing a quote from here is strictly better than a fresh search: it's + already vetted, and it's free. +- `data/citation_mgr.csv` (synced Citation Mgr tab) — per-family seed papers + for families without an M2 report yet (as of this run: basophil, + granulocyte, mast cell, monocyte, neutrophil, MDSC). +- The `citation-traversal` skill — use when the Asta MCP server is reachable + and a family has no M2 coverage. Seed round 1 from `data/citation_mgr.csv`. +- `soulcap_cl_mapping.europepmc_search.search(query, page_size=...)` / + `uv run soulcap-europepmc ""` — free, keyless fallback when Asta + isn't reachable (this was the case for the whole first run — check + `.mcp.json`/`/mcp` before assuming Asta is down). Single-round only, no + citation-graph following. Prefer a targeted query naming an already-known + canonical paper (e.g. by PMID via `EXT_ID: AND SRC:MED`) over blind + free-text search — free-text search on general marker+cell-type queries + reliably surfaces recent tangential disease-cohort papers, not clean + definitional statements. If this repo already cites a canonical paper for + the cell family elsewhere (e.g. `reports/cl_term_issues.md`, + `reports/gaps.tsv`), fetch that paper by PMID first. + +## Step 1 — scope the pairs + +```bash +uv run python -c " +from soulcap_cl_mapping.pro_species_support import get_scoped_pairs, group_by_cell, DEFAULT_TSV +from soulcap_cl_mapping.sssom_export import CURATED_MAPPINGS +ids = {m['cl_id'] for m in CURATED_MAPPINGS} +pairs = get_scoped_pairs(DEFAULT_TSV, ids) +print(len(pairs), 'pairs across', len(group_by_cell(pairs)), 'CL cell types') +" +``` + +Diff this against the CL IDs/PR markers already in +`reports/pro_marker_species_support.tsv` to find only the *new* pairs when +refreshing rather than re-running everything. + +## Step 2 — find evidence, in priority order + +For each pair (or each CL cell type's group of pairs, sharing a seed set): + +1. **Search the relevant `reports/literature/*.md` file** for the marker's + CD synonym or PR label. If a verbatim blockquote already covers it, reuse + that quote and citation directly — `evidence_source: m2_literature_report`. +2. **If Asta is reachable and no M2 coverage exists**, run `citation-traversal` + seeded from `data/citation_mgr.csv`'s row for that family — + `evidence_source: citation_traversal`. +3. **If Asta isn't reachable**, use `soulcap-europepmc` — prefer fetching a + specific known-good paper by PMID over free-text search (see Tools above) + — `evidence_source: europepmc_search`. +4. **If nothing turns up via any route**, record + `species_support: insufficient_evidence`, `evidence_source: none`, and + write a one-line note in `source_ref` naming exactly what was tried (which + file was checked, which query was run) so the gap is reproducible, not + just asserted. This is a real, useful finding — the monocyte subset panel + (CD192/CD19/CD3/CD20/CX3CR1 across all three monocyte types) came back + this way in the first run, which is itself worth knowing. + +## Step 3 — verify every quote before writing + +Never write a quote you haven't confirmed is a **literal substring** of its +source — the M2 markdown file's raw text, or the freshly fetched Europe PMC +abstract (`europepmc_search.search()` returns the `abstract` field; re-fetch +it, don't rely on a paraphrase from earlier in the conversation). Do this +with a small check before writing the row, e.g.: + +```python +assert quote in source_text, "not verbatim" +``` + +Watch for smart quotes (`"…"` vs `"…"`), en/em-dashes vs hyphens, and +markdown line-wrapping (`"...marked\n> by CLEC9A"` in the source should +collapse to one line — `.replace("\n> ", " ")` — in the TSV field, not be +written with an embedded newline, which breaks simple line-based TSV +tooling even though it's technically valid CSV-quoted). + +There is no PreToolUse hook enforcing this for this report (unlike +`citation-traversal`'s `validate_report_quotes.py`, which is scoped to +`reports/citation_traversal/*/report.md` only) — the verification is your +own discipline here. + +## Step 4 — write the row + +Append to `reports/pro_marker_species_support.tsv` +(tab-separated, one row per pair): + +| Column | Meaning | +|---|---| +| `cl_id`, `cl_label`, `pr_id`, `pr_label`, `cd_synonym` | From `get_scoped_pairs()` — don't hand-retype these, pull them programmatically to avoid transcription errors (a real bug caught during the first run: hand-typed PR labels didn't match `cl_pro_relationships.tsv`). | +| `species_support` | `human` / `mouse` / `both` / `insufficient_evidence`. | +| `evidence_source` | `m2_literature_report` / `citation_traversal` / `europepmc_search` / `none`. | +| `citation` | Author/year/PMID/DOI, plus a trailing `-- ` caveat clause when the quote's context doesn't exactly match the pair's cell type/subset (e.g. a naive-T marker's only evidence coming from a Treg paper) — say so rather than let the row imply a cleaner match than it is. | +| `quote` | The verbatim substring itself. | +| `source_ref` | `reports/literature/.md` for M2 reuse, an EuropePMC article URL for a fresh search, or `reports/citation_traversal//` for a traversal — or, for `insufficient_evidence`, a one-line note on what was tried. | + +## Step 5 — flag real findings + +If a `species_support` result is genuinely surprising (e.g. a marker CL +implicitly treats as pan-species turns out to only have mouse evidence, or a +whole marker cluster comes back `insufficient_evidence` the way monocyte +subset markers did), log it the same way other literature/CL gaps from this +project are handled: `reports/gaps.tsv` / `reports/cl_term_issues.md`, +following the existing entry format — not a new mechanism. + +## Known limitations + +- Europe PMC search is single-round, no citation-graph following — it will + miss evidence that only exists in a paper the seed doesn't directly cite. + Prefer M2 reuse or `citation-traversal` (when Asta is up) first. +- The scoping in Step 1 only covers `asserted=True` rows already in + `reports/cl_pro_relationships.tsv`, restricted to CL IDs SOULCAP already + maps to. A correct answer for a CL term outside `CURATED_MAPPINGS`, or a + marker CL hasn't asserted at all yet, is structurally out of scope here — + that's a `cl_term_issues.md`/`gaps.tsv` matter, not this skill's job. +- `insufficient_evidence` means "not found via the routes tried," not "does + not exist" — a paywalled paper or a full-text-only mention (abstract + didn't have it) can still produce a false negative. Note what was tried so + a future pass with different access can pick up where this left off. diff --git a/CLAUDE.md b/CLAUDE.md index 4ceec29..5a26548 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,10 +45,10 @@ See [README.md](README.md) for the full overview and setup. | [ROADMAP.md](ROADMAP.md) | Planned work / milestones and their deliverables and dependencies. | | [Notes.md](Notes.md) | Scratch notes on data sources + a marker-syntax quick reference. | | [MARKER_SYNTAX.md](MARKER_SYNTAX.md) | **Canonical spec** of the marker expression language (human-readable guide + EBNF). Cite this for anything parsing/validating marker strings. | -| [reports/](reports/) | Analysis reports. `marker_string_issues.md` is the curated review; `marker_validation.md` is **auto-generated** by the EBNF validator on each sync. `cl_pro_relationships.md`/`.tsv` are **auto-generated** by `soulcap-cl-pro` from Ubergraph. `cl_term_issues.md` tracks proposed CL corrections found during mapping work. `candidate_cl_mappings.md` is the Milestone 4 deliverable — proposed SOULCAP→CL mappings with rationale and evidence. `candidate_cl_mappings_batch.tsv`/`candidate_cl_mappings_agreement.tsv` are **auto-generated** by `soulcap-match --batch [--lexical]` (issue #6) — a marker-axiom-only pass and a marker/lexical agreement pass across every SOULCAP cell type; treat as an unreviewed draft shortlist (conflicts must be checked), not curated output. `candidate_cl_mappings.sssom.tsv` is **auto-generated** by `soulcap-sssom` from the curated `CURATED_MAPPINGS` table in `sssom_export.py` (kept in sync by hand with `candidate_cl_mappings.md`) — standard SSSOM format, with `confidence`/`comment` derived from whether the match is backed by a directly-asserted CL marker axiom, an inferred-only one, or none at all (lexical/name match only). `gaps.tsv` is a curated, hand-maintained log of cases where SOULCAP has no reasonable CL match, CL conflicts with a marker panel, CL has an axiom gap, or the sheet itself has a data problem — separate from the mapping table; matching GitHub issues use the `gap` label (`.github/ISSUE_TEMPLATE/mapping_gap.yml`). New reports go here. `reports/citation_traversal/` holds gitignored, regenerable snippet caches + summaries from the `citation-traversal` skill. | -| `src/soulcap_cl_mapping/` | All Python code. `sync_sheets.py` → `soulcap-sync`; `marker_syntax.py` → `soulcap-validate` (EBNF validator); `snippet_cache.py` → `soulcap-cache`; `report_validator.py` → `soulcap-validate-report`; `cl_pro.py` → `soulcap-cl-pro` (CL→PR relationships via Ubergraph SPARQL); `cl_match.py` → `soulcap-match` (marker-axiom + lexical CL candidate scoring, single-profile or `--batch`); `oak_match.py` → `soulcap-oak-match` (OAK sqlite-backed lexical/synonym CL search — downloads/caches a local CL database on first use, ~100MB); `sssom_export.py` → `soulcap-sssom` (curated mappings → SSSOM TSV, with confidence derived from CL marker-axiom assertion status). | +| [reports/](reports/) | Analysis reports. `marker_string_issues.md` is the curated review; `marker_validation.md` is **auto-generated** by the EBNF validator on each sync. `cl_pro_relationships.md`/`.tsv` are **auto-generated** by `soulcap-cl-pro` from Ubergraph. `cl_term_issues.md` tracks proposed CL corrections found during mapping work. `candidate_cl_mappings.md` is the Milestone 4 deliverable — proposed SOULCAP→CL mappings with rationale and evidence. `candidate_cl_mappings_batch.tsv`/`candidate_cl_mappings_agreement.tsv` are **auto-generated** by `soulcap-match --batch [--lexical]` (issue #6) — a marker-axiom-only pass and a marker/lexical agreement pass across every SOULCAP cell type; treat as an unreviewed draft shortlist (conflicts must be checked), not curated output. `candidate_cl_mappings.sssom.tsv` is **auto-generated** by `soulcap-sssom` from the curated `CURATED_MAPPINGS` table in `sssom_export.py` (kept in sync by hand with `candidate_cl_mappings.md`) — standard SSSOM format, with `confidence`/`comment` derived from whether the match is backed by a directly-asserted CL marker axiom, an inferred-only one, or none at all (lexical/name match only). `gaps.tsv` is a curated, hand-maintained log of cases where SOULCAP has no reasonable CL match, CL conflicts with a marker panel, CL has an axiom gap, or the sheet itself has a data problem — separate from the mapping table; matching GitHub issues use the `gap` label (`.github/ISSUE_TEMPLATE/mapping_gap.yml`). `pro_marker_species_support.tsv` is the issue #11 deliverable — for each (CL cell type, general PRO marker) pair SOULCAP's mappings actually touch, whether literature supports the marker as human, mouse, or both, or `insufficient_evidence`; built by the `pro-marker-species-support` skill, every quote verified verbatim against its source before writing. New reports go here. `reports/citation_traversal/` holds gitignored, regenerable snippet caches + summaries from the `citation-traversal` skill. | +| `src/soulcap_cl_mapping/` | All Python code. `sync_sheets.py` → `soulcap-sync`; `marker_syntax.py` → `soulcap-validate` (EBNF validator); `snippet_cache.py` → `soulcap-cache`; `report_validator.py` → `soulcap-validate-report`; `cl_pro.py` → `soulcap-cl-pro` (CL→PR relationships via Ubergraph SPARQL); `cl_match.py` → `soulcap-match` (marker-axiom + lexical CL candidate scoring, single-profile or `--batch`); `oak_match.py` → `soulcap-oak-match` (OAK sqlite-backed lexical/synonym CL search — downloads/caches a local CL database on first use, ~100MB); `sssom_export.py` → `soulcap-sssom` (curated mappings → SSSOM TSV, with confidence derived from CL marker-axiom assertion status); `pro_species_support.py` (scopes CL PRO markers needing species-specificity literature support — no CLI, called as a library from the `pro-marker-species-support` skill); `europepmc_search.py` → `soulcap-europepmc` (free, keyless Europe PMC search — fallback literature source when the Asta MCP server isn't reachable). | | `tests/` | Unit tests (mirror `src/` layout). | -| `.claude/skills/` | Project skills — see [citation-traversal](.claude/skills/citation-traversal/SKILL.md) and `ontology-term-lookup`. | +| `.claude/skills/` | Project skills — see [citation-traversal](.claude/skills/citation-traversal/SKILL.md), `ontology-term-lookup`, and [pro-marker-species-support](.claude/skills/pro-marker-species-support/SKILL.md). | | `.claude/hooks/` | Claude Code hooks. `validate_report_quotes.py` is a PreToolUse guard that blocks writing a citation-traversal report whose quotes aren't verbatim in the snippet cache. | | `data/` | Gitignored cache of the synced sheet (CSV + xlsx). | | `pyproject.toml` | Project metadata, deps, and the CLI entry points. | @@ -118,6 +118,13 @@ exact ontology labels via OLS4 — prefer it for term resolution. written into `reports/candidate_cl_mappings.md` with rationale and evidence. The Milestone 4 workflow. See [SKILL.md](.claude/skills/soulcap-cl-matching/SKILL.md). +- **`pro-marker-species-support`** — check whether a CL PRO marker asserted on + a cell type (without stating a species) is actually validated in the + literature as human, mouse, or both, reusing verbatim quotes from the + Milestone 2 literature reports first, then `citation-traversal` (if Asta is + reachable) or the keyless `soulcap-europepmc` fallback, writing verdicts to + `reports/pro_marker_species_support.tsv`. The issue #11 workflow. See + [SKILL.md](.claude/skills/pro-marker-species-support/SKILL.md). > Local RAG indexing (a `local-paper-index` skill) was trialled here but removed > to avoid confusion — it was copied verbatim from `atlas_chat` and unused. diff --git a/pyproject.toml b/pyproject.toml index f3b6fa3..72ebe62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ soulcap-hgnc = "soulcap_cl_mapping.hgnc_map:main" soulcap-map = "soulcap_cl_mapping.marker_map:main" soulcap-oak-match = "soulcap_cl_mapping.oak_match:main" soulcap-sssom = "soulcap_cl_mapping.sssom_export:main" +soulcap-europepmc = "soulcap_cl_mapping.europepmc_search:main" [project.optional-dependencies] dev = [ diff --git a/reports/pro_marker_species_support.tsv b/reports/pro_marker_species_support.tsv new file mode 100644 index 0000000..6af1f8b --- /dev/null +++ b/reports/pro_marker_species_support.tsv @@ -0,0 +1,78 @@ +cl_id cl_label pr_id pr_label cd_synonym species_support evidence_source citation quote source_ref +CL:0000623 natural killer cell PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) human m2_literature_report Scoville SD et al. 2017 (PMID:28396671; DOI:10.3389/fimmu.2017.00360) ILCs are identified as “lineage” (Lin) negative lymphocytes, lacking expression of surface markers more specifically expressed on T cells (CD3, CD5, TCR), B cells (CD19, CD20, BCR), myelomonocytic cells (CD14, CD15, CD36), and dendritic cells (DCs) (CD116, CD123, CD303). reports/literature/nk_cell_markers.md +CL:0000623 natural killer cell PR:000001889 CD14 molecule CD14 (label) human m2_literature_report Van Acker HH et al. 2017 (PMID:28791027; DOI:10.3389/fimmu.2017.00892) there also exists a CD56− NK cell subpopulation, namely CD3−CD4−CD14−CD19−CD16+NKp46+ lymphocytes reports/literature/nk_cell_markers.md +CL:0000624 CD4-positive, alpha-beta T cell PR:000025402 T cell receptor co-receptor CD8 CD8 (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000624 CD4-positive, alpha-beta T cell PR:000001004 CD4 molecule CD4 (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000625 CD8-positive, alpha-beta T cell PR:000001004 CD4 molecule CD4 (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000625 CD8-positive, alpha-beta T cell PR:000025402 T cell receptor co-receptor CD8 CD8 (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000771 eosinophil PR:000001254 C-C chemokine receptor type 1 CD191 (exact) insufficient_evidence none CD191/CCR1 not among the markers studied in PMID:38666394 (CCR3/CD193 is, but not CCR1); no other eosinophil-specific CD191 evidence found via targeted EuropePMC search. +CL:0000771 eosinophil PR:000001255 C-C chemokine receptor type 3 CD193 (exact) human europepmc_search (Cytometry B) 2024 (PMID:38666394; DOI:10.1002/cyto.b.22174) -- flow cytometric immunophenotyping of eosinophilopoiesis in normal human bone marrow candidate markers that included EMR-1, Siglec-8, CCR3, CD9, CD11a, CD11b, CD11c, CD13, CD16, CD29, CD34, CD38, CD45, CD44, CD49d, CD49f, CD54, CD62L, CD69, CD117, CD125 (IL-5Rα), HLA-DR, using 10 parameter flow cytometry https://europepmc.org/article/MED/38666394 +CL:0000771 eosinophil PR:000001307 CD44 molecule CD44 (label); CDw44 (exact) human europepmc_search (Cytometry B) 2024 (PMID:38666394; DOI:10.1002/cyto.b.22174) -- flow cytometric immunophenotyping of eosinophilopoiesis in normal human bone marrow candidate markers that included EMR-1, Siglec-8, CCR3, CD9, CD11a, CD11b, CD11c, CD13, CD16, CD29, CD34, CD38, CD45, CD44, CD49d, CD49f, CD54, CD62L, CD69, CD117, CD125 (IL-5Rα), HLA-DR, using 10 parameter flow cytometry https://europepmc.org/article/MED/38666394 +CL:0000771 eosinophil PR:000001343 CD69 molecule CD69 (label) human europepmc_search (Cytometry B) 2024 (PMID:38666394; DOI:10.1002/cyto.b.22174) -- flow cytometric immunophenotyping of eosinophilopoiesis in normal human bone marrow candidate markers that included EMR-1, Siglec-8, CCR3, CD9, CD11a, CD11b, CD11c, CD13, CD16, CD29, CD34, CD38, CD45, CD44, CD49d, CD49f, CD54, CD62L, CD69, CD117, CD125 (IL-5Rα), HLA-DR, using 10 parameter flow cytometry https://europepmc.org/article/MED/38666394 +CL:0000771 eosinophil PR:000005178 CD9 molecule CD9 (label) human europepmc_search (Cytometry B) 2024 (PMID:38666394; DOI:10.1002/cyto.b.22174) -- flow cytometric immunophenotyping of eosinophilopoiesis in normal human bone marrow candidate markers that included EMR-1, Siglec-8, CCR3, CD9, CD11a, CD11b, CD11c, CD13, CD16, CD29, CD34, CD38, CD45, CD44, CD49d, CD49f, CD54, CD62L, CD69, CD117, CD125 (IL-5Rα), HLA-DR, using 10 parameter flow cytometry https://europepmc.org/article/MED/38666394 +CL:0000775 neutrophil PR:000001479 low affinity immunoglobulin gamma Fc region receptor II CD32 (broad) human europepmc_search Su K et al. 2007 (PMID:17312177; DOI:10.4049/jimmunol.178.5.3272) -- FcγRIIb (CD32B) expression profiling in human circulating leukocytes we found that FcgammaRIIb is expressed on the cell surface of circulating B lymphocytes, monocytes, neutrophils, myeloid dendritic cells (DCs), and at very low levels on plasmacytoid DCs from some donors https://europepmc.org/article/MED/17312177 +CL:0000775 neutrophil PR:000001879 leukosialin CD43 (related) insufficient_evidence none CD43 not found in any targeted EuropePMC search in a human-neutrophil-specific context ('CD43 leukosialin human neutrophil' returned unrelated papers). +CL:0000786 plasma cell PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) as B cells differentiate to become plasmablasts and plasma cells, CD20 is lost but CD19 remains, as CD38/CD138 is acquired reports/literature/b_cell_markers.md +CL:0000786 plasma cell PR:000001935 syndecan-1 CD138 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) as B cells differentiate to become plasmablasts and plasma cells, CD20 is lost but CD19 remains, as CD38/CD138 is acquired reports/literature/b_cell_markers.md +CL:0000787 memory B cell PR:000001935 syndecan-1 CD138 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) -- describes the plasmablast/plasma-cell transition boundary, not memory B cells directly, but confirms CD138 as a human B-lineage differentiation marker as B cells differentiate to become plasmablasts and plasma cells, CD20 is lost but CD19 remains, as CD38/CD138 is acquired reports/literature/b_cell_markers.md +CL:0000787 memory B cell PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) Mature B cells typically express both CD19 and CD20. reports/literature/b_cell_markers.md +CL:0000788 naive B cell PR:000001963 CD27 molecule CD27 (label); CD27L (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**N**: CD19+ CD27− CD38− CD24− IgD+"" [naive B cell gating entry, Table 1]" reports/literature/b_cell_markers.md +CL:0000788 naive B cell PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) Mature B cells typically express both CD19 and CD20. reports/literature/b_cell_markers.md +CL:0000803 CD4-negative CD8-negative gamma-delta intraepithelial T cell PR:000001004 CD4 molecule CD4 (label) human m2_literature_report Kelm M et al. 2026 (PMID: 41727470; DOI: 10.3389/fimmu.2026.1739493; PMC: PMC12920462) Purity of PHA- and Zole- expanded αβ and γδ T-cell cultures and MACS-isolated T-cell populations were assessed by flow cytometry using anti-CD3, CD4, CD8, γδ TCR, and αβ TCR monoclonal antibodies. reports/literature/t_cell_markers.md +CL:0000803 CD4-negative CD8-negative gamma-delta intraepithelial T cell PR:000001084 T-cell surface glycoprotein CD8 alpha chain CD8 (label); CD8A (exact); CD8a (exact) human m2_literature_report Kelm M et al. 2026 (PMID: 41727470; DOI: 10.3389/fimmu.2026.1739493; PMC: PMC12920462) Purity of PHA- and Zole- expanded αβ and γδ T-cell cultures and MACS-isolated T-cell populations were assessed by flow cytometry using anti-CD3, CD4, CD8, γδ TCR, and αβ TCR monoclonal antibodies. reports/literature/t_cell_markers.md +CL:0000814 mature NK T cell PR:000001874 KLRB1-like protein human m2_literature_report Lal KG et al. 2018 (PMID:29533501; DOI:10.1002/cyto.a.23357) we defined iNKT cell subsets by the co-expression of TCR Vα24 and Vβ11 among CD3+ cells, and further through their expression of CD4 and CD161 reports/literature/inkt_markers.md +CL:0000818 transitional stage B cell PR:000001408 ADP-ribosyl cyclase/cyclic ADP-ribose hydrolase 1 CD38 (exact) human m2_literature_report Martin VG et al. 2016 (PMID: 27994589; DOI: 10.3389/fimmu.2016.00546) T1 (CD38+++CD24hiCD10++IgDlo/−), T2 (CD38++CD24hiCD10+IgD+), and T3 (CD38+CD24+IgD+ABCB1−) reports/literature/b_cell_markers.md +CL:0000818 transitional stage B cell PR:000001932 signal transducer CD24 CD24 (label); CD24A (related) human m2_literature_report Martin VG et al. 2016 (PMID: 27994589; DOI: 10.3389/fimmu.2016.00546) T1 (CD38+++CD24hiCD10++IgDlo/−), T2 (CD38++CD24hiCD10+IgD+), and T3 (CD38+CD24+IgD+ABCB1−) reports/literature/b_cell_markers.md +CL:0000818 transitional stage B cell PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) insufficient_evidence none CD20 positivity on transitional B cells is not stated verbatim in reports/literature/b_cell_markers.md -- the report only confirms CD20 on 'mature B cells' generally (Griffin & Rothstein 2012), not the transitional stage specifically. +CL:0000860 classical monocyte PR:000001199 C-C chemokine receptor type 2 CD192 (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000860 classical monocyte PR:000001002 CD19 molecule CD19 (label) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000860 classical monocyte PR:000001020 CD3 epsilon CD3 (label); CD3E (exact); CD3e (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000860 classical monocyte PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000875 non-classical monocyte PR:000001206 CX3C chemokine receptor 1 insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000875 non-classical monocyte PR:000001002 CD19 molecule CD19 (label) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000875 non-classical monocyte PR:000001020 CD3 epsilon CD3 (label); CD3E (exact); CD3e (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000875 non-classical monocyte PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0000895 naive thymus-derived CD4-positive, alpha-beta T cell PR:000001380 interleukin-2 receptor subunit alpha CD25 (exact) human m2_literature_report Kamran M et al. 2025 (PMID: 41394832; DOI: 10.3389/fimmu.2025.1676937; PMC: PMC12695756) -- confirms CD25/CD127 as human T cell markers, though in a Treg (not naive-T) context; naive T cells are CD25-low/CD127-high, the inverse pattern from Treg In humans, a combination of high CD25 and low or absent CD127 expression helps differentiate Tregs from effector T cells, which usually express high levels of CD127 reports/literature/t_cell_markers.md +CL:0000898 naive T cell PR:000001318 L-selectin CD62 (exact); CD62L (exact) human m2_literature_report Kamigaichi A et al. 2026 (PMID: 42047825; DOI: 10.1007/s00262-026-04357-4; PMC: PMC13125411) -- confirms CD62L used on human CD4+ T cells, though in a Th1 (not naive-T) context Th1 (CXCR3+CCR4−CCR6− CD62LlowCD4+ T cells) reports/literature/t_cell_markers.md +CL:0000898 naive T cell PR:000001307 CD44 molecule CD44 (label); CDw44 (exact) insufficient_evidence none CD44 not mentioned anywhere in reports/literature/t_cell_markers.md; not found via a targeted EuropePMC search either ('CD44 human naive T cell surface marker' returned unrelated tissue-resident-memory/B-cell papers). +CL:0000898 naive T cell PR:000001015 receptor-type tyrosine-protein phosphatase C isoform CD45RA CD45RA (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000898 naive T cell PR:000001203 C-C chemokine receptor type 7 CD197 (exact); CDw197 (exact) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000898 naive T cell PR:000001869 interleukin-7 receptor subunit alpha CD127 (exact); CDw127 (exact) human m2_literature_report Kamran M et al. 2025 (PMID: 41394832; DOI: 10.3389/fimmu.2025.1676937; PMC: PMC12695756) -- confirms CD25/CD127 as human T cell markers, though in a Treg (not naive-T) context; naive T cells are CD25-low/CD127-high, the inverse pattern from Treg In humans, a combination of high CD25 and low or absent CD127 expression helps differentiate Tregs from effector T cells, which usually express high levels of CD127 reports/literature/t_cell_markers.md +CL:0000900 naive thymus-derived CD8-positive, alpha-beta T cell PR:000001380 interleukin-2 receptor subunit alpha CD25 (exact) human m2_literature_report Kamran M et al. 2025 (PMID: 41394832; DOI: 10.3389/fimmu.2025.1676937; PMC: PMC12695756) -- confirms CD25/CD127 as human T cell markers, though in a Treg (not naive-T) context; naive T cells are CD25-low/CD127-high, the inverse pattern from Treg In humans, a combination of high CD25 and low or absent CD127 expression helps differentiate Tregs from effector T cells, which usually express high levels of CD127 reports/literature/t_cell_markers.md +CL:0000904 central memory CD4-positive, alpha-beta T cell PR:000001015 receptor-type tyrosine-protein phosphatase C isoform CD45RA CD45RA (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA− reports/literature/t_cell_markers.md +CL:0000904 central memory CD4-positive, alpha-beta T cell PR:000001203 C-C chemokine receptor type 7 CD197 (exact); CDw197 (exact) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7+/CD45RA− reports/literature/t_cell_markers.md +CL:0000905 effector memory CD4-positive, alpha-beta T cell PR:000001015 receptor-type tyrosine-protein phosphatase C isoform CD45RA CD45RA (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7−/CD45RA+ reports/literature/t_cell_markers.md +CL:0000905 effector memory CD4-positive, alpha-beta T cell PR:000001203 C-C chemokine receptor type 7 CD197 (exact); CDw197 (exact) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) CD3+/CD8−/CD4+/CCR7−/CD45RA+ reports/literature/t_cell_markers.md +CL:0000907 central memory CD8-positive, alpha-beta T cell PR:000001015 receptor-type tyrosine-protein phosphatase C isoform CD45RA CD45RA (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) -- CD8 Naive gating string quoted verbatim; the CD8 Central Memory string was not separately captured in this M2 report, but the same CD45RA/CCR7 panel is applied orthogonally to CD4 and CD8 per the paper's design CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000907 central memory CD8-positive, alpha-beta T cell PR:000001203 C-C chemokine receptor type 7 CD197 (exact); CDw197 (exact) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) -- CD8 Naive gating string quoted verbatim; the CD8 Central Memory string was not separately captured in this M2 report, but the same CD45RA/CCR7 panel is applied orthogonally to CD4 and CD8 per the paper's design CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000913 effector memory CD8-positive, alpha-beta T cell PR:000001015 receptor-type tyrosine-protein phosphatase C isoform CD45RA CD45RA (label) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) -- CD8 Naive gating string quoted verbatim; the CD8 Effector Memory string was not separately captured in this M2 report, but the same CD45RA/CCR7 panel is applied orthogonally to CD4 and CD8 per the paper's design CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000913 effector memory CD8-positive, alpha-beta T cell PR:000001203 C-C chemokine receptor type 7 CD197 (exact); CDw197 (exact) human m2_literature_report Finak G et al. 2016 (PMID: 26861911; DOI: 10.1038/srep20686; PMC: PMC4748244) -- CD8 Naive gating string quoted verbatim; the CD8 Effector Memory string was not separately captured in this M2 report, but the same CD45RA/CCR7 panel is applied orthogonally to CD4 and CD8 per the paper's design CD3+/CD8+/CD4−/CCR7+/CD45RA+ reports/literature/t_cell_markers.md +CL:0000938 CD16-negative, CD56-bright natural killer cell, human PR:000001024 neural cell adhesion molecule 1 CD56 (exact) human m2_literature_report Rebuffet L et al. 2024 (PMID:38956378; DOI:10.1038/s41590-024-01883-0) human NK cells are typically divided into two main categories on the basis of the density of CD56, the 140-kDa isoform of the neural cell adhesion molecule (NCAM)2, on the cell surface: CD56bright and CD56dim NK cells. reports/literature/nk_cell_markers.md +CL:0000938 CD16-negative, CD56-bright natural killer cell, human PR:000001483 low affinity immunoglobulin gamma Fc region receptor III CD16 (exact) human m2_literature_report Scoville SD et al. 2017 (PMID:28396671; DOI:10.3389/fimmu.2017.00360) CD56bright human NK cells express high levels of CD62L (l-selectin) and CD94 but absent or low levels of CD16 and KIRs reports/literature/nk_cell_markers.md +CL:0000938 CD16-negative, CD56-bright natural killer cell, human PR:000002015 MHC class II histocompatibility antigen alpha chain DRA human m2_literature_report Rebuffet L et al. 2024 (PMID:38956378; DOI:10.1038/s41590-024-01883-0) -- indirect: HLA-D family expression shown on an adaptive NK subset (NK3B), not stated specifically for CD56bright/CD56dim Notably, NK3B (cluster 1) was distinguished by expression of members of the HLA-D gene family, CD74, CCL5, CD7 and KLRC1, and NK3A exhibited enhanced cytotoxic capabilities (through expression of GZMA, GZMB and PRF1) reports/literature/nk_cell_markers.md +CL:0000938 CD16-negative, CD56-bright natural killer cell, human PR:000001319 SLAM family member 5 CD84 (exact) insufficient_evidence none No CD84-on-NK-cell evidence found in reports/literature/nk_cell_markers.md or a targeted EuropePMC search ('CD84 human natural killer cell surface marker' -- top hits were CD84 in MDSCs/breast cancer/CLL, none NK-specific). +CL:0000939 CD16-positive, CD56-dim natural killer cell, human PR:000001024 neural cell adhesion molecule 1 CD56 (exact) human m2_literature_report Rebuffet L et al. 2024 (PMID:38956378; DOI:10.1038/s41590-024-01883-0) human NK cells are typically divided into two main categories on the basis of the density of CD56, the 140-kDa isoform of the neural cell adhesion molecule (NCAM)2, on the cell surface: CD56bright and CD56dim NK cells. reports/literature/nk_cell_markers.md +CL:0000939 CD16-positive, CD56-dim natural killer cell, human PR:000002015 MHC class II histocompatibility antigen alpha chain DRA human m2_literature_report Rebuffet L et al. 2024 (PMID:38956378; DOI:10.1038/s41590-024-01883-0) -- indirect: HLA-D family expression shown on an adaptive NK subset (NK3B), not stated specifically for CD56bright/CD56dim Notably, NK3B (cluster 1) was distinguished by expression of members of the HLA-D gene family, CD74, CCL5, CD7 and KLRC1, and NK3A exhibited enhanced cytotoxic capabilities (through expression of GZMA, GZMB and PRF1) reports/literature/nk_cell_markers.md +CL:0000939 CD16-positive, CD56-dim natural killer cell, human PR:000001483 low affinity immunoglobulin gamma Fc region receptor III CD16 (exact) human m2_literature_report Scoville SD et al. 2017 (PMID:28396671; DOI:10.3389/fimmu.2017.00360) In contrast, CD56dim NK cells are CD16hi and express more KIRs but less CD94 and CD62L; they predominate in PB reports/literature/nk_cell_markers.md +CL:0000960 T3 B cell PR:000001318 L-selectin CD62 (exact); CD62L (exact) insufficient_evidence none CD62L not mentioned in the T3-transitional context in reports/literature/b_cell_markers.md (only mentioned for DN2 B cells: 'lack of CXCR5 and CD62L expression', Sachinidis 2021, a different B cell subset). +CL:0000960 T3 B cell PR:000001338 complement receptor type 2 CD21 (exact) human m2_literature_report Martin VG et al. 2016 (PMID: 27994589; DOI: 10.3389/fimmu.2016.00546) The gradual loss of CD10, CD5, and IgM and the upregulation of CD22, CD44, CD21, and CD23 as cells progress from immature to transitional (TI to T2 to T3) to mature naïve cells reports/literature/b_cell_markers.md +CL:0000960 T3 B cell PR:000001880 low affinity immunoglobulin epsilon Fc receptor CD23 (exact); CD23A (related) human m2_literature_report Martin VG et al. 2016 (PMID: 27994589; DOI: 10.3389/fimmu.2016.00546) The gradual loss of CD10, CD5, and IgM and the upregulation of CD22, CD44, CD21, and CD23 as cells progress from immature to transitional (TI to T2 to T3) to mature naïve cells reports/literature/b_cell_markers.md +CL:0000970 unswitched memory B cell PR:000001963 CD27 molecule CD27 (label); CD27L (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**M**: CD19+ CD27+ CD38−/lo"" [memory B cell gating entry]" reports/literature/b_cell_markers.md +CL:0000972 class switched memory B cell PR:000001963 CD27 molecule CD27 (label); CD27L (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**M**: CD19+ CD27+ CD38−/lo"" [memory B cell gating entry]" reports/literature/b_cell_markers.md +CL:0000980 plasmablast PR:000001408 ADP-ribosyl cyclase/cyclic ADP-ribose hydrolase 1 CD38 (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**ASC**: CD19+ CD27+ CD38hi"" and ""**M**: CD19+ CD27+ CD38−/lo"" and" reports/literature/b_cell_markers.md +CL:0000980 plasmablast PR:000001963 CD27 molecule CD27 (label); CD27L (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**ASC**: CD19+ CD27+ CD38hi"" and ""**M**: CD19+ CD27+ CD38−/lo"" and" reports/literature/b_cell_markers.md +CL:0000980 plasmablast PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) human m2_literature_report Griffin DO & Rothstein TL 2012 (PMID: 22654880; DOI: 10.3389/fimmu.2012.00122) as B cells differentiate to become plasmablasts and plasma cells, CD20 is lost but CD19 remains, as CD38/CD138 is acquired reports/literature/b_cell_markers.md +CL:0000980 plasmablast PR:000001935 syndecan-1 CD138 (exact) human m2_literature_report Woodruff MC et al. 2020 (PMID: 33028979; DOI: 10.1038/s41590-020-00814-z) """**CD138neg ASC**: CD138−"" and ""**CD138pos ASC**: CD138+"" [gating labels distinguishing" reports/literature/b_cell_markers.md +CL:0000981 double negative memory B cell PR:000001963 CD27 molecule CD27 (label); CD27L (exact) human m2_literature_report Sachinidis A & Garyfallos A 2021 (PMID: 34964023; DOI: 10.31138/mjr.32.3.192) Double negative (DN) B cells, also known as atypical memory B cells, constitute a B cell population that lacks expression of immunoglobulin D and CD27 memory marker. reports/literature/b_cell_markers.md +CL:0000990 conventional dendritic cell PR:000001013 integrin alpha-X CD11 (exact); CD11C (related); CD11c (exact) human m2_literature_report Rhodes JW et al. 2019 (DOI:10.3389/fimmu.2019.01088) Conventional DCs (cDCs), also known as myeloid DCs, can be defined as CD11c+ CD123− reports/literature/dc_markers.md +CL:0001058 plasmacytoid dendritic cell, human PR:000001013 integrin alpha-X CD11 (exact); CD11C (related); CD11c (exact) human m2_literature_report Rhodes JW et al. 2019 (DOI:10.3389/fimmu.2019.01088) Conventional DCs (cDCs), also known as myeloid DCs, can be defined as CD11c+ CD123− reports/literature/dc_markers.md +CL:0001058 plasmacytoid dendritic cell, human PR:000001889 CD14 molecule CD14 (label) human m2_literature_report Villani AC et al. 2017 (PMID:28428369; DOI:10.1126/science.aah4573) DCs were defined as live, LIN(CD3, CD19, CD56)−CD14−HLA-DR+ cells. reports/literature/dc_markers.md +CL:0001058 plasmacytoid dendritic cell, human PR:000001865 interleukin-3 receptor class 2 alpha chain CD123 (exact) human m2_literature_report Rhodes JW et al. 2019 (DOI:10.3389/fimmu.2019.01088) pDCs are CD11c− CD123+ cells best characterized for their type I interferon reports/literature/dc_markers.md +CL:0001065 innate lymphoid cell PR:000001002 CD19 molecule CD19 (label) human m2_literature_report Krabbendam L et al. 2021 (PMID:33300130; DOI:10.1002/eji.202048696) Subsequently we gated out non‐relevant lineages using antibodies against CD3, CD4, TCRαβ, and TCRγδ (T cells), CD19 (B cells) CD14 (monocytes) FcRεR1a (mast cells), BDCA2 (pDCs), CD123 (pDCs, basophils), and CD34 (hematopoietic progenitors). reports/literature/ilc_markers.md +CL:0001065 innate lymphoid cell PR:000001020 CD3 epsilon CD3 (label); CD3E (exact); CD3e (exact) human m2_literature_report Krabbendam L et al. 2021 (PMID:33300130; DOI:10.1002/eji.202048696) We gated for lymphocytes, excluded doublets and dead cells and selected CD45+CD3− cells reports/literature/ilc_markers.md +CL:0002393 intermediate monocyte PR:000001002 CD19 molecule CD19 (label) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0002393 intermediate monocyte PR:000001020 CD3 epsilon CD3 (label); CD3E (exact); CD3e (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0002393 intermediate monocyte PR:000001199 C-C chemokine receptor type 2 CD192 (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0002393 intermediate monocyte PR:000001289 membrane-spanning 4-domains subfamily A member 1 CD20 (exact) insufficient_evidence none No verbatim sentence naming this marker on monocytes was found. The canonical human/mouse monocyte nomenclature paper this repo already cites for issue #12 (Ziegler-Heitbrock L et al. 2010, PMID:20628149, DOI:10.1182/blood-2010-02-258558) explicitly covers both species at the classification-scheme level ('defines 3 types of monocytes ... in human and in mouse blood') but its abstract does not name this marker specifically; full text would be needed to confirm. Targeted EuropePMC searches for this marker in a monocyte context returned only tangential recent disease-cohort papers, not clean definitional statements. +CL:0002394 CD141-positive myeloid dendritic cell PR:000002105 thrombomodulin CD141 (exact) human m2_literature_report Villani AC et al. 2017 (PMID:28428369; DOI:10.1126/science.aah4573) DC1 corresponds to the cross-presenting CD141/BDCA-3+ cDC1, which is best marked by CLEC9A reports/literature/dc_markers.md +CL:0002399 CD1c-positive myeloid dendritic cell PR:000002027 T-cell surface glycoprotein CD1c CD1C (exact); CD1c (label) human m2_literature_report Villani AC et al. 2017 (PMID:28428369; DOI:10.1126/science.aah4573) DC2 and DC3 correspond to new subdivisions of the CD1C/BDCA-1+ cDC2 reports/literature/dc_markers.md diff --git a/src/soulcap_cl_mapping/europepmc_search.py b/src/soulcap_cl_mapping/europepmc_search.py new file mode 100644 index 0000000..a5cd267 --- /dev/null +++ b/src/soulcap_cl_mapping/europepmc_search.py @@ -0,0 +1,88 @@ +"""Search Europe PMC for literature evidence — no API key required. + +Fallback literature source for issue #11 (species-specificity support for CL +PRO markers) when the Asta/Semantic Scholar MCP server isn't reachable. +Europe PMC's REST API (https://europepmc.org/RestfulWebService) is free and +keyless, so this only ever does a single-round search against abstracts — +it does not follow citation graphs the way the citation-traversal skill's +Asta-backed two-round traversal does. Use it as the "general search" fallback +tier the issue itself allows, not a replacement for citation-traversal when +Asta is available. + +Usage:: + + from soulcap_cl_mapping.europepmc_search import search + hits = search("CD56 natural killer cell human") +""" + +from __future__ import annotations + +import argparse +import sys + +import requests + +EUROPEPMC_BASE = "https://www.ebi.ac.uk/europepmc/webservices/rest/search" +DEFAULT_PAGE_SIZE = 5 + + +def search( + query: str, + page_size: int = DEFAULT_PAGE_SIZE, + timeout: int = 15, +) -> list[dict]: + """Search Europe PMC for *query*. + + Returns a list of normalised result dicts with keys: ``pmid``, ``doi``, + ``title``, ``abstract`` (``""`` if none indexed), ``is_open_access``. + Uses ``resultType=core`` so abstracts come back inline — no separate + full-text fetch needed for abstract-level evidence. + """ + params: dict[str, str | int] = { + "query": query, + "format": "json", + "resultType": "core", + "pageSize": page_size, + } + resp = requests.get(EUROPEPMC_BASE, params=params, timeout=timeout) + resp.raise_for_status() + results = resp.json().get("resultList", {}).get("result", []) + return [_normalise_result(r) for r in results] + + +def _normalise_result(r: dict) -> dict: + return { + "pmid": r.get("pmid", ""), + "doi": r.get("doi", ""), + "title": r.get("title", ""), + "abstract": r.get("abstractText", ""), + "is_open_access": r.get("isOpenAccess", "N") == "Y", + } + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + description="Search Europe PMC (free, no API key required)." + ) + parser.add_argument("query", help="Free-text search query.") + parser.add_argument( + "--rows", type=int, default=DEFAULT_PAGE_SIZE, help="Max results." + ) + args = parser.parse_args(argv) + + hits = search(args.query, page_size=args.rows) + if not hits: + print(f"No results for: {args.query}") + return 1 + + for h in hits: + print(f"PMID:{h['pmid']} DOI:{h['doi']} OA:{h['is_open_access']}") + print(f" {h['title']}") + if h["abstract"]: + print(f" {h['abstract'][:300]}...") + print() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/soulcap_cl_mapping/pro_species_support.py b/src/soulcap_cl_mapping/pro_species_support.py new file mode 100644 index 0000000..a161355 --- /dev/null +++ b/src/soulcap_cl_mapping/pro_species_support.py @@ -0,0 +1,74 @@ +"""Scope the CL PRO markers that need species-specificity literature support. + +CL asserts many PRO (Protein Ontology) markers on cell types without saying +whether the underlying evidence is human, mouse, or both — the ``pr_label`` +just reads e.g. "neural cell adhesion molecule 1", not "... (mouse)". This +module identifies exactly which (CL cell type, PR marker) pairs need a +literature check, restricted to the CL terms this project actually maps +SOULCAP cell types to (``CURATED_MAPPINGS`` in ``sssom_export.py``) rather +than all of CL — see issue #11. + +Downstream literature work (citation-traversal runs, the standardised output +TSV) is driven by this scoping, but lives outside this module; see the plan +for issue #11. +""" + +from __future__ import annotations + +import csv +from collections import defaultdict +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +DEFAULT_TSV = REPO_ROOT / "reports" / "cl_pro_relationships.tsv" + +_SPECIES_QUALIFIERS = ("(mouse)", "(human)") + + +def get_scoped_pairs(tsv_path: Path, curated_cl_ids: set[str]) -> list[dict[str, str]]: + """Return one dict per (CL cell type, general PR marker) pair to check. + + A pair qualifies when: the CL cell type is in *curated_cl_ids*, the row is + directly asserted (``asserted == "True"``), and ``pr_label`` carries no + species qualifier already (i.e. it's a "general" marker whose species + applicability isn't stated). Deduplicated on (cell, pr). + """ + seen: set[tuple[str, str]] = set() + pairs: list[dict[str, str]] = [] + with tsv_path.open(encoding="utf-8", newline="") as fh: + reader = csv.DictReader(fh, delimiter="\t") + for row in reader: + cl_id = row.get("cell", "") + pr_id = row.get("pr", "") + pr_label = row.get("pr_label", "") + if not cl_id or not pr_id: + continue + if cl_id not in curated_cl_ids: + continue + if row.get("asserted") != "True": + continue + if any(q in pr_label for q in _SPECIES_QUALIFIERS): + continue + key = (cl_id, pr_id) + if key in seen: + continue + seen.add(key) + pairs.append( + { + "cl_id": cl_id, + "cl_label": row.get("cell_label", ""), + "pr_id": pr_id, + "pr_label": pr_label, + "cd_synonym": row.get("cd_synonym", ""), + } + ) + return pairs + + +def group_by_cell(pairs: list[dict[str, str]]) -> dict[str, list[dict[str, str]]]: + """Group scoped pairs by ``cl_id`` so one citation-traversal run can cover + every marker for a cell type against a shared seed set.""" + grouped: dict[str, list[dict[str, str]]] = defaultdict(list) + for pair in pairs: + grouped[pair["cl_id"]].append(pair) + return dict(grouped) diff --git a/tests/test_europepmc_search.py b/tests/test_europepmc_search.py new file mode 100644 index 0000000..d9e7b94 --- /dev/null +++ b/tests/test_europepmc_search.py @@ -0,0 +1,95 @@ +"""Tests for soulcap_cl_mapping.europepmc_search. + +Network is always mocked — these tests never hit Europe PMC. +""" + +from __future__ import annotations + +from unittest.mock import patch + +from soulcap_cl_mapping import europepmc_search as eps + + +class _FakeResp: + def __init__(self, data: dict): + self._data = data + + def raise_for_status(self) -> None: + pass + + def json(self) -> dict: + return self._data + + +def _payload(*results: dict) -> dict: + return {"resultList": {"result": list(results)}} + + +def _result(**overrides) -> dict: + base = { + "pmid": "28791027", + "doi": "10.3389/fimmu.2017.00892", + "title": "NK cell markers", + "abstractText": "NK cells express CD56 in humans.", + "isOpenAccess": "Y", + } + base.update(overrides) + return base + + +# --------------------------------------------------------------------------- # +# search +# --------------------------------------------------------------------------- # +def test_search_normalises_results(): + with patch.object(eps.requests, "get", return_value=_FakeResp(_payload(_result()))): + hits = eps.search("CD56 NK cell") + assert hits == [ + { + "pmid": "28791027", + "doi": "10.3389/fimmu.2017.00892", + "title": "NK cell markers", + "abstract": "NK cells express CD56 in humans.", + "is_open_access": True, + } + ] + + +def test_search_no_results_returns_empty_list(): + with patch.object(eps.requests, "get", return_value=_FakeResp(_payload())): + assert eps.search("nonsense query") == [] + + +def test_search_missing_abstract_defaults_to_empty_string(): + result = _result() + del result["abstractText"] + with patch.object(eps.requests, "get", return_value=_FakeResp(_payload(result))): + hits = eps.search("x") + assert hits[0]["abstract"] == "" + + +def test_search_not_open_access_is_false(): + with patch.object( + eps.requests, "get", return_value=_FakeResp(_payload(_result(isOpenAccess="N"))) + ): + hits = eps.search("x") + assert hits[0]["is_open_access"] is False + + +# --------------------------------------------------------------------------- # +# CLI +# --------------------------------------------------------------------------- # +def test_main_prints_results_and_returns_zero(capsys): + with patch.object(eps.requests, "get", return_value=_FakeResp(_payload(_result()))): + rc = eps.main(["CD56 NK cell"]) + out = capsys.readouterr().out + assert rc == 0 + assert "28791027" in out + assert "NK cell markers" in out + + +def test_main_no_results_returns_one(capsys): + with patch.object(eps.requests, "get", return_value=_FakeResp(_payload())): + rc = eps.main(["nonsense query"]) + out = capsys.readouterr().out + assert rc == 1 + assert "No results" in out diff --git a/tests/test_pro_species_support.py b/tests/test_pro_species_support.py new file mode 100644 index 0000000..ed3f31f --- /dev/null +++ b/tests/test_pro_species_support.py @@ -0,0 +1,121 @@ +"""Tests for soulcap_cl_mapping.pro_species_support.""" + +from __future__ import annotations + +import csv + +from soulcap_cl_mapping import pro_species_support as pss + +_HEADER = ["cell", "cell_label", "pr", "pr_label", "cd_synonym", "asserted"] + + +def _write_tsv(tmp_path, rows): + path = tmp_path / "cl_pro_relationships.tsv" + with path.open("w", encoding="utf-8", newline="") as fh: + writer = csv.writer(fh, delimiter="\t") + writer.writerow(_HEADER) + writer.writerows(rows) + return path + + +# --------------------------------------------------------------------------- # +# get_scoped_pairs +# --------------------------------------------------------------------------- # +def test_get_scoped_pairs_includes_asserted_general_marker_in_curated_set( + tmp_path, +): + tsv = _write_tsv( + tmp_path, + [["CL:0000623", "natural killer cell", "PR:1", "CD56 protein", "CD56", "True"]], + ) + pairs = pss.get_scoped_pairs(tsv, curated_cl_ids={"CL:0000623"}) + assert pairs == [ + { + "cl_id": "CL:0000623", + "cl_label": "natural killer cell", + "pr_id": "PR:1", + "pr_label": "CD56 protein", + "cd_synonym": "CD56", + } + ] + + +def test_get_scoped_pairs_excludes_cell_not_in_curated_set(tmp_path): + tsv = _write_tsv( + tmp_path, + [["CL:9999999", "other cell", "PR:1", "X protein", "X", "True"]], + ) + assert pss.get_scoped_pairs(tsv, curated_cl_ids={"CL:0000623"}) == [] + + +def test_get_scoped_pairs_excludes_unasserted_rows(tmp_path): + tsv = _write_tsv( + tmp_path, + [["CL:0000623", "natural killer cell", "PR:1", "X protein", "X", "False"]], + ) + assert pss.get_scoped_pairs(tsv, curated_cl_ids={"CL:0000623"}) == [] + + +def test_get_scoped_pairs_excludes_species_qualified_labels(tmp_path): + tsv = _write_tsv( + tmp_path, + [ + [ + "CL:0000623", + "natural killer cell", + "PR:1", + "lymphocyte antigen 76 (mouse)", + "", + "True", + ], + [ + "CL:0000623", + "natural killer cell", + "PR:2", + "CD56 antigen (human)", + "CD56", + "True", + ], + ], + ) + assert pss.get_scoped_pairs(tsv, curated_cl_ids={"CL:0000623"}) == [] + + +def test_get_scoped_pairs_deduplicates_repeated_cell_pr_pairs(tmp_path): + tsv = _write_tsv( + tmp_path, + [ + [ + "CL:0000623", + "natural killer cell", + "PR:1", + "CD56 protein", + "CD56", + "True", + ], + [ + "CL:0000623", + "natural killer cell", + "PR:1", + "CD56 protein", + "CD56", + "True", + ], + ], + ) + assert len(pss.get_scoped_pairs(tsv, curated_cl_ids={"CL:0000623"})) == 1 + + +# --------------------------------------------------------------------------- # +# group_by_cell +# --------------------------------------------------------------------------- # +def test_group_by_cell_groups_multiple_markers_under_one_cl_id(): + pairs = [ + {"cl_id": "CL:0000623", "pr_id": "PR:1"}, + {"cl_id": "CL:0000623", "pr_id": "PR:2"}, + {"cl_id": "CL:0000236", "pr_id": "PR:3"}, + ] + grouped = pss.group_by_cell(pairs) + assert set(grouped) == {"CL:0000623", "CL:0000236"} + assert len(grouped["CL:0000623"]) == 2 + assert len(grouped["CL:0000236"]) == 1