Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .claude/skills/gap-issue-filing/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
name: gap-issue-filing
description: Turn unfiled rows in reports/gaps.tsv into GitHub issues using the mapping_gap template, without duplicating existing issues, then update the row's status/repo_issue columns. Invoke when asked to file logged mapping gaps as issues, clear the gaps.tsv backlog, or check which gaps still need one.
---

You take rows already logged in `reports/gaps.tsv` — cases where matching a
SOULCAP cell type to CL broke down — and turn the ones that aren't tracked on
GitHub yet into real issues, using the existing `mapping_gap` template so they
stay consistent with issues #12–#14. See [CLAUDE.md](../../../CLAUDE.md) for
repo conventions and [ROADMAP.md](../../../ROADMAP.md) for how gaps relate to
the CL term corrections table.

`gaps.tsv` is the durable record; GitHub issues are the actionable, triageable
copy. This skill keeps the two in sync — it never replaces `gaps.tsv` as the
source of truth.

## When to invoke

The user asks to file gaps as issues, clear the `gaps.tsv` backlog, or check
how many logged gaps still lack a tracking issue.

## Tools

- `reports/gaps.tsv` — the source rows. Columns: `soulcap_abbreviation`,
`soulcap_full_name`, `gap_type`, `description`, `related_cl_ids`,
`evidence`, `status` (`open` / `filed_as_issue`), `repo_issue`,
`date_logged`.
- `.github/ISSUE_TEMPLATE/mapping_gap.yml` — the template whose fields map
directly onto `gaps.tsv` columns: `soulcap_abbreviation` → title, `gap_type`
→ the dropdown, `description` + `related_cl_ids` + `evidence` → the
matching body fields.
- `gh issue list` / `gh issue create` — via Bash. This repo is
`Cellular-Semantics/SOULCAP_CL_Mapping` (confirm with `gh repo view` if the
remote is ever ambiguous).
- `Edit` — to update `gaps.tsv`'s `status`/`repo_issue` columns once an issue
is filed.

## Step 1 — find what's actually unfiled

Read `reports/gaps.tsv` and list every row where `status` is `open` (not
`filed_as_issue`). Note two rows sharing a `soulcap_abbreviation` group (e.g.
`NK1 / NK3`) file as **one** issue, matching how the row itself is structured
— don't split a single row into multiple issues.

## Step 2 — check for an existing issue first

Before filing, search for a plausible duplicate:

```bash
gh issue list --search "<soulcap_abbreviation> in:title" --state all
gh issue list --label gap --state all
```

Some gaps already have a related, non-`gap`-labelled issue (e.g. the
monocyte/B-cell axiom-gap rows reference issue #12 in their `description` even
though #12 itself is filed under a different title). Read the row's
`description` and `evidence` columns for an existing issue number before
assuming none exists — filing a duplicate is worse than leaving a row open one
more day.

## Step 3 — confirm the batch with the user before filing

List the rows you intend to file (abbreviation + one-line description) and get
explicit confirmation before running `gh issue create` — this creates public,
visible state (issues, notifications to watchers) that's easy to create but
tedious to clean up if wrong. Do not file silently in a loop.

## Step 4 — file each issue

For each confirmed row, create the issue from the template's own field
structure — reproduce it as a plain markdown body (the `gh issue create
--template` flag targets local `.github/` templates inconsistently across `gh`
versions; writing the body directly from the YAML fields is more reliable):

```bash
gh issue create \
--title "[gap] <soulcap_abbreviation>: <short description, ~8 words>" \
--label gap \
--body "$(cat <<'EOF'
**SOULCAP abbreviation:** <soulcap_abbreviation> (<soulcap_full_name>)

**Gap type:** <gap_type>

**Description:**
<description column, verbatim>

**Related CL ID(s):** <related_cl_ids, or "none">

**Evidence:**
<evidence column, verbatim — turn `;`-separated paths into a bullet list>
EOF
)"
```

## Step 5 — write the result back to gaps.tsv

For each filed issue, edit its `gaps.tsv` row:

- `status`: `open` → `filed_as_issue`
- `repo_issue`: `(not yet filed)` → the issue URL `gh issue create` printed

Do this immediately after each successful `gh issue create` — don't batch it
to the end, so a failure partway through the batch doesn't leave filed issues
unrecorded.

## Known limitations

- This only closes the loop one direction (gap → issue). It does not detect
when a filed issue has since been closed/resolved upstream — that's
`roadmap-status-sync`'s job for the CL term corrections table, and isn't yet
extended to `gaps.tsv` rows with `status=filed_as_issue`.
- A `sheet_data_issue` row (a typo/malformed string in the master Google
Sheet) still can't be *fixed* here — filing the issue documents it, but the
actual correction still has to happen in the Sheet per
[CLAUDE.md](../../../CLAUDE.md)'s data-flow rules.
138 changes: 138 additions & 0 deletions .claude/skills/mapping-audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
name: mapping-audit
description: Cross-check curated SOULCAP→CL mappings against current CL marker axioms and literature evidence to catch drift, unsupported confidence, or mistyped broad/exact matches — the Milestone 3 deliverable, run today as a drift/evidence audit even though the Sheet hasn't yet gained the broad/exact + comment columns the milestone originally specified. Invoke when asked to audit curated CL mappings, check Milestone 3 progress, or re-verify candidate_cl_mappings.sssom.tsv after a CL or SOULCAP sheet update.
---

You re-verify mappings that were already curated in
[reports/candidate_cl_mappings.md](../../../reports/candidate_cl_mappings.md)
and exported to
[reports/candidate_cl_mappings.sssom.tsv](../../../reports/candidate_cl_mappings.sssom.tsv),
checking whether the evidence that justified each one still holds. See
[ROADMAP.md](../../../ROADMAP.md) Milestone 3, which this implements in
reduced form: the milestone was scoped assuming the Google Sheet would gain
dedicated broad/exact-match and comment columns first, blocking it entirely.
Those columns would sharpen *curator intent* capture, but the audit itself —
"does the evidence for this mapping still check out?" — doesn't strictly need
them. Say explicitly, in whatever you produce, that this is the
evidence/drift half of M3, not the full milestone as originally scoped.

## When to invoke

The user asks to audit curated CL mappings, wants a Milestone 3 progress
check, or just re-ran `soulcap-cl-pro` (CL's marker axioms can change between
Ubergraph syncs) or `soulcap-sync` (the SOULCAP sheet can change too) and
wants to know if any existing mapping is now stale.

## Tools

- `src/soulcap_cl_mapping/sssom_export.py`'s `CURATED_MAPPINGS` table — the
actual curated entries, each with `abbreviation`, `cl_id`, `cl_label`,
`match_type` (`Exact`/`Broad`), optional `evidence_override`, `uncertain`,
and `note`. This is the ground truth for *what was decided*; the generated
`.sssom.tsv` is its rendered output.
- `sssom_export.load_assertion_status()` / `classify_evidence()` — classify a
CL ID's current marker-axiom support as `confirmed` (directly asserted),
`inferred_only`, or `no_marker_axiom`, from
`reports/cl_pro_relationships.tsv`.
- `reports/literature/*.md` and `reports/pro_marker_species_support.tsv` — the
literature side of the evidence a mapping might rely on, especially for
species-specific marker claims.
- `uv run soulcap-match --req-excl ... --ideal-excl ... --req-pheno ...
--ideal-pheno ... --parent "..."` — re-run a SOULCAP profile's marker-axiom
scoring against the *current* CL data to see if the picture has changed
since the mapping was curated.
- `uv run soulcap-lookup --id CL:XXXXXXX` — confirm a term hasn't been
renamed, deprecated, or replaced since curation.

## Step 1 — recompute evidence classification for every curated entry

```bash
uv run python -c "
from soulcap_cl_mapping.sssom_export import CURATED_MAPPINGS, load_assertion_status, classify_evidence
status = load_assertion_status()
for e in CURATED_MAPPINGS:
evidence = e.get('evidence_override') or classify_evidence(e['cl_id'], status)
print(e['abbreviation'], e['cl_id'], e['match_type'], evidence, e.get('uncertain', False))
"
```

This reproduces exactly what `soulcap-sssom` computes today. The audit's job
is to question it, not just print it — for each row, ask:

1. **Does `match_type` still make sense given `evidence`?** An `Exact` match
resting on `no_marker_axiom` (lexical-only) is structurally weaker than its
label implies — flag it, even though `sssom_export.py` already discounts
its numeric confidence (0.55 vs. 0.9 for a confirmed Exact match). The
*label* still reads as "Exact" to anyone skimming `candidate_cl_mappings.md`.
2. **Has the CL side moved?** Re-fetch the CL ID with `soulcap-lookup --id` —
check the label and definition still match what `candidate_cl_mappings.md`
describes. CL is actively maintained (see issues #12–#14, all filed against
terms this project maps to); a maintainer accepting one of those upstream
fixes changes the term's markers/definition/label out from under a mapping
made before the fix landed.
3. **Does literature actually back a species-specific claim the mapping
relies on?** Cross-check against `reports/pro_marker_species_support.tsv`
— if a mapping leans on a marker that TSV records as
`insufficient_evidence` or a different species than SOULCAP's row assumes,
that's a real finding, not noise.
4. **Was it marked `uncertain` for a reason that's since been resolved (or
gotten worse)?** Re-read the `note` field and check whether the ambiguity
it describes is still accurate.

## Step 2 — re-score against current marker axioms for anything suspicious

When Step 1 flags a row, re-run the actual profile (pull the four marker
columns for that `abbreviation` from `data/marker_combinations.csv`) through
`soulcap-match` and compare the result to what's recorded in
`candidate_cl_mappings.md`. A new `contradictions` entry or a `disqualified`
result that wasn't there before is the clearest possible signal something
changed.

## Step 3 — write findings, don't silently fix

This audit's output is a report, not an automatic rewrite of curated mappings
— changing a curated match_type or CL ID is `soulcap-cl-matching`'s job (or a
human decision), not this skill's. Write findings to a new
`reports/mapping_audit.md`, one entry per flagged mapping:

```markdown
## <Abbreviation> — <Full Name> → `CL:XXXXXXX`

**Curated as:** <match_type>, evidence=<evidence tier>, confidence=<value>

**Finding:** <what changed or what doesn't hold up, with the specific
evidence — a soulcap-lookup diff, a soulcap-match re-run, a
pro_marker_species_support.tsv row>

**Suggested action:** <re-curate via soulcap-cl-matching / downgrade
match_type / log as a new gaps.tsv row / no action needed, false alarm>
```

If this is the first run, add `reports/mapping_audit.md` to
[CLAUDE.md](../../../CLAUDE.md)'s repository layout table and to
[ROADMAP.md](../../../ROADMAP.md) Milestone 3 as its (partial) deliverable —
new artifacts get linked from both, per repo convention.

## Step 4 — flag anything that's actually a full M3 blocker

If a finding can't be resolved without the sheet's still-missing broad/exact +
comment columns (e.g. a curator's original *intent* is ambiguous and the
mapping's own `note` doesn't disambiguate it), don't force an answer — log it
as exactly the kind of case that's still blocked, so the roadmap accurately
shows partial progress plus a real remaining blocker rather than looking
either fully done or fully stuck.

## Known limitations

- This audits `CURATED_MAPPINGS` (the signed-off subset, currently 87 of 127
SOULCAP rows) — it says nothing about rows that don't have a curated
mapping yet at all. That's Milestone 4's remaining work, not this skill's.
- `classify_evidence()` only sees `reports/cl_pro_relationships.tsv`, which is
itself a snapshot from the last `soulcap-cl-pro` run against Ubergraph — run
that first if CL upstream has plausibly changed (e.g. one of issues #12–#14
was recently merged) before trusting a "nothing changed" result.
- This is genuinely a reduced version of Milestone 3 as scoped in
`ROADMAP.md` — it catches drift and re-validates evidence, but it cannot
check curator intent (was this *meant* to be broad or exact?) the way the
sheet's planned dedicated columns would. Don't report a clean audit run as
"Milestone 3 done."
79 changes: 79 additions & 0 deletions .claude/skills/roadmap-status-sync/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
name: roadmap-status-sync
description: Reconcile ROADMAP.md's milestone status markers (⬜/🟡/✅/⛔) against what's actually committed in the repo, and rewrite whichever are stale. Invoke when asked to update/check the roadmap's status, before reporting project progress to someone outside the repo, or whenever a milestone's deliverable changes.
---

You check whether [ROADMAP.md](../../../ROADMAP.md)'s status markers still
match reality, and fix them when they don't. This exists because they went
stale once already: Milestones 1, 2, and 4 sat marked ⬜ "not started" for
weeks after real deliverables had already landed, and nobody caught it until
an unrelated audit. Don't let that repeat — the roadmap is what gets shown to
people outside this repo (advisors, collaborators); it should never undersell
or oversell progress.

## When to invoke

The user asks to check or update `ROADMAP.md`'s status, wants an accurate
progress summary before a meeting/report, or just finished work that plausibly
changes a milestone's completion state.

## Tools

- `ROADMAP.md` — the file being audited. Status legend: ⬜ not started ·
🟡 in progress · ✅ done · ⛔ blocked.
- `uv run soulcap-sync` — re-pull the Google Sheet before checking anything
that depends on its current column set (this is a network call and
overwrites the gitignored `data/` cache — safe, but mention you're doing it).
- File/row counts under `marker_mappings/`, `reports/`, `reports/literature/`.
- `gh issue view <N>` — for the CL term corrections table, whose "Status"
column tracks upstream `obophenotype/cell-ontology` issues that this repo
doesn't control the resolution of.

## Step 1 — recompute each milestone's real status

Don't infer from memory or from the current emoji — recompute from what's on
disk, every time:

| Milestone | Evidence to check | How to read it |
|---|---|---|
| M1 — marker → protein → gene | `marker_mappings/marker_protein_gene.csv` exists and has rows | Empty/missing → ⬜. Has rows but visibly incomplete (spot-check a few marker tokens from `data/marker_combinations.csv` against it) → 🟡. Covers essentially all distinct tokens → ✅. |
| M2 — literature support | Count `reports/literature/*.md` files against the distinct `Category` values in `data/citation_mgr.csv` (re-sync first if `data/` looks stale) | 0 files → ⬜. Some but not all categories covered → 🟡 (state the fraction, e.g. "8/14"). All categories covered → ✅. |
| M3 — audit curated mappings | Whether the Google Sheet now has dedicated broad/exact-match and comment columns beyond today's `Type of Match` (assay-type, not CL-match-type), `OLS CL identifier`, and `CL Mapping Notes` — check the freshly-synced sheet's actual column headers, don't assume from the column *names* alone (`Type of Match` sounds relevant but is not the same field per the roadmap's own note) | Columns still absent → stays ⛔, but if the `mapping-audit` skill has since made a drift/evidence-based audit possible without those columns, say so explicitly rather than leaving it a flat "blocked with nothing happening." |
| M4 — candidate mappings | Row count in `reports/candidate_cl_mappings.sssom.tsv` (minus header) against total rows in `data/marker_combinations.csv` (minus header) | Partial coverage → 🟡 with the fraction. All SOULCAP rows covered (including documented "no reasonable match" cases logged in `gaps.tsv`, which count as resolved, not missing) → ✅. |
| CL term corrections table | `gh issue view <N>` for each row's linked repo issue, and the upstream `obophenotype/cell-ontology` issue if linked | If the upstream issue is closed/merged, the table's "Status" cell is stale — update it. |

## Step 2 — diff against what the file currently says

Read the current milestone headers (`## Milestone N — ... <emoji>`) and the CL
term corrections table's Status column. List every mismatch between Step 1's
computed status and what's written, with the evidence for each.

## Step 3 — confirm before rewriting anything non-trivial

A pure emoji swap backed by unambiguous evidence (e.g. a deliverable file that
plainly didn't exist before now does) can be applied directly. Anything
judgment-based — 🟡 vs ✅ on a milestone that's "mostly" done, or whether a
partially-blocked milestone should flip to 🟡 — should be confirmed with the
user first, since this file represents the project to people outside it.

## Step 4 — write the update

Edit the milestone heading's emoji and, where useful, add a short parenthetical
with the evidence (e.g. `## Milestone 2 — Literature support for markers 🟡
(8/14 categories)`) so the fraction doesn't silently go stale again the same
way the flat emoji did. Update the CL term corrections table's Status cell the
same way when an upstream issue has moved.

## Known limitations

- This only checks the four numbered milestones and the CL term corrections
table — it doesn't audit the "Cross-cutting dependencies" section, which is
qualitative by nature.
- M2's category list comes from `data/citation_mgr.csv`'s `Category` column,
which mixes true cell-type families (NK cell, B cell, monocyte, ...) with at
least one cross-cutting method category (`immunoprofiling`) that doesn't map
to a single `reports/literature/*.md` file — don't count it against M2's
denominator; use judgment on any other ambiguous category the same way.
- A "done" `data/` count is only as fresh as the last `uv run soulcap-sync` —
always re-sync before trusting M2/M3/M4 numbers if the last sync date is
unclear or more than a few days old.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ wheels/

# Test / coverage / type-check caches
.pytest_cache/
.test-runs/
.uv-cache/
.ruff_cache/
.mypy_cache/
.coverage
Expand Down
Loading
Loading