oh-my-issues
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseoh-my-issues
oh-my-issues
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 , 1:1.
open issues == open plans将问题积压转化为路线图。问题是症状数据,而非工作单元——工作单元是产生这些症状的架构缺陷。最终状态是,1:1对应。
开放问题 == 开放计划Core principle
核心原则
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 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.
plans/0X-*.mdThis 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.
停止逐个关闭问题。将共享单一架构修复方案的症状归为一个聚类,为该聚类创建一个标准主条目(一个计划主问题 + 一份设计文档),用标准化重定向评论关闭所有子问题,并为每个聚类提交一个PR以一次性关闭所有子问题。新提交的bug将作为“第N轮”评论添加到匹配的主问题中,而非作为新的追踪问题创建。
plans/0X-*.md这种方式有三重优势:架构修复可以消除整个症状类别;计划的测试矩阵在CI中固化了预防措施;标准化分类降低了后续问题处理的成本。
When to use
使用场景
- The repo has 20+ open issues and many feel like duplicates or platform-specific symptoms of the same defect.
- The user asks to "triage", "consolidate", "cluster", "dedupe", "group", or "make a plan from" the issue list.
- A new bug is filed and the user wants to know whether it belongs to existing work.
- The user wants to ship a focused PR that resolves a cluster of related issues.
- 仓库有20+个开放问题,其中许多看起来是重复问题,或是同一缺陷在不同平台上的症状表现。
- 用户要求对问题列表进行“分类”“合并”“聚类”“去重”“分组”或“制定计划”。
- 提交了新bug,用户想知道它是否属于现有工作范畴。
- 用户希望提交一个针对性PR来解决一组相关问题。
When NOT to use
不适用于以下场景
- Fewer than ~15 open issues: just close them.
- Issues are genuinely independent (no shared root causes): one fix per issue is correct.
- The repo lacks discipline and the user does not want to introduce one — propose first, do not impose.
plans/
- 开放问题少于约15个:直接关闭即可。
- 问题完全独立(无共享根本原因):每个问题对应一个修复方案是正确的做法。
- 仓库没有文档规范,且用户不想引入该规范——先提议,不要强制推行。
plans/
Three modes
三种模式
Mode 1: Cluster pass (initial reduction)
模式1:聚类处理(初始精简)
Use when the backlog has never been consolidated. Goal: go from N issues to N_plans masters in one operation.
- Read everything in full. Fetch every open issue's body and its comment thread — not just titles. Surface-level grouping fails without full text, and reproduction steps, linked duplicates, and diagnostic output often live in comments rather than the original body. See "GitHub CLI primitives" below for the correct paginated listing + per-issue comment fetch (a single call does not return comment bodies).
gh issue list - Cluster by root cause, not by surface. The clustering question is would one architectural change retire all of these? — not do these mention the same word?. "Windows" is a surface; "spawn contract violated by host shells" is a root cause. Two issues with different surfaces can share a cluster (e.g. an env-var leak in two different code paths sharing one missing env-isolation boundary).
- Name each cluster as an architectural problem. Title format: . Example:
[plan-XX] <Architectural Defect> — <one-line scope>. The title must imply a fix, not a topic.[plan-02] Spawn-Contract Templating — canonical ${CLAUDE_PLUGIN_ROOT} resolution across all hosts - Open one master issue per cluster with a body that lists: the architectural defect, the children (by issue number), the fix sequence, and a required test matrix (host × IDE × shell, etc.) that prevents regression.
- Mirror each master as in the repo. The issue is the public tracker; the doc is the design. They reference each other.
plans/0X-<slug>.md - Close every child with the standardized redirect comment (see below) and state .
not planned - Verify end state: returns exactly the masters and nothing else.
gh issue list --state open
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.
适用于从未进行过合并的问题积压。目标:通过一次操作将N个问题精简为N_plans个主问题。
- 完整阅读所有内容:获取每个开放问题的正文及其评论线程——不只是标题。不阅读全文的话,表层分组会失效,重现步骤、关联的重复问题和诊断输出通常存在于评论而非原始正文中。请参阅下方的“GitHub CLI 基础命令”获取正确的分页列表+逐问题评论获取方式(单次调用不会返回评论内容)。
gh issue list - 按根本原因聚类,而非表层现象:聚类的判断标准是一个架构变更能否解决所有这些问题?——而非这些问题是否提到了同一个词?。“Windows”是表层现象;“主机Shell违反Spawn协议”是根本原因。两个表层不同的问题可能属于同一聚类(例如,两个不同代码路径中的环境变量泄漏,共享同一个缺失的环境隔离边界)。
- 将每个聚类命名为架构问题:标题格式:。示例:
[plan-XX] <架构缺陷> — <一行描述范围>。标题必须隐含修复方案,而非仅提及主题。[plan-02] Spawn协议模板化 — 跨所有主机统一解析${CLAUDE_PLUGIN_ROOT} - 为每个聚类创建一个主问题,正文需包含:架构缺陷、子问题编号列表、修复步骤序列,以及防止回归所需的测试矩阵(主机×IDE×Shell等)。
- 在仓库中为每个主问题创建对应的文档。问题是公开追踪条目;文档是设计方案。两者互相引用。
plans/0X-<slug>.md - 关闭所有子问题,并附上标准化重定向评论(见下方),标记为(未计划)。
not planned - 验证最终状态:仅返回主问题,无其他内容。
gh issue list --state open
对于约100个问题的目标形态:4–8个主问题。超过10个意味着你是按表层现象聚类;少于3个意味着聚类范围过宽,无法作为单个PR提交。
Mode 2: Triage (new incoming bug, steady state)
模式2:分类处理(新bug提交,稳定状态)
Use when a new issue is filed after consolidation is in place. Goal: never let the issue list re-accumulate.
- Read the new issue's body in full.
- Pattern-match the symptom against existing plan masters. For each open master, ask: would the fix described here also fix this new bug? If yes → it belongs to that plan.
- If a match exists, post a "Round N" comment on the master that:
- Names the new child by number
- Describes the symptom in one line
- Sketches the concrete fix (1–3 lines, e.g. "guard with ")
case "$_SH" in /*.exe|"") _SH=bash ;; esac - Adds any new test-matrix cell the bug exposes
- Close the child with the standardized redirect comment, .
not planned - If no match exists and the bug is genuinely novel: open a new plan master + . Resist this. Most bugs are children of existing plans.
plans/0X-*.md
适用于合并完成后提交的新问题。目标:不让问题列表再次积压。
- 完整阅读新问题的正文。
- 将症状与现有计划主问题匹配。对于每个开放的主问题,问:*此处描述的修复方案是否也能解决这个新bug?*如果是→它属于该计划。
- 如果找到匹配项,在主问题上发布“第N轮”评论,内容包括:
- 列出新子问题的编号
- 用一行描述症状
- 简述具体修复方案(1–3行,例如:"用添加防护")
case "$_SH" in /*.exe|"") _SH=bash ;; esac - 添加该bug暴露的新测试矩阵单元
- 关闭子问题,附上标准化重定向评论,标记为。
not planned - 如果没有匹配项且该bug确实是新问题:创建一个新的计划主问题 + 文档。尽量避免这种情况,大多数bug都是现有计划的子问题。
plans/0X-*.md
Mode 3: Bundle (ship the cluster)
模式3:打包处理(交付聚类问题的修复)
Use when a plan slice is ready to ship. Goal: one PR closes N children atomically.
- List the master's children. From the master body and consolidation comments, collect every child issue number routed to this plan.
- Verify each child's symptom is covered by the architectural fix in the PR. If a child is not covered, the PR is not ready or that child belongs in a different plan.
- Generate the PR description: title is the plan slice (e.g. "fix(spawn): canonical ${CLAUDE_PLUGIN_ROOT} resolution"); body lists every child with so GitHub auto-closes them on merge.
Closes #N - Add the test matrix from the plan to CI in the same PR. Without the matrix, the cluster will re-emerge.
- After merge, the master issue can be closed only if every child was covered. If the plan has remaining scope, leave the master open and link the PR as a partial-shipping checkpoint.
适用于计划的某一部分准备交付时。目标:一个PR一次性关闭N个子问题。
- 列出主问题的所有子问题。从主问题正文和合并评论中,收集所有归入该计划的子问题编号。
- 验证每个子问题的症状都被PR中的架构修复覆盖。如果某个子问题未被覆盖,说明PR尚未准备好,或者该子问题属于其他计划。
- 生成PR描述:标题为计划的部分内容(例如:"fix(spawn): 统一解析${CLAUDE_PLUGIN_ROOT}");正文列出所有子问题,每个子问题前加上,以便GitHub在合并时自动关闭它们。
Closes #N - 在同一个PR中添加计划中的测试矩阵到CI。没有测试矩阵的话,聚类问题会再次出现。
- 合并后,只有当所有子问题都被覆盖时,才能关闭主问题。如果计划还有剩余范围,保持主问题开放,并将PR作为部分交付的检查点链接起来。
Naming a plan master
计划主问题的命名
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.
计划主问题的标题必须隐含其修复方案。
| 错误命名(表层现象) | 正确命名(架构层面) |
|---|---|
| Windows bugs | 跨主机Spawn协议模板化 |
| Worker crashes | Worker/守护进程生命周期强化 — 监控、健康检查、重试 |
| Auth issues | Worker环境隔离 — 从SDK子进程中移除主机CLI环境变量 |
| Install failures | 安装失败透明度 — 跨IDE错误分类 + 12×4测试矩阵 |
如果你无法写出一行架构层面的范围描述,说明聚类存在问题。
The standardized redirect comment
标准化重定向评论
Use this exact phrasing on every child closure. Consistency lets contributors recognize the pattern at a glance and keeps the audit trail searchable.
text
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 ) — the child was a symptom, not a unit of work.
not plannedcompleted关闭每个子问题时,请使用以下精确措辞。一致性让贡献者一眼就能识别模式,并保持审计线索可搜索。
text
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.标记为(未计划)而非(已完成)——子问题是症状,而非工作单元。
not plannedcompletedGitHub CLI primitives
GitHub CLI 基础命令
Resolve repo:
bash
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:
- returns only a count placeholder, not the comment bodies. You must fetch comments per issue with
gh issue list --json comments.gh issue view <N> --json comments - Any explicit silently truncates if the backlog is larger. Always check the total open count first.
--limit
bash
undefined解析仓库:
bash
repo_json=$(gh repo view --json owner,name)
owner=$(jq -r '.owner.login // .owner.name' <<<"$repo_json")
repo=$(jq -r '.name' <<<"$repo_json")列出所有开放问题(完整阅读步骤)。两个注意事项:
- 仅返回计数占位符,而非评论内容。你必须使用
gh issue list --json comments逐问题获取评论。gh issue view <N> --json comments - 任何显式的参数如果小于积压问题数量,会静默截断结果。请始终先检查开放问题的总数。
--limit
bash
undefined1. Confirm total — never trust an arbitrary --limit.
1. 确认总数 — 永远不要信任任意设置的--limit。
Note: GitHub's REST API treats PRs as issues, so .open_issues_count
注意:GitHub的REST API将PR视为问题,因此/repos/{owner}/{repo}返回的.open_issues_count
from /repos/{owner}/{repo} is actually issues + PRs. Use the search
实际上是问题 + PR的数量。使用搜索API获取仅问题的数量。
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"
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)
2. 列出问题正文(将--limit设置为等于或大于实际总数)
gh issue list --state open --limit "$total"
--json number,title,body,labels,author,createdAt
--json number,title,body,labels,author,createdAt
gh issue list --state open --limit "$total"
--json number,title,body,labels,author,createdAt
--json number,title,body,labels,author,createdAt
3. For each issue, fetch its full comment thread
3. 为每个问题获取完整的评论线程
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
--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:
```bash
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-02Post the consolidation comment + close the child:
bash
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:
bash
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:
bash
gh issue list --state open --json number,title \
| jq -r '.[] | "\(.number)\t\(.title)"'Output should be exactly the plan masters.
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
--jq '.comments[] | "(.author.login) ((.createdAt)): (.body)"' done
如果`total > 1000`,通过REST API分页:循环调用`gh api "repos/$owner/$repo/issues?state=open&per_page=100&page=N"`直到结果数组为空(注意这包含PR,因此需要过滤`select(.pull_request|not)`)。
创建计划主问题:
```bash
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发布合并评论并关闭子问题:
bash
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"在主问题上添加“第N轮”分类评论:
bash
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
)"验证最终状态:
bash
gh issue list --state open --json number,title \
| jq -r '.[] | "\(.number)\t\(.title)"'输出应仅包含计划主问题。
Plan master body template
计划主问题正文模板
Save as and use as for the master issue.
plans/0X-<slug>.md--body-filemarkdown
undefined保存为,并作为主问题的使用。
plans/0X-<slug>.md--body-filemarkdown
undefined[plan-XX] <Architectural Defect> — <one-line scope>
[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>
- ...
- #N — <一行症状描述>
- #N — <一行症状描述>
- ...
Fix sequence
修复步骤序列
- <First architectural change — bounded, reviewable>
- <Second>
- ...
- <第一个架构变更 — 边界清晰、可评审>
- <第二个>
- ...
Test matrix
测试矩阵
| Axis A | Axis B | Required behavior |
|---|---|---|
| ... | ... | ... |
The matrix lives in CI. A future regression must fail CI before a user can file.
| 轴A | 轴B | 要求的行为 |
|---|---|---|
| ... | ... | ... |
该矩阵存在于CI中。未来的回归必须在用户提交问题前就导致CI失败。
Out of scope
范围外内容
<What this plan deliberately does not cover, with pointers to other plan masters.>
undefined<本计划明确不涵盖的内容,以及指向其他计划主问题的链接。>
undefinedHealth checks
健康检查
Run periodically against the plan masters to catch the failure modes.
- Graveyard master: master issue has accumulated 5+ "Round N" comments without a shipping PR. The plan needs a forcing PR or it must be split.
- Over-broad master: the children's fixes cannot fit one PR. Split into two plans with narrower scope.
- Surface-clustered master: the children share a topic but not a fix. Re-cluster by root cause; some children belong to different plans.
- Drift between issue and doc: the plan master body and disagree. Pick one as canonical (the doc) and regenerate the issue body from it.
plans/0X-*.md
定期针对计划主问题运行,以发现故障模式。
- “墓地”主问题:主问题积累了5+条“第N轮”评论但未提交交付PR。该计划需要一个强制推进的PR,或者必须拆分。
- 范围过宽的主问题:子问题的修复无法放入一个PR中。拆分为两个范围更窄的计划。
- 表层聚类的主问题:子问题共享主题但不共享修复方案。按根本原因重新聚类;部分子问题属于其他计划。
- 问题与文档不一致:计划主问题正文与文档内容不符。选择其中一个作为标准(文档),并据此重新生成问题正文。
plans/0X-*.md
Stop conditions
停止条件
For a cluster pass: stop when returns exactly the masters.
gh issue list --state openFor 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对于聚类处理:当仅返回主问题时停止。
gh issue list --state open对于分类处理:当新子问题被关闭且主问题添加了第N轮条目时停止。
对于打包处理:当PR被合并且所有列出的子问题通过自动关闭时停止。
Closes #NFailure modes worth refusing
需要避免的故障模式
- Premature clustering before reading every issue body in full. Don't.
- Closing children before the master is open. Children must always have a redirect target.
- Using the redirect comment for issues that aren't symptoms (e.g. genuine feature requests with no shared root cause). Those stay open or get their own track.
- Closing a master before every listed child is shipped. The master is the contract; closing it early breaks the audit trail.
- 提前聚类:在完整阅读所有问题正文之前就进行聚类。不要这样做。
- 主问题未创建就关闭子问题:子问题必须始终有一个重定向目标。
- 对非症状问题使用重定向评论(例如,无共享根本原因的真正功能请求)。这些问题应保持开放或单独追踪。
- 所有列出的子问题交付前关闭主问题:主问题是契约;提前关闭会破坏审计线索。