root-cause

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Root Cause

根本原因

The Iron Law

铁律

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
Guess-and-patch wastes hours and plants new bugs. This process applies to every technical issue — and applies hardest when it's tempting to skip: emergencies, "obvious" one-liners, and the moment right after a previous fix didn't work.
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
猜测式修补会浪费数小时时间,还会引入新Bug。此流程适用于所有技术问题——尤其是在你想跳过的场景下更需严格执行:紧急情况、“显而易见”的单行修复、以及上一次修复无效后的时刻。

Phase 1 — Build the feedback loop (the gate)

阶段1 — 构建反馈循环(准入门槛)

Before ANY theory-building, construct and RUN a red-capable signal: one command — a test invocation, a script, a curl — that is red now because of this exact bug and will go green when it is fixed. Build the right feedback loop and the bug is 90% fixed; every later phase merely consumes it.
Catch yourself reading code to form a theory before this command exists? Stop. No red-capable command, no Phase 2.
Tactics for constructing one, in rough order of preference:
  1. Failing test at whatever seam reaches the bug (unit, integration, e2e)
  2. curl / HTTP script against a running dev server
  3. CLI invocation on a fixture input, diffed against known-good output
  4. Headless browser script asserting on DOM/console/network
  5. Replay of a captured audit-trace (saved request, payload, event log) through the code path
  6. Throwaway harness: a minimal slice of the system exercising the bug path
  7. Property/fuzz loop over random inputs when output is "sometimes wrong"
  8. Bisection harness (
    git bisect run
    ) when the bug appeared between two known states
  9. Differential loop: same input through old vs new version, diff the outputs
  10. Human-in-the-loop script — last resort, only when a human must physically act; script their steps so the loop stays structured
Then tighten it: faster (cache setup, narrow scope — seconds, not minutes), sharper (assert the user's exact symptom, not "didn't crash"), deterministic (pin time, seed randomness, isolate filesystem). Non-deterministic bug? Don't chase a clean repro — raise the reproduction rate (loop the trigger 100x, add stress, shrink timing windows) until it's high enough to root-cause against.
Genuinely cannot build one? Say so explicitly, list what you tried, and ask the user for a reproducing environment, a captured artifact, or permission to add temporary instrumentation. Do not proceed on vibes.
Gate check: name the command, paste one run of its red output.
在构建任何理论之前,先构建并运行一个red-capable signal:一条命令——测试调用、脚本、curl请求——当前因该Bug显示失败(red),修复后会显示成功(green)。构建正确的反馈循环,Bug就已经解决了90%;后续所有阶段都只是基于此循环开展工作。
发现自己在这条命令完成前就开始阅读代码以形成理论?立刻停止。没有red-capable命令,就不能进入阶段2。
构建该命令的策略,按优先级从高到低排列:
  1. 在能触及Bug的任意层级编写失败测试(单元测试、集成测试、端到端测试)
  2. 针对运行中的开发服务器执行curl/HTTP脚本
  3. 对测试输入执行CLI调用,与已知正确输出对比差异
  4. 无头浏览器脚本,断言DOM/控制台/网络状态
  5. 通过代码路径重放捕获的审计跟踪(保存的请求、负载、事件日志)
  6. 临时测试工具:仅包含触发Bug路径的最小系统切片
  7. 针对随机输入的属性/模糊测试循环,适用于“输出偶尔错误”的场景
  8. 二分测试工具(
    git bisect run
    ),适用于Bug出现在两个已知状态之间的场景
  9. 差异循环:相同输入分别在旧版本和新版本中运行,对比输出差异
  10. 人工参与的脚本——最后手段,仅当必须人工操作时使用;将操作步骤脚本化,保持循环结构化
然后优化循环:更快(缓存设置、缩小范围——以秒为单位,而非分钟)、更精准(断言用户遇到的具体症状,而非“未崩溃”)、可确定(固定时间、随机种子、隔离文件系统)。遇到非确定性Bug?不要追求完美的复现方式——提高复现率(循环触发100次、增加压力、缩小时间窗口),直到复现率足够高以便进行根因分析。
确实无法构建该循环?明确说明这一点,列出你尝试过的方法,并请求用户提供复现环境、捕获的工件,或允许添加临时监控。不要凭感觉继续。
准入检查:命名该命令,粘贴一次其失败(red)输出的运行结果。

Phase 2 — Reproduce and minimise

阶段2 — 复现并最小化

Run the loop; watch it go red with the failure the user described (a nearby different failure = wrong bug = wrong fix). Read the complete error — full message, full stack trace, line numbers; errors often contain the answer. Check recent changes: git diff, new dependencies, config, environment.
Shrink the repro one cut at a time (inputs, config, callers, steps), re-running after each cut, until every remaining element is load-bearing — removing any one turns it green. A minimal repro shrinks the hypothesis space and becomes the regression test.
For multi-component systems (CI → build → deploy, API → service → DB): instrument each boundary — log what enters and exits every layer — and run once to see WHERE it breaks, before touching any fix. When the error surfaces deep in a call chain, audit-trace the bad value backward through its callers to the ORIGINAL trigger. Never fix only where the error appears; that is treating the symptom.
Retrieval is not the red-capable loop. During Phases 1–2, do not use
load-subgraph
/ retrieval as the feedback signal. The red-capable command alone gates progress.
运行循环;观察它是否因用户描述的故障显示失败(若出现附近的不同故障,则说明找错了Bug,对应的修复也会错误)。阅读完整错误信息——完整消息、完整堆栈跟踪、行号;错误信息往往包含答案。检查近期变更:git diff、新依赖项、配置、环境。
逐步缩小复现范围(输入、配置、调用方、步骤),每次修改后重新运行,直到剩余的每个元素都是必要的——移除任何一个元素都会让循环显示成功(green)。最小化的复现范围会缩小假设空间,并成为回归测试用例。
对于多组件系统(CI → 构建 → 部署,API → 服务 → 数据库):为每个边界添加监控——记录每一层的输入和输出——运行一次以确定故障发生位置,再进行任何修复操作。当错误出现在调用链深处时,跟踪错误值反向遍历调用方,找到最初的触发点。永远不要只修复错误出现的位置;那只是治标不治本。
检索并非red-capable循环。在阶段1-2期间,请勿将
load-subgraph
/检索作为反馈信号。只有red-capable命令才能管控进度。

After Phase 2 — feature-ownership context (before Phase 3)

阶段2之后 — 功能所有权上下文(阶段3之前)

When Phase 2 is complete and you have a path or stable term, and before Phase 3 hypotheses: REQUIRED SUB-SKILL: use
load-subgraph
for ownership / neighbor context (seed paths/terms;
neighbors
or
blast_radius
as fits). Grounded claims (one home): follow
skills/execution/load-subgraph/references/grounded-claims.md
— never invent root-cause hypotheses from the envelope; never skip the red loop because neighbors look suggestive.
当阶段2完成,你获得了路径或稳定术语,并且在阶段3提出假设之前:必须使用子技能:
load-subgraph
获取所有权/相关上下文(种子路径/术语;根据情况使用
neighbors
blast_radius
)。基于事实的声明(单一来源):遵循
skills/execution/load-subgraph/references/grounded-claims.md
——永远不要凭空提出根因假设;永远不要因为相关内容看起来有提示性就跳过失败循环。

Ops docs after Phase 2 (optional — never replaces the red loop)

阶段2之后的运维文档(可选——永远不能替代失败循环)

Only after Phase 2. Phases 1–2 still require a red-capable command first. Load:
skills/project/define-system-doc/consult-recipe.md
.
Paths when applicable:
docs/ops/runbooks.md
(and runbook slugs),
docs/ops/observability.md
,
docs/ops/disaster-recovery.md
— advisory Phase 3+ context only; never a substitute for the red loop. No-op when absent; suggest once
/define-system-doc ops/runbooks|observability|disaster-recovery
if material; never auto-invoke.
仅在阶段2之后使用。阶段1-2仍需先构建red-capable命令。加载
skills/project/define-system-doc/consult-recipe.md
适用路径
docs/ops/runbooks.md
(及运行手册别名)、
docs/ops/observability.md
docs/ops/disaster-recovery.md
——仅作为阶段3及之后的参考上下文;永远不能替代失败循环。若不存在则无需操作;若内容重要,可建议执行一次
/define-system-doc ops/runbooks|observability|disaster-recovery
;切勿自动调用。

External dependency evidence after Phase 2 (conditional — before Phase 3)

阶段2之后的外部依赖证据(有条件——阶段3之前)

IF the minimized failure path crosses a versioned external dependency — a library, framework, SDK, database, cache, search/observability platform, cloud service, external API, CLI, provider distribution, or protocol — THEN complete this evidence gate before writing hypotheses. The Phase 1 signal and Phase 2 minimal repro remain the gate; documentation never replaces either.
  1. Runtime identity. Capture product, distribution/provider, server/runtime version, client/SDK version, topology/deployment mode, effective relevant configuration, and the exact error/output. The artifact cites the literal Phase 1 command and red output; a handoff claim that they ran is not evidence. Unknown fields stay
    unresolved
    .
  2. Owning documentation. REQUIRED SUB-SKILL: use
    research
    for the exact failing concept. For libraries/frameworks/APIs, it resolves current documentation through Context7 first. Match the source to the observed version/provider; latest docs do not establish older-runtime behavior.
  3. Contract diff. Produce this artifact before Phase 3:
SurfaceActual runtime evidenceOfficial documented expectationApplicabilityMatch
<failing concept>
<captured value/error>
<owning-source behavior>
<matching version/provider, mismatch, or unresolved>
<yes, no, unresolved>
  1. History check. When actual and current docs disagree, inspect the owning changelog, migration guide, deprecation notice, or official issue history for the observed version. A community answer may locate a source; it is not the evidence entered in the table.
  2. Disposition.
    match=no
    becomes a hypothesis candidate, not a confirmed cause.
    applicability=unresolved
    blocks dependency-behavior claims; report the missing identity/source instead of filling it from model memory.
When runtime access or identity is unavailable, use this disposition verbatim in the artifact:
text
Runtime evidence unavailable: <missing access/artifact>.
Version-matched owning documentation cannot be resolved.
Current official documentation is reference only; applicability is unresolved.
Phase 3 external-behavior hypotheses are blocked pending: <required evidence>.
Gate check: the Phase 1 command/red output is cited; runtime identity is explicit; an owning source is version-matched or explicitly unavailable; every table cell is filled; and no Phase 3 hypothesis claims external behavior beyond that artifact.
如果最小化的故障路径涉及版本化的外部依赖项——库、框架、SDK、数据库、缓存、搜索/可观测性平台、云服务、外部API、CLI、供应商发行版或协议——那么在提出假设之前,必须完成此证据检查。阶段1的信号和阶段2的最小化复现仍为准入门槛;文档永远不能替代这两者。
  1. 运行时标识。捕获产品、发行版/供应商、服务器/运行时版本、客户端/SDK版本、拓扑/部署模式、有效相关配置,以及确切的错误/输出。工件需引用阶段1的命令和失败输出;仅口头声称已运行不算证据。未知字段保留为
    unresolved
  2. 官方文档。必须使用子技能:
    research
    查找确切的故障概念。对于库/框架/API,先通过Context7获取最新文档。确保来源与观察到的版本/供应商匹配;最新文档不能代表旧运行时的行为。
  3. 契约差异。在阶段3之前生成此工件:
层面实际运行时证据官方文档预期适用性匹配情况
<故障概念>
<捕获的数值/错误>
<官方来源行为>
<匹配的版本/供应商、不匹配或未解决>
<是、否、未解决>
  1. 历史检查。当实际行为与当前文档不符时,检查对应版本的官方变更日志、迁移指南、弃用通知或官方问题历史。社区答案可能会找到来源,但不能作为表格中的证据。
  2. 处理结果
    匹配情况=否
    成为假设候选,而非已确认的原因。
    适用性=未解决
    会阻止关于依赖项行为的声明;报告缺失的标识/来源,而非用模型记忆填充。
当无法获取运行时访问权限或标识时,在工件中使用以下固定处理结果:
text
Runtime evidence unavailable: <missing access/artifact>.
Version-matched owning documentation cannot be resolved.
Current official documentation is reference only; applicability is unresolved.
Phase 3 external-behavior hypotheses are blocked pending: <required evidence>.
准入检查:引用阶段1的命令/失败输出;明确运行时标识;已匹配版本的官方来源或明确说明无法获取;表格的每个单元格均已填写;阶段3的假设未超出该工件所记录的外部行为。

Phase 3 — Hypothesise

阶段3 — 提出假设

Write 3–5 ranked hypotheses before testing any (a single hypothesis anchors you to the first plausible idea). Each must be falsifiable with a stated prediction: "if X is the cause, then changing Y makes the bug disappear". If you cannot state the prediction, it is a vibe — sharpen it or discard it. Show the ranked list to the user (they often re-rank it instantly); don't block if they're away.
Test the smallest hypothesis first. ONE variable at a time — never stack changes. Instrument with a debugger/REPL when available (one breakpoint beats ten logs), else targeted logs with a unique prefix per probe (e.g.
[DBG-x7q2]
) so cleanup is one grep — never log-everything-and-grep. Performance bugs: measure a baseline first (profiler, timing harness), then bisect; logs mislead here. Don't understand something? Say "I don't understand X" and investigate — never pretend and guess.
Hypothesis falsified? Strike it, move to the next. Don't pile a new fix on top of a failed one.
在测试任何假设之前,先写出3-5个排序后的假设(单一假设会让你局限于第一个看似合理的想法)。每个假设必须可证伪,并附带明确的预测:“如果X是原因,那么修改Y会让Bug消失”。如果你无法明确预测,那只是一种感觉——要么细化它,要么舍弃它。将排序后的列表展示给用户(他们通常会立即重新排序);如果用户不在,也无需等待。
先测试最小的假设。每次只改变一个变量——永远不要同时进行多项变更。如果可用,使用调试器/REPL进行监控(一个断点胜过十条日志),否则使用带有唯一前缀的定向日志(例如
[DBG-x7q2]
),这样清理时只需一次grep——永远不要记录所有内容再去搜索。性能Bug:先测量基线(性能分析器、计时工具),然后二分排查;日志在此处会产生误导。有不理解的内容?直接说“我不理解X”并进行调查——永远不要假装懂而猜测。
假设被证伪?划掉它,继续下一个。不要在失败的修复上叠加新的修复。

Phase 4 — Fix

阶段4 — 修复

  1. Failing regression test first. REQUIRED SUB-SKILL: use
    test-first
    . The test goes at a CORRECT seam — one that exercises the real bug pattern as it occurred. If no correct seam exists, that is itself a finding: document it and flag it for the post-mortem; a shallow test there is false confidence.
  2. One fix addressing the root cause. No "while I'm here" improvements, no bundled refactoring.
  3. Watch the regression test pass, re-run the full suite, re-run the Phase 1 loop against the original un-minimised scenario.
Three failed fix attempts = STOP. The architecture is in question, not your latest hypothesis — especially if each fix reveals new coupling somewhere else. Discuss with the user before attempt 4.
  1. 先编写失败的回归测试。必须使用子技能:
    test-first
    。测试要放在正确的层级——能复现实际Bug模式的层级。如果没有合适的层级,这本身就是一个发现:记录下来并在事后复盘时标记;在此处编写浅层测试只会带来虚假的信心。
  2. 仅修复根本原因。不要附带“顺便优化”的改进,不要捆绑重构。
  3. 观察回归测试通过,重新运行完整测试套件,针对原始未最小化的场景重新运行阶段1的循环。
三次修复尝试失败 = 停止。问题出在架构上,而非你最新的假设——尤其是如果每次修复都暴露出新的耦合问题。在第四次尝试前与用户讨论。

Exit — mini-spec, cleanup, post-mortem

收尾 — 迷你规范、清理、事后复盘

  • Tier-1 mini-spec: add a fix requirement plus a
    SHALL CONTINUE TO
    guard requirement to the owning feature's
    requirements.md
    (or
    docs/specs/fixes.md
    if no feature owns it). Map the regression test to that ID in the task report / Spec review — docs-only spine; do not require greppable IDs in test source for consumer apps.
  • Remove ALL instrumentation: grep for your
    [DBG-...]
    prefixes; delete throwaway harnesses.
  • State the confirmed root cause in the commit message.
  • Route Task: "what would have prevented this bug?" If the answer is architectural (no good seam, hidden coupling, tangled callers), write the specifics down and tell the user to run
    /scan-architecture
    — after the fix lands, when you know the most.
  • REQUIRED SUB-SKILL: use
    prove-claim
    before claiming the bug fixed.
  • 一级迷你规范:在对应功能的
    requirements.md
    中添加修复要求和
    SHALL CONTINUE TO
    防护要求(如果没有对应功能,则添加到
    docs/specs/fixes.md
    )。在任务报告/规范评审中将回归测试映射到该ID——仅文档层面;对于消费类应用,不要求测试代码中包含可搜索的ID。
  • 移除所有监控:搜索你的
    [DBG-...]
    前缀;删除临时测试工具。
  • 在提交信息中说明已确认的根本原因。
  • 发起任务:“什么可以防止这个Bug再次发生?”如果答案是架构层面的(没有合适的层级、隐藏的耦合、混乱的调用方),写下具体内容并告知用户运行
    /scan-architecture
    ——在修复完成后,当你对问题了解最透彻的时候。
  • 必须使用子技能:在声称Bug已修复前使用
    prove-claim

Rationalizations

常见借口与真相

ThoughtReality
"Emergency, no time for process"Root-cause debugging is FASTER than guess-and-check thrashing
"It's obviously X, let me just fix it"Seeing a symptom is not understanding a cause
"Quick patch now, investigate later"The patch becomes permanent and the cause strikes again
"Try several changes at once to save time"You can't tell which one worked, and one of them broke something
"Too simple to need a repro"Simple bugs have root causes too; the loop takes minutes
"I'll add the regression test after the fix"Untested fixes regress; the red test is the proof the fix fixes
"One more attempt" (after 2+ failures)Attempt 4 without an architecture discussion is thrashing
"Load the feature subgraph first — that is the loop"Phases 1–2 need a red-capable command; retrieval only after Phase 2
"Neighbor card says who owns it — skip minimize"Ownership context is advisory after Phase 2; the minimal repro still gates Phase 3
"I know this library; fetch docs after the likely fix"Runtime identity and version-matched owning docs complete before external-behavior hypotheses
想法真相
“紧急情况,没时间走流程”根因调试比猜测式尝试更快
“显然是X的问题,我直接修复就行”看到症状并不等于理解原因
“先快速修补,之后再调查”修补会变成永久方案,而原因会再次引发Bug
“同时尝试多个变更以节省时间”你无法知道哪个变更起作用,而且其中一个可能会破坏其他功能
“太简单了,不需要复现”简单Bug也有根本原因;构建循环只需几分钟
“我会在修复后添加回归测试”未测试的修复会再次退化;失败测试是修复有效的证明
“再试一次”(已失败2+次)不讨论架构就进行第四次尝试纯粹是浪费时间
“先加载功能子图——这就是循环”阶段1-2需要red-capable命令;仅在阶段2之后进行检索
“相关卡片显示了所有者——跳过最小化步骤”所有权上下文仅在阶段2之后作为参考;最小化复现仍管控阶段3的准入
“我了解这个库;在可能的修复之后再获取文档”在提出外部行为假设之前,必须完成运行时标识和版本匹配的官方文档检查

User signals — return to Phase 1

用户信号 — 返回阶段1

The user saysIt means
"Stop guessing"You proposed fixes without a root cause — back to Phase 1
"Is that actually happening?"You assumed without verifying — gather evidence
"Will that show us anything?"Your probe maps to no prediction — restate hypotheses
"We're going in circles"Count your failed fixes; you're probably at the architecture gate
用户表述含义
“别瞎猜了”你在未做根因分析的情况下提出了修复方案——回到阶段1
“真的是这样吗?”你未经验证就做出了假设——收集证据
“这能告诉我们什么?”你的监控没有对应预测——重新表述假设
“我们在绕圈子”数一下你的失败修复次数;你可能已经触及架构门槛