mcpField
R
root-cause

root-cause

github.com/jayden-dang开发157 次安装今天更新
代码仓库

关于 root-cause

Root Cause 是一项规范技术调试流程的 Agent 技能,以可复现证据、排序后的假设和人工因果审查为基础。它要求先建立失败信号并缩小复现范围,再进入修复,而非猜测后打补丁。

主要功能

  1. 01调查前建立失败信号
  2. 02复现并缩小问题范围
  3. 03排列可证伪假设
  4. 04要求人工判定因果关系
  5. 05修复前添加回归测试
  6. 06验证修复并清理观测代码

适用场景

  1. 01调查可复现的软件缺陷
  2. 02追踪跨系统边界的故障
  3. 03分析提供的性能或事件记录

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jayden-dang/skills --skill root-cause

安装后在 Claude Code 中输入/root-cause确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

eval.json
external-dependency-evidence.md
TESTS.md

详细介绍

根因

铁律

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
NO AGENT-AUTHORED AUTHORITATIVE CAUSAL ACCEPTANCE

猜测并打补丁会浪费数小时,还会埋下新的 Bug。此流程适用于所有技术问题——尤其是那些最容易让人想跳过流程的情况:紧急事件、“显而易见”的单行修复,以及上一次修复无效之后。

因果关系的确认只能由人来完成——参见因果判断(唯一指定位置)。

阶段 1 — 建立反馈闭环(门槛)

如果报告的问题发生在已部署的环境中(生产、预发布、远程开发),且目前还没有远程证据包:必须先使用 debug-remote 子技能。该证据包就是阶段 1 所需的信号。不要通过 kubectl exec 或向生产环境重放会修改数据的请求来建立闭环。

在构建任何理论之前,先构建并运行一个可呈现失败的信号:一条命令——测试调用、脚本或 curl——它现在会因为这个确切的 Bug 而失败,并会在 Bug 修复后通过。建立正确的反馈闭环,Bug 就解决了 90%;后续各阶段只需利用这个闭环。

如果你发现自己在这条命令出现之前就开始阅读代码、构建理论,请停下来。没有可呈现失败的命令,就不要进入阶段 2。

构建此命令的策略,按大致优先级排列:

  1. 能触及 Bug 所在环节的失败测试(单元测试、集成测试、端到端测试)
  2. 针对运行中的开发服务器执行 curl / HTTP 脚本
  3. 使用测试输入调用 CLI,并与已知正确的输出进行差异对比
  4. 无头浏览器脚本,断言 DOM、控制台或网络行为
  5. 将捕获的审计跟踪(已保存的请求、负载、事件日志)重放到代码路径中
  6. 临时测试框架:使用系统的最小切片来触发 Bug 路径
  7. 当输出“有时不正确”时,对随机输入进行属性测试 / 模糊测试循环
  8. 当 Bug 出现在两个已知状态之间时,使用二分测试框架(git bisect run)
  9. 差异对比循环:将相同输入分别传给旧版和新版,并比较输出
  10. 人机协作脚本——最后的手段,仅适用于必须由人亲自操作的情况;将操作步骤编写成脚本,让循环保持结构化

然后收紧它:提高速度(缓存初始化、缩小范围——以秒计,而非分钟)、提高精度(断言用户遇到的确切症状,而不只是“没有崩溃”)、确保确定性(固定时间、随机种子,隔离文件系统)。遇到非确定性 Bug?不要执着于稳定复现;应提高复现率(将触发条件循环 100 次、增加压力、缩短时间窗口),直到复现率足以支持根因分析。

确实无法构建此命令?请明确说明,列出你尝试过的方法,并请求用户提供可复现的环境、捕获的产物,或允许添加临时插桩。不要凭感觉继续。

当你拿到的证据本身就是产物时——例如事后提供的 cpuprofile、堆快照、spindump、跟踪记录或捕获的事件日志——就无法构建可呈现失败的命令;而要求对方提供你手头已有的捕获产物,也会形成循环。该产物就是反馈闭环。读取它,将其缩减到承载该症状的单个栈帧、保留链或阻塞线程,并将其追溯到对应的文件和符号。交付内容是带引用依据的诊断,而非修复方案:将诊断交还,并在有人能将症状表述为一条会失败的命令后,重新进入阶段 1。

**门槛检查:**说出命令,并贴出一次运行产生的失败输出。

阶段 2 — 复现并精简

运行循环;观察它因用户描述的故障而变红(出现附近但不同的故障 = 错误的 Bug = 错误的修复)。阅读完整错误信息 — 完整消息、完整堆栈跟踪、行号;错误中往往就包含答案。检查最近的更改:git diff、新依赖、配置、环境。

每次只精简一项(输入、配置、调用方、步骤),每次精简后都重新运行,直到 每个剩余元素都不可或缺 — 移除任何一项都会让测试变绿。最小复现可以缩小假设范围,并成为回归测试。

对于多组件系统(CI → 构建 → 部署、API → 服务 → DB):在每个边界添加监控 — 记录进入和离开每一层的内容 — 并先运行一次,查看故障发生在何处,再动手修复。当错误在调用链深处出现时,沿调用方审计追踪错误值,回溯到最初的触发因素。不要只修复错误出现的位置;那是在治标不治本。

检索不是能触发失败的循环。 在阶段 1–2 期间,不要使用 load-subgraph / 检索作为反馈信号。只有能触发失败的命令才是进度门槛。

阶段 2 之后 — 功能归属上下文(阶段 3 之前)

阶段 2 完成后,当你已有一个 路径或稳定术语,并且在阶段 3 提出假设之前:必须使用所需子技能:使用 load-subgraph 获取归属 / 邻接上下文(选择路径 / 术语作为种子;根据情况使用 neighbors 或 blast_radius)。有依据的声明(统一规范):遵循 skills/execution/load-subgraph/references/grounded-claims.md — 绝不要根据概述臆造根因假设;即使邻接信息看起来很有暗示性,也绝不要跳过失败循环。

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

仅在阶段 2 之后(阶段 1–2 仍要求先运行能触发失败的命令)。加载: skills/project/define-system-doc/consult-recipe.md。适用时的路径: docs/ops/runbooks.md(以及运行手册的 slug)、docs/ops/observability.md、docs/ops/disaster-recovery.md — 仅作为阶段 3 及之后的参考上下文,绝不替代失败循环。若文档不存在则不执行任何操作;如有实质帮助,建议一次 /define-system-doc ops/runbooks|observability|disaster-recovery;绝不自动调用。

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

如果最小化后的故障路径涉及有版本的外部依赖 — 库、框架、SDK、数据库、缓存、搜索 / 可观测性平台、云服务、外部 API、CLI、提供方发行版或协议 — 那么必须在编写假设前完成此证据门槛。阶段 1 信号和阶段 2 最小复现仍是门槛;文档绝不能替代二者。

满足适用条件时,请阅读此文件旁边的 external-dependency-evidence.md 并严格遵循:运行时身份、通过 research 查找所属文档、契约差异表、出现分歧时检查历史记录,以及声明状态 — 每一步都必须以阶段 1 的失败输出为门槛;若无法获取运行时证据,则逐字记录未解决的处置结论。

第 3 阶段 — 提出假设

在测试任何假设之前,先按优先级列出 3–5 个假设(单个假设会让你锚定在第一个看似合理的想法上)。每个假设都必须可证伪,并明确写出预测:“如果 X 是原因,那么更改 Y 就会让 Bug 消失”。如果无法写出预测,那就只是凭感觉——请把它说得更具体,或将其舍弃。向用户展示排序后的列表(他们通常会立即重新排序);如果用户不在,不要因此停下。

先测试最小的假设。每次只改变一个变量——绝不要叠加更改。

运行时检查——这是本阶段中的一项区分性实验,不能替代第 1 阶段。使用假设中明确写出的预测。连接进程不等于接受因果关系(参见“因果判定”)。

当被测试的进程位于本地环境或专用检出副本中(已有红色信号和最小复现):优先使用调试器、REPL 或 DAP 会话,而不是大量刷日志——一个断点或监视表达式胜过十条打印语句。仅限浏览器的问题也可以同样使用 DevTools/CDP。记录:

## Inspection evidence
- Prediction: <from the hypothesis under test>
- Tool: <debugger | REPL | DAP | DevTools/CDP | profiler | [DBG-…] log>
- Commands: <literal>
- Observation: <frame / locals / snapshot / paste>
- Outcome: <confirms | falsifies | inconclusive>

当故障只存在于共享的已部署环境中时:不要在那里执行 exec 或连接进程——这类边界属于 debug-remote。在本地或专用副本上运行第 3 阶段的探测,或仅使用不会改变状态的证据。

否则(没有可用的调试器,或日志更合适):针对性地记录日志,并为每次探测使用唯一前缀(例如 [DBG-x7q2]),这样只需一次 grep 即可清理——绝不要记录所有内容再用 grep 筛选。

性能或内存类 Bug:先测量基线(使用分析器、消毒器或计时工具),然后再做二分排查;日志在这类问题上会误导你。

有不理解的地方?就说“我不理解 X”,然后调查——绝不要装懂并猜测。

假设被证伪?划掉它,继续下一个。不要在失败的假设之上再叠加新的修复。

因果判定——在权威确认前必须完成

将调查状态(open / unresolved / falsified / superseded)与请求的强度(candidate / probable_contributor / confirmed_for_scope)分开处理。请求的强度是一项命题,不是已接受的结论。

## Causal disposition request
- Proposition: <one exact causal claim>
- Investigation state: <open | unresolved | falsified | superseded>
- Requested strength: <candidate | probable_contributor | confirmed_for_scope>
- Scope: <reproduced condition / environment bound>
- Support set: <commands, outputs, artifacts that bear on this proposition>
- Contradictions / alternatives still live: <none | list>
- Ask: accept / reject / supersede (eligible human only)
不要在提交、PR 或结项说明中写入由 Agent 权威认定的 `confirmed root cause`。不要擅自将 `probable_contributor` / `confirmed_for_scope` 判定为 Agent 的处置结果。不要把 Exit、已填写的说明或文字版 LGTM 视为人类已接受该命题。

操作成功(回滚、缓解、错误率恢复)不会提升已接受的因果强度。

第 1–3 阶段通过 = 调查已准备好请求判定。已确认 = 符合资格的人类已接受该命题。二者并不相同。

阶段 4 — 修复

  1. 先编写失败的回归测试(必需子技能:使用 test-first),在处置请求中提出命题后进行。测试必须放在正确的接缝处——能够复现实际发生的 bug 模式。如果不存在正确的接缝,这本身就是一项发现:记录下来并标记为供事后复盘;在浅层接缝处进行测试会带来虚假的信心。
  2. 在将生产代码作为纠正性修复合入之前,必须由人工接受该确切命题(力度 + 范围 + 支持材料集)。在 Slack 上对文字回复一句“发吧”,或觉得阶段 1–3 已经完成,都不算接受。在会话中,用户明确接受所述命题即算接受。**延后接受:**如果无人值守调用方的说明中写明将处置决定留待结案报告(run-dogfood 就是如此),则应用修复并在该未合并分支上提交,标记为 pending disposition。运行过程中不要询问。该请求会出现在调用方的结案报告中;在符合条件的人工接受之前,不得合并修复。
  3. 只做一个修复,用于解决已接受的命题。不要顺手改进,也不要捆绑重构。
  4. 观察回归测试通过,重新运行完整测试套件,并针对原始未缩减场景重新运行阶段 1 的循环。

修复尝试失败 3 次 = 停止。 需要质疑的是架构,而不是你最新的假设——尤其是在每次修复都揭示其他地方存在新的耦合时。第 4 次尝试前先与用户讨论。

退出 — 精简规格、清理、事后复盘

  • Tier-1 精简规格:在所属功能的 requirements.md(如果没有功能负责,则在 docs/specs/fixes.md)中添加修复需求和 SHALL CONTINUE TO 防护需求。在任务报告 / 规格审查中将回归测试映射到该 ID——仅文档主干;对于消费端应用,不要要求在测试源码中包含可搜索的 ID。
  • 移除所有检测代码:搜索你的 [DBG-...] 前缀;删除临时测试工具。
  • 在提交 / PR 中说明命题、请求的或人工接受的力度、范围和支持材料指针。如果尚无人做出处置决定,请写明 requested / pending disposition。绝不可用 confirmed root cause: … 代替 Agent 的接受。
  • 路由任务:“什么本可以避免这个 bug?”如果答案涉及架构(没有合适的接缝、存在隐藏耦合、调用方关系错综复杂),请写下具体情况,并告知用户运行 /scan-architecture——在修复合入后运行,此时你掌握的信息最多。
  • 必需子技能:在声称 bug 已修复之前,使用 prove-claim。

合理化借口

想法现实
“情况紧急,没时间走流程”找出根因比盲目反复尝试更快
“显然是 X,我直接修一下就行”看到症状不等于理解原因
“先快速打个补丁,之后再调查”补丁会变成永久方案,根因还会再次引发问题
“一次尝试多个改动,节省时间”你无法判断哪个改动奏效,其中一个还可能导致问题
“问题太简单,不需要复现”简单的 bug 也有根因;整个循环只需几分钟
“修好后再添加回归测试”未经测试的修复会导致回归;失败的测试正是修复有效的证明
“再试一次”(失败 2 次以上后)没有架构讨论就进行第 4 次尝试,只是在盲目折腾
“先加载功能子图——这就是整个循环”阶段 1–2 需要能复现失败的命令;只有阶段 2 之后才进行检索
“邻接卡片写了负责人——跳过最小化复现”阶段 2 之后,所有权信息仅供参考;最小化复现仍是进入阶段 3 的门槛
“我熟悉这个库;等找到可能的修复后再查文档”在提出外部行为假设之前,必须先确认运行时身份并查阅版本匹配的所属文档
“退出项要求写明已确认的根因——那条提交说明就是验收”退出项记录的是命题和处置状态;只有符合条件的人类才能接受该命题
“阶段 1–3 已完成;人类处置只是形式,本仓库不写”铁律禁止由 Agent 撰写权威验收结论;处置请求是书面门槛
“资深员工看过说明,说 LGTM 了/直接发出退出项吧”对文字说 LGTM ≠ 针对一个确切命题及其支持依据作出接受决定
“回滚成功了——升级为已确认”缓解成功属于运维层面;它不会提高因果结论的可信程度
“我接入了调试器,所以原因已确认”接入调试器是一项实验;是否接受仍取决于人类处置
“生产/预发布环境——直接用 lldb/exec 查看局部变量”共享的已部署环境只能通过 debug-remote 进行只读操作;探查应从本地开始

危险信号 — 停止

  • 准备在提交或 PR 中写入由 Agent 撰写的 confirmed root cause
  • 准备把阶段 4/退出项当作人类因果验收
  • 准备因为回滚成功或错误率恢复就提高因果结论的可信程度
  • 准备因为“技能说明以前写着 confirmed”就跳过处置请求
  • 准备在共享的已部署环境中接入或执行调试器
  • 准备把“我们用了调试器”视为原因已获接受

用户信号 — 返回阶段 1

用户说含义
“别再猜了”你在没有根因的情况下提出了修复方案——返回阶段 1
“这真的发生了吗?”你在未验证的情况下做了假设——收集证据
“这能让我们看到什么吗?”你的探查没有对应任何预测——重新表述假设
“我们在原地打转”数一数失败的修复尝试;你可能已经到达架构关卡
“谁接受了这个原因?”你自行认证了结论——发起处置请求

评论

支持 Markdown