plugin/skills/oh-my-issues/SKILL.md
Turn an issue backlog into a roadmap. Issues are symptom data, not units of work — the unit of work is the architectural defect that produces them. The end state is open issues == open plans, 1:1.
Stop closing issues one at a time. Group symptoms that share a single architectural fix into a cluster, give the cluster one canonical home (a plan-master issue + a plans/0X-*.md design doc), close every child with a standardized redirect, and ship one PR per cluster that closes all children atomically. New incoming bugs get appended to the matching master as a "Round N" comment, not opened as new tracked issues.
This compounds three ways: architectural fixes retire whole symptom families, the plan's test matrix institutionalizes prevention in CI, and standardized triage makes residual inflow cheap.
plans/ discipline and the user does not want to introduce one — propose first, do not impose.Use when the backlog has never been consolidated. Goal: go from N issues to N_plans masters in one operation.
gh issue list call does not return comment bodies).[plan-XX] <Architectural Defect> — <one-line scope>. Example: [plan-02] Spawn-Contract Templating — canonical ${CLAUDE_PLUGIN_ROOT} resolution across all hosts. The title must imply a fix, not a topic.plans/0X-<slug>.md in the repo. The issue is the public tracker; the doc is the design. They reference each other.not planned.gh issue list --state open returns exactly the masters and nothing else.Target shape for ~100 issues: 4–8 masters. More than 10 means you're clustering by surface; fewer than 3 means clusters are too broad to ship as one PR each.
Use when a new issue is filed after consolidation is in place. Goal: never let the issue list re-accumulate.
case "$_SH" in /*.exe|"") _SH=bash ;; esac")not planned.plans/0X-*.md. Resist this. Most bugs are children of existing plans.Use when a plan slice is ready to ship. Goal: one PR closes N children atomically.
Closes #N so GitHub auto-closes them on merge.A plan-master title must imply its fix.
| Bad (surface) | Good (architectural) |
|---|---|
| Windows bugs | Spawn-Contract Templating across hosts |
| Worker crashes | Worker / Daemon Lifecycle Hardening — supervision, health, retry |
| Auth issues | Worker Env Isolation — strip host CLI env from the SDK subprocess |
| Install failures | Installer Failure Transparency — cross-IDE error taxonomy + 12×4 test matrix |
If you cannot write a one-line architectural scope, the cluster is wrong.
Use this exact phrasing on every child closure. Consistency lets contributors recognize the pattern at a glance and keeps the audit trail searchable.
Consolidating into #<MASTER> (plan-XX). The root cause and fix sequencing are tracked there alongside the rest of the cluster — please follow that issue for progress.
Close as not planned (not completed) — the child was a symptom, not a unit of work.
Resolve repo:
repo_json=$(gh repo view --json owner,name)
owner=$(jq -r '.owner.login // .owner.name' <<<"$repo_json")
repo=$(jq -r '.name' <<<"$repo_json")
List all open issues (the read-everything pass). Two gotchas:
gh issue list --json comments returns only a count placeholder, not the comment bodies. You must fetch comments per issue with gh issue view <N> --json comments.--limit silently truncates if the backlog is larger. Always check the total open count first.# 1. Confirm total — never trust an arbitrary --limit.
# Note: GitHub's REST API treats PRs as issues, so .open_issues_count
# from /repos/{owner}/{repo} is actually issues + PRs. Use the search
# API to get the issue-only count.
total=$(gh api "search/issues?q=repo:$owner/$repo+is:issue+is:open" --jq '.total_count')
echo "Open issues: $total"
# 2. List bodies (set --limit at or above the true total)
gh issue list --state open --limit "$total" \
--json number,title,body,labels,author,createdAt
# 3. For each issue, fetch its full comment thread
for n in $(gh issue list --state open --limit "$total" --json number --jq '.[].number'); do
echo "=== Issue #$n ==="
gh issue view "$n" --json comments \
--jq '.comments[] | "\(.author.login) (\(.createdAt)): \(.body)"'
done
If total > 1000, paginate via the REST API: gh api "repos/$owner/$repo/issues?state=open&per_page=100&page=N" looped until the result array is empty (note this includes PRs, so filter select(.pull_request|not)).
Open a plan master:
gh issue create \
--title "[plan-02] Spawn-Contract Templating — canonical \${CLAUDE_PLUGIN_ROOT} resolution across all hosts" \
--body-file plans/02-spawn-contract-templating.md \
--label plan,plan-02
Post the consolidation comment + close the child:
gh issue comment <CHILD> --body "Consolidating into #<MASTER> (plan-XX). The root cause and fix sequencing are tracked there alongside the rest of the cluster — please follow that issue for progress."
gh issue close <CHILD> --reason "not planned"
Append a "Round N" triage comment to a master:
gh issue comment <MASTER> --body "$(cat <<'EOF'
**Round N consolidation**
- #<CHILD> (<one-line symptom>) folded into this plan as <classification>.
Proposed fix: <1–3 line sketch>.
Adds matrix cell: <host/IDE/shell combination>.
EOF
)"
Verify final state:
gh issue list --state open --json number,title \
| jq -r '.[] | "\(.number)\t\(.title)"'
Output should be exactly the plan masters.
Save as plans/0X-<slug>.md and use as --body-file for the master issue.
# [plan-XX] <Architectural Defect> — <one-line scope>
## Defect
<One paragraph: what is structurally broken, why it produces the observed family of symptoms.>
## Children
- #N — <symptom one-liner>
- #N — <symptom one-liner>
- ...
## Fix sequence
1. <First architectural change — bounded, reviewable>
2. <Second>
3. ...
## Test matrix
| Axis A | Axis B | Required behavior |
|---|---|---|
| ... | ... | ... |
The matrix lives in CI. A future regression must fail CI before a user can file.
## Out of scope
<What this plan deliberately does not cover, with pointers to other plan masters.>
Run periodically against the plan masters to catch the failure modes.
plans/0X-*.md disagree. Pick one as canonical (the doc) and regenerate the issue body from it.For a cluster pass: stop when gh issue list --state open returns exactly the masters.
For a triage: stop when the new child is closed and the master has a Round-N entry.
For a bundle: stop when the PR is merged and every listed child is auto-closed by Closes #N.