issue-reconciler
Skill0 starsChanged 4 months agoMITView on GitHub
Finds GitHub issues that are actually already resolved but were never closed — either because a merged PR said "Closes #N" and the auto-close didn't fire, or because the thing the issue asked for now exists in the repo (a skill got built, a bug got fixed) without anyone ever linking the PR that did it. Two passes, not one: a deterministic scan (${CLAUDE_PLUGIN_ROOT}/scripts/find_closing_references.py) for explicit-but-failed closing keywords, then a judgment pass over what's left for thematic matches the mechanical scan structurally can't catch. Never closes an issue without presenting the evidence and getting confirmation first. Deliberately excludes issues that self-identify as intentionally open (a "[blocked-human]" title prefix, or a "Status: deferred/blocked" section) — those are tracked on purpose, not stale. Use when the user wants to clean up a stale or growing issue backlog, asks "which of my issues are actually done," "check if any issues are already resolved," "close issues that got fixed," or wants their tracker kept honest without manually cross-checking every issue against every PR. Triggers on "check my issues," "reconcile issues," "clean up the issue tracker," "issue-reconciler," and the script find_closing_references.py.
In this repository19 other files it gives its agents.
- agent-evalSkill
- agent-redteamSkill
- broadcastSkill
- deid-reid-harnessSkill
- fiction-workshopSkill
- privacy-linterSkill
- privacy-threat-oracleSkill
- repo-pincerSkill
- research-ledgerSkill
- review-and-planSkill
- style-obfuscatorSkill
- wiki-governorSkill
- wiki-librarianSkill
- wiki-operatorSkill
- wiki-privacy-auditSkill
- wiki-synthesizerSkill
- wiki-teacherSkill
- wiki-warehouseSkill
- my-skill-nameSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 94
Any form of auto-closing: Every close is proposed with evidence and confirmed by a human, every time, no exceptions, no --yes flag.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Issue Reconciler
An open issue is a claim: "this still needs doing." Like any claim, it can go stale — the thing gets done, but nobody tells the tracker. This skill finds the gap between what your issues say is still open and what your repo's actual state and PR history say really happened.
Why this is two passes, not one
Pass 1 (mechanical, ${CLAUDE_PLUGIN_ROOT}/scripts/find_closing_references.py) catches the unambiguous
case: a merged PR's body used GitHub's own closing-keyword syntax ("Closes #6", "Fixes
#12", "Resolves #3") and the issue is nonetheless still open. That's not a judgment
call — GitHub's auto-close didn't fire (the keyword was added after merge, a squash
edge case) or nobody noticed. Pure pattern-matching, zero reasoning needed, which is
exactly why it's a script and not part of the conversational methodology.
Pass 2 (judgment, conversational) catches everything Pass 1 structurally can't: an issue whose ask was fulfilled without anyone ever using a closing keyword at all. This is the more common case in practice, not the edge case — running Pass 1 against every open issue and all 189 merged PRs here found zero matches, because none of those PRs used "Closes #N" syntax. Only a judgment pass, checking the issue's actual ask against actual repo state, can find the rest.
It is also where the false positives live. Issue #6 ("Build wiki-teacher skill") looks
resolved at a glance: skills/wiki-teacher/ exists. It is not. The skill that exists
is a /checkin project-accountability tool; #6 asked for quizzes, spaced repetition,
/study, /flashcards and /study-plan, none of which is built. A name match is
not a scope match. Pass 2 must compare the issue's listed responsibilities or
acceptance criteria against what the artifact actually does, not just whether
something with the same name exists.
How this works
gh issue list --state open --limit 500 --json number,title,body,createdAt,labels > issues.json
gh pr list --state merged --limit 500 --json number,title,body,mergedAt > merged_prs.json
python ${CLAUDE_PLUGIN_ROOT}/scripts/find_closing_references.py --issues issues.json --prs merged_prs.jsonAlways pass --limit. gh returns only 30 rows by default, so the bare commands silently scan the newest 30 PRs and miss the rest (this repo had 189 merged PRs when that was found on 2026-10-31). The script warns on stderr when an input has exactly 30 rows.
(No GitHub API client lives in this skill — it consumes JSON that whatever GitHub
tooling the host already has produces, gh CLI or an MCP server's issue/PR list
calls. Same "consume data, don't reimplement the fetch" convention
privacy-linter's scan_log_history.py already uses. One real quirk hit building
this: at least one GitHub MCP tool's PR-list response left its own merged boolean
field false on every row while merged_at was populated correctly — the script reads
mergedAt/merged_at directly rather than trusting a merged flag, so it isn't
fooled by a host tool with this same quirk.)
- Run Pass 1. Deterministic, cheap, always run first — it eliminates the obvious cases before any conversational effort goes into the harder ones.
- For every open issue Pass 1 didn't already flag, check two things:
- Is this issue meant to stay open? Skip it from staleness consideration
entirely if the title starts with
[blocked-human], or the body has a## Statussection saying deferred/blocked/not being worked on. These are tracked on purpose — age alone is not evidence of staleness for them. - Does its actual ask match current reality? For an issue that names a
specific skill, file, or feature (
skills/<name>/, a described flag or function in a named script), check whether it exists now and covers each responsibility or acceptance criterion the issue lists — read the artifact, not just its name. For an issue describing a bug or gap, check whether recent merged PRs — even ones that never used a closing keyword — describe fixing the same thing.
- Is this issue meant to stay open? Skip it from staleness consideration
entirely if the title starts with
- Report findings in two tiers, never as one flat list:
- High confidence — a Pass 1 finding (explicit closing keyword, issue still open), or a named artifact that now unambiguously exists exactly as the issue described it.
- Worth a look — a thematic match to a merged PR without an explicit link, where a human should confirm the PR actually addressed the issue's real ask before closing it.
- Propose, never auto-close. Present the evidence (which PR, which file, which
line) and wait for confirmation — closing an issue is a visible, public action,
the same "propose redaction, never auto-apply" discipline
privacy-linterandwiki-privacy-auditalready hold for their own visible actions. On confirmation, close with a reason (completed, not the bare default) and a comment naming the evidence, so the closed issue still carries an audit trail for anyone who finds it later. - Going forward, not just retroactively: suggest using
Closes #N/Fixes #Nin new PR descriptions when they really do resolve a tracked issue — free, native, and it's what would have caught issue #6 immediately instead of months later.
What's NOT built here
- Cross-repo issue linking.
find_closing_references.pyonly recognizes the same-repo#Nform, notowner/repo#N. Rare enough for a personal skills repo that it wasn't worth the regex complexity yet — a documented scope limit, not a silent gap. - Any form of auto-closing. Every close is proposed with evidence and confirmed
by a human, every time, no exceptions, no
--yesflag. - Fuzzy/semantic matching as code. Pass 2's "does this PR's actual change address
this issue's actual ask" judgment is deliberately conversational, not a string-
similarity script — the same reasoning
repo-pinceruses for why its own reconciliation is a documented methodology, not a classifier.
Pairing
- repo-pincer — the closest sibling: both reconcile a claim against reality and
classify the discrepancy. The boundary:
repo-pinceraudits whether documentation matches code, run deliberately and occasionally for onboarding or vendor evaluation. This skill audits whether the issue tracker matches reality, meant to run often and stay lightweight — routine hygiene, not a deep audit. Different cadence, different artifact pair, kept separate rather than folded into one skill for the same reason MECE and pre-mortem stayed separate fromagent-eval.
Files
| Path | What it is |
scripts/find_closing_references.py | Pass 1 — deterministic closing-keyword cross-reference |
scripts/test_find_closing_references.py | Unit + CLI test suite (stdlib unittest) |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

