mcpField
R
runtime-evidence-debug

runtime-evidence-debug

github.com/fudesign2008开发675 次安装1 天前更新
代码仓库

关于 runtime-evidence-debug

runtime-evidence-debug 是一项指导调试者从静态分析转向运行时取证的技能,适用于根因不明确或修复后问题仍存在的情况。它提供从仪器设计、复现到证据分析和修复置信度评估的流程。

主要功能

  1. 01判断何时升级到运行时观察
  2. 02按假设设计定向仪器
  3. 03指导问题复现与环境记录
  4. 04按证据强弱分析运行结果
  5. 05评估根因置信度与修复条件

适用场景

  1. 01静态分析后仍无法确认根因
  2. 02静态修复后故障依旧
  3. 03排查无报错但行为异常的问题
  4. 04通过运行时证据验证修复

安装

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

$npx skills add https://github.com/fudesign2008/open-skills --skill runtime-evidence-debug

安装后在 Claude Code 中输入/runtime-evidence-debug确认已加载。

附带文件evals/evals.jsonreference.md

详细介绍

基于运行时证据的调试

角色:运行时调试的方法论层——回答何时应从静态分析升级到运行时观察,以及如何收集、分析和利用运行时证据。PDCA 宿主通过 frontmatter dependencies 引用此技能(在前置检查后调用)。它将工具选择委托给 browser-debug-toolkit,将领域分析委托给 hybrid-debug。

详细的框架条目(作者、年份、方法、来源)和插桩模板见 reference.md。

此技能存在的原因

静态代码分析——阅读代码、追踪调用链、推理逻辑——有其局限。它无法观察:

  • 运行时数据流:变量在执行时实际持有的值
  • 调用顺序:时序至关重要时,哪个函数先运行
  • 平台/宿主拦截:WebView、Electron、操作系统级控制或框架运行时施加的静默行为更改——这些在源代码中不可见

静态分析后,如果根因置信度仍为“模糊”或“未知”,有条理的做法不是更卖力地重读同一段代码,而是升级到运行时证据收集——添加有针对性的插桩、复现问题、观察实际执行情况,并以观察到的事实而非推断的逻辑为根因提供依据。

此技能提供了这套方法论。核心要点:

通过运行时观察确认的根因,比通过阅读代码推断出的根因可靠一个数量级。置信度较低时,不要加倍推理——去收集证据。

何时使用

强烈信号(满足任意一项即可):

  • 静态分析后根因置信度较低——你能定位可能涉及的模块,却无法确认具体逻辑或触发路径。
  • 重试场景——基于静态分析应用了修复,但问题仍然存在。静态分析已经触及过一次局限;下一步必须使用运行时数据。
  • 静默故障——调用链看起来完整,参数看起来正确,日志也显示代码已执行,但运行时行为却不正确。这说明可能存在平台/宿主拦截,只有运行时观察才能揭示。
  • 你需要添加日志/插桩(console.log、print、调试器断点),以观察运行时实际发生的情况。
  • 修复验证——你需要通过比较修复前后的行为来确认修复有效,而不只是推理代码。

与其他技能的关系

技能职责与此技能的关系
PDCA 宿主宿主工作流PDCA 宿主通过 frontmatter dependencies 引用此技能(缺失时 → 宿主中止)。深入探查其技术分析插桩步骤的运行时证据。宿主是此关系的事实来源。
browser-debug-toolkit浏览器 DevTools 工具选择决策表对于可在浏览器中复现的问题(典型场景为 UI/CSS/DOM),此技能的插桩步骤会将任务委托给它——使用 DevTools 实时检查,比用 console.log 观察运行时更高效。
hybrid-debug混合应用四层调试模型此技能提供通用的运行时证据方法论;hybrid-debug 则在原生+Web 应用的四层模型中应用该方法论。对于混合应用问题,先使用 hybrid-debug(它会告诉你检查哪里);此技能会告诉你如何在对应层收集证据。
debug-workflow(如可用)系统化调试协议(根因识别)两者互为补充——debug-workflow 驱动整体调试流程;此技能是其中的运行时证据收集阶段。
essence-diagnosis证据链构建与对抗式辩论对于运行时证据仍不足的问题,升级到 essence-diagnosis,进行多假设证据链验证。

证据收集生命周期

从决定是否升级,到验证修复,共分为七个阶段。并非每个问题都需要经历全部七个阶段,但跳过阶段会导致结论置信度偏低。

阶段 1 — 升级决策(何时从静态分析转向运行时分析)

框架:科学调试法 — Andreas Zeller,Why Programs Fail(2009 年,第 2 版,2022 年)。

添加任何日志之前,请确认静态分析确实已达到极限。判断标准:追踪调用链并阅读相关代码后,你能否有较高置信度地说出根本原因?如果可以,暂时不需要运行时证据。如果置信度“模糊”或“未知”——你能指出可疑模块,却无法确定确切机制——就该升级了。

科学方法同样适用:提出假设(根本原因是 X),设计实验(在 Y 处添加检测,以观察 Z),观察结果,并得出结论(确认或证伪 X)。每一轮检测都是受控实验,而不是漫无目的地探查。

阶段 2 — 检测设计(针对性日志)

框架:可观测性三支柱(Charity Majors,Observability Engineering,2022 年;Cindy Sridharan,Distributed Systems Observability,2018 年);结构化日志最佳实践(OpenTelemetry 语义约定)。

设计 2–5 个检测点,不要到处散布 console.log。每个检测点都应针对特定假设:

  • 函数入口/出口:确认函数是否被调用,以及传入了哪些参数
  • 状态变更点:观察变量何时以及如何变成错误的值
  • 数据流交汇处:追踪数据跨越模块/服务边界时的变化

良好的检测会记录:位置标识符([DEBUG-<location>])、关键变量(并非所有变量,而是与你的假设相关的变量)以及时间戳。格式:console.log('[DEBUG-auth-check]', { userId, hasToken: !!token, timestamp: Date.now() })。

反模式:为了“以防万一”而到处添加日志。这会产生噪声,掩盖信号。每个日志点都必须对应一个假设。

对于可在浏览器中复现的问题(典型场景为 UI/CSS/DOM),请交由 browser-debug-toolkit 处理——通过 DevTools 实时检查(DOM、计算样式、盒模型、控制台、网络)比使用 console.log 更高效。

阶段 3 — 复现指导

框架:最小可复现示例(MRE/MCVE,Stack Overflow 方法论;Benjamin C. Haller,Ten Simple Rules for Reporting a Bug,PLOS Computational Biology,2022 年);确定性复现(Google Testing Blog——不稳定测试缓解方法,Micco 2016)。

要获取有用的运行时证据,问题必须可复现。引导用户完成以下步骤:

  1. 复现步骤:触发问题的确切操作顺序(不是“有时会发生”,而是具体路径)
  2. 环境:浏览器版本、操作系统、设备、网络状况——任何会影响运行时行为的因素
  3. 最小复现:移除所有无关内容,直到只剩下最小复现案例。复现案例越小,日志越清晰,迭代也越快。

如果问题无法确定性复现(间歇性出现、依赖时序),请记录发生频率和触发条件。非确定性错误(海森堡错误,参见 reference.md)需要特殊处理:检测本身可能改变时序并掩盖错误。

工具限制:AI 负责设计检测并提供复现指导,但由用户添加日志并运行复现(除非用户明确授权 AI 执行)。未经用户确认,不要运行复现步骤。

阶段 4 — 证据分析

框架:调试中的证据层级。

当用户提供运行时输出时,请结合证据层级进行分析:

证据层级强度示例
第 1 层 — 运行时观察最强调试器断点显示变量值;DevTools 计算后的样式;性能分析器热点
第 2 层 — 日志输出强console.log 输出确认函数使用特定参数调用
第 3 层 — 代码推断中等阅读代码并推断“这个分支应该执行,因为……”
第 4 层 — 假设/猜测弱“我觉得可能是缓存导致的,因为……”

以第 1–2 层证据为依据的根本原因值得信赖。依赖第 3–4 层证据的根本原因需要进一步添加检测手段才能提升可信度。如果最强证据是第 3 层(代码推断),就不要宣称根本原因“已确认”——那仍然是披着运行时外衣的静态分析。

常见误诊陷阱(避免固守第一个假设)

阅读代码往往会产生一个看似合理的假设,然后就此止步。但在源代码中,大多数症状类别都有许多难以区分的成因。在确定检测方案前,请检查以下替代原因:

症状你可能贸然认定的假设阅读代码时容易忽略的其他可能
内存泄漏(RSS 增长)事件监听器未清理闭包捕获了较大的作用域;缓存/Map 无界增长;定时器未清除;流缓冲区;原生扩展泄漏(Buffer/C++);V8 垃圾回收延迟(根本不是泄漏);连接池保留连接
间歇性崩溃 / 500竞态条件突发负载下连接池耗尽;数据库超时;网络短暂故障;OOM 终止;上游服务短暂故障;连接泄漏(不同于监听器泄漏)
UI 渲染过时useEffect 依赖项错误异步竞态(旧 Promise 最后才解析);useCallback 中的闭包过时;React.memo 保留了过时的 props;并发渲染的批处理差异;StrictMode 在开发环境中掩盖问题
静默失败(没有错误,行为不正确)我们代码中的逻辑错误平台/宿主拦截(WebView 特性、Electron IPC);边界处的类型强制转换;序列化导致数据丢失(JSON.stringify 丢弃 undefined/函数);环境变量未加载
性能回退算法复杂度内存压力导致垃圾回收频繁;隐藏的 N+1 查询;包体积膨胀;锁竞争;缓存失效风暴

规则:如果你的检测手段只测试了一个假设,而且结果证实了它,你仍然没有排除其他原因。设计检测手段来证伪排名前 2–3 的候选原因,而不只是证实第一个假设。

阶段 5 — 置信度门控

框架:将贝叶斯推理应用于调试(先验置信度 + 新证据 → 后验置信度)。

分析运行时证据后,请校准置信度:

  • 高:运行时证据直接确认了作用机制(例如,日志显示变量在本应为对象时却是 null;调试器显示执行了错误的分支)。→ 可以安全地开始修复。
  • 中:运行时证据缩小了范围,但无法唯一确认某个机制。→ 再进行一轮有针对性的检测,或在明确说明不确定性的前提下继续。
  • 低/模糊:运行时证据未能澄清作用机制——日志显示行为符合预期,但问题仍然存在。→ 不要开始修复。转入备用方案(阶段 6)。

代价最高的调试错误:在置信度较低时就开始动手(编写修复代码)。第一次尝试最终会变成被弃用的表面补丁。置信度较低时,严谨的做法是继续收集证据,而不是猜测并寄希望于运气。

修复就绪门槛 — 编写任何修复前必须同时满足的 3 个条件

提出或应用代码更改前,请核实以下三项。如果缺少任何一项,就说明还没准备好修复——请回到埋点阶段。

  1. 你能根据运行时证据而非推断补全这句话:“我确信根本原因是 X,因为[具体运行时观察:日志行/调试器值/分析器输出显示 Y]。”——如果你只能说“我觉得可能是 X”或“代码看起来像 X”,就说明你还没准备好。
  2. 你至少排除了一个替代假设——而非只确认自己偏好的假设。上方的常见误诊陷阱表列出了每种症状类型对应的替代假设。你的埋点应当排除其中至少一个。
  3. 你知道如何验证修复——你有一条复现路径,会在修复后重新运行,以确认行为发生了变化(阶段 7)。如果你无法在修复前描述验证方法,就说明你还没准备好。

这是这项技能中最具杠杆效应的一条准则。大多数无效的调试周期都源于跳过这一步——依据 Tier 3 证据编写修复,发现不起作用,然后反复重来。

阶段 6 — 退出方案(运行时无法穿透所在层时)

框架:git bisect(通过提交历史进行二分查找);在线搜索已知的平台/框架问题。

运行时埋点也有局限:它可以观察代码层的执行路径,但无法穿透平台/宿主层的静默拦截(WebView prefers-color-scheme 的特殊行为、Electron IPC 序列化、操作系统级控件)。当日志显示代码执行正常,但行为仍然不正确时:

  1. Git bisect:如果问题是回归,请通过提交历史进行二分查找,定位引入问题的提交(git bisect start → git bisect bad → git bisect good <commit> → 测试每个中间版本)。这样可以将原因缩小到某个具体的差异。
  2. 在线搜索:搜索与症状相符的已知平台/框架问题(症状关键词 + 平台/框架 + 年份)。如果有 effective-web-research 技能,请遵循其规范。
  3. 升级至领域专家:对于混合应用,升级至 hybrid-debug(四层模型)。对于证据链多假设验证,升级至 essence-diagnosis。
阶段 7 — 验证修复

框架:修复前后对比;回归测试(Kent Beck,《Test-Driven Development》,2002 年);无责复盘(Google SRE,《Site Reliability Engineering》,2016 年)。

应用修复后,请通过运行时证据验证,而不只是阅读修复后的代码:

  1. 修复前后对比:使用阶段 3 中相同的复现步骤和环境重现问题。比较修复前后的运行时行为。只有先前观察到的错误行为已变为正确行为,才能确认修复有效。
  2. 回归检查:验证修复没有破坏其他行为。运行测试套件;检查与修改逻辑共用代码的相邻路径。
  3. 注意 Heisenbug——修复本身就是一种时序干预:对于时序敏感的 Bug(竞态条件、死锁、UI 闪烁),插桩和修复都可能充分改变时序,从而掩盖 Bug。互斥锁比 console.log 的干扰更大——它会从根本上改变线程执行顺序。重构为 async/await 会改变微任务调度。debounce 会改变事件时序。请验证:
    • 在未启用任何调试日志、且与发布版或生产环境等效的构建中,修复仍然有效
    • 修复经得起多次运行(竞态条件至少运行 10 次,间歇性 UI Bug 至少运行 50 次),而不只是一次手动测试
    • 修复没有引入新的时序问题(锁顺序导致的死锁、串行化导致的吞吐量骤降、互斥锁持有时间导致的饥饿) 如果修复之所以“有效”,只是因为它带来的时序副作用恰好掩盖了另一个竞态条件,那就不是真正的修复,而是一颗定时炸弹。
  4. 清理:移除临时插桩。确定哪些日志点值得提升为永久监控。

反模式(禁止做法)

  1. 重复阅读代码,而不是升级排查手段——静态分析已经失败过一次,再加把劲重复分析也只会得到相同结果。应升级到运行时证据。
  2. 撒网式插桩——为了“以防万一”到处添加 console.log。每个日志点都必须对应一个假设。
  3. 根据代码推断就断定根因——“代码表明 X,所以运行时肯定是 X”属于第 3 级证据,而不是确认。要达到高置信度,必须有运行时观察(第 1–2 级证据)。
  4. 在置信度较低时采取行动——置信度仍然“模糊”时就进入执行阶段(编写修复)。结果只会是一个被丢弃的初次尝试。
  5. 忽视 Heisenbug——如果添加日志会影响 Bug 是否复现,那就是时序或并发问题。不要因为依赖插桩的修复奏效就宣告胜利。
  6. 跳过修复验证——“代码改动看起来没问题”不等于验证。请用相同步骤重现,并确认行为确实发生了变化。
  7. 未经用户授权就运行复现步骤——用户必须添加插桩并运行复现步骤(除非用户明确授权 AI 代为执行)。

评论

支持 Markdown