git:20261015.2c8093f to git:20261015.f79eac0

5 added, 3 removed. Audit A to A.

---
name: systematic-debugging
description: 在缺陷、测试或构建失败、异常行为和反复修错时使用;先建立可区分假设的反馈环和根因证据,再按用户授权决定是否修复。普通代码审查、明确的小修改或只执行已有测试不触发。
---
# 系统化排查问题
目标是找到能解释现象的根因,并用同一个反馈环证明修复有效。诊断请求默认只定位和报告;用户要求修复时才修改代码。
## 核心循环
1. **固定现象**:记录错误、环境、输入、期望、实际结果和最近已知正常状态。
- 2. **建立反馈环**:优先使用最小测试、复现命令、页面路径、日志条件或可观察状态,使问题能失败、复跑并在修复后变绿。
+ 2. **建立反馈环**:优先使用最小测试、复现命令、页面路径、日志条件或可观察状态,使问题能失败、复跑并在修复后变绿。这一步最值得投入:有了能在这个缺陷上变红的命令,后面的二分、假设和观测都只是在消费它。构造方法、收紧和提高复现率见 [references/advanced-modes.md](references/advanced-modes.md)。
3. **收集独立证据**:日志、堆栈、配置差异、近期变更、相关源码、网络和环境状态可以并行读取,但结论必须汇入同一证据链。
- 4. **排序假设**:维护轻量矩阵:
+ 4. **排序假设**:先列出 3-5 个按可能性排序的假设再开始验证,只列一个容易锚定在第一个想法上。每个假设写出它的预测:“如果原因是 X,改动 Y 会让问题消失”;说不出预测的不算假设。用户在场时把排序给用户看一眼,他们常有能立刻重排的领域信息;不等待回复,用户不在时按自己的排序继续。维护轻量矩阵:
`假设 | 支持证据 | 反证 | 置信度 | 下一步最小检查`
- 5. **验证一个假设**:用最小观测或可逆改动获取能证实或证伪的结果,不同时改变多个变量。
+ 5. **验证一个假设**:用最小观测或可逆改动获取能证实或证伪的结果,不同时改变多个变量。环境支持时优先用调试器或 REPL 查看,一个断点胜过十条日志;加日志时给每条临时日志加唯一前缀(如 `[DEBUG-a4f2]`),结束时按前缀搜索并全部删除。
6. **更新模型**:记录被排除方向;同一症状未消失时停止叠补丁,回到矩阵重新排序。
7. **修复与回归**:根因明确且用户授权修复后,做最小修改;先用原反馈环证明问题消失,再按风险扩大验证。修复授权不包含提交、推送、发布或破坏性操作。
8. **同型排查**:宣布修复前,提炼触发缺陷的函数、调用形状、选择器、锁或校验缺口,在仓库内搜索同类位置,逐项标注;方法见 [references/advanced-modes.md](references/advanced-modes.md)。
+ 9. **事后追问**:修复完成后问一句“什么能预防这个缺陷?”。答案涉及架构(没有合适的测试接缝、调用方纠缠、隐式耦合)时,把具体情况作为建议提出,不在本次修复中扩大范围。正确的假设写进提交信息或 PR 说明。
在改代码前,应能用一句话表达当前假设:`我认为根因是 X,因为证据 Y;结果 Z 会证伪它。`X 要具体到文件、函数、行或触发条件;“状态管理问题”这类说法无法证伪,还不算假设。假设必须解释全部已观察症状,包括报告人认为无关的症状;只解释首个症状的是表层猜测。
出现“先试试这个”“应该是同一个问题”“我本地正常”时,说明还没有假设:先写出假设、重读执行路径或列出环境差异。用户的直接观察与日志冲突时,优先相信观察,把差异当作未覆盖的路径;只在正常路径上通过的探针不能证明故障路径不存在。
反馈环应命中用户报告的具体故障路径,并断言对应的错误输出或终态;“没有报错”或无关测试通过不算复现。先运行一次确认它确实会失败,再尽量缩小输入、调用方和环境条件,每次删去一个条件后重跑。保留原始场景,修复后同时检查最小复现和原始场景。
## 没有稳定复现时
只能做读取、加观测和缩小范围,不把猜测提交成修复。记录可替代证据,例如 CI 失败、生产日志、用户的稳定步骤、状态差异或最小监控指标,并说明它比本地复现弱在哪里。
运行一次后通过不能自动判定 flaky。至少需要近期重复运行、通过对照和能解释失败的基础设施或时序证据。
缺的是报告人的环境时,下一步交付只读探针而不是新假设:一段可直接复制运行的命令,输出环境信息、争议中的测量值和假设所依赖的状态,不输出凭据或私有路径;安装方式、目录、locale、shell 和版本都要探测,不按自己的环境写死。
## 按问题类型取证
- 窗口生命周期、事件顺序、焦点、定时器、状态机和异步时序问题:形成假设时就加定向日志,不靠静态阅读连猜两次。
- 渲染或视觉问题:先查图层、层叠上下文和样式计算,再加日志或调试覆盖层。
- 纯逻辑问题(公式、边界、off-by-one):静态分析和最小用例即可。
- 性能问题:修改前测基线(耗时、采样、内存),修改后用同一方法复测,报告前后数值;“感觉变快”不是证据。
- MCP、API 或外部工具失败:先确认服务、凭据、配置和版本,再决定是否换工具,不直接绕开。
- 修改算法后输出仍不对:先确认运行时没有读取旧代码写入的缓存、快照或生成物;需要时在同一修改中失效或升级旧数据版本。
## 参考路由
只在命中对应问题时读取:
- [root-cause-tracing.md](root-cause-tracing.md):错误出现在深层调用链,需要追踪原始输入或触发点。
- [condition-based-waiting.md](condition-based-waiting.md):异步或测试依赖时序,需要用条件轮询替代固定 `sleep`。
- [defense-in-depth.md](defense-in-depth.md):根因修复后仍需要在多个边界阻止无效状态传播。
- [references/advanced-modes.md](references/advanced-modes.md):回归对照、二分定位、同型缺陷搜索、回归测试红绿验证、运行时证据和配置诊断。
- [references/failure-patterns.md](references/failure-patterns.md):问题重复出现、首次修复未生效,或症状更像运行时状态、入口、环境差异而不是语法错误。
## 安全边界
- 修改配置、版本、端口、权限或网络参数前,记录原值、目标文件和要验证的假设;失败后恢复本轮临时变化。
- 生产和共享环境先做只读检查;修复、重启、删除、回滚和权限操作需要明确目标与授权。
- 临时日志只记录能区分假设的事实,不输出凭据或敏感数据,并在任务结束前移除不需要的观测代码。
+ - 展示命令、输出和抓取的请求前先脱敏,把密钥替换为 `<REDACTED>`;反馈环通过环境变量读取凭据,避免凭据出现在展示内容里。只引用携带信号的行,抓包和日志常带认证头。脱敏后不足以诊断时说明并询问用户。
- 保护用户工作区中的无关改动,不用破坏性 Git 命令清理环境。
- 编译或静态检查通过不能证明界面、网络、生成物、状态机或生产行为已经恢复。
## 停止与重置
- 连续两个假设被证伪后,重新排序矩阵,不沿熟悉方向继续加码。
- 连续三个假设被证据推翻后,汇总现象、证据和已排除方向,重新选择诊断方法;有已授权的有效检查时继续,不因计数自动交接。
- 同一阈值、超时、间距或数值调整三次仍不对时,停止调参,检查结构约束和不变量。
- 缺少完成诊断所必需的访问、日志或复现条件时,报告具体缺口和能继续的最小输入。
## 输出契约
报告:
- 现象、范围和复现/替代证据;
- 当前最高置信假设及其反证条件;
- 已排除方向和关键命令/观察;
- 根因结论及证据强度,或为什么仍不能确定;
- 若已修复:实际改动、原反馈环结果、更宽验证和残余风险;
- 同型排查:检查了哪些位置、哪些同样有问题、哪些可安全保留及原因;未做时说明原因;
- 回归保护:新增测试的位置和红绿运行结果,或不加测试的原因;
- 若只诊断:不修改文件,并给出下一步最小检查。