mcpField
P
post-mortem

post-mortem

github.com/thananon开发3.2k4.7k 次安装19 周前更新
代码仓库

关于 post-mortem

post-mortem 是用于记录已验证缺陷修复的工程写作技能,面向需要留存症状、根因、修复和验证信息的工程师。起草前必须确认关键证据,并区分缺陷修复记录与事件报告。

主要功能

  1. 01要求提供复现、根因、修复和验证
  2. 02记录症状与根因链路
  3. 03整理调试过程和验证证据
  4. 04按需记录后续行动
  5. 05发布前支持审阅草稿

适用场景

  1. 01为后续工程师记录已验证的缺陷修复
  2. 02说明代码层根因如何导致外部症状
  3. 03用可复现的验证细节完成缺陷归档

安装

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

$npx skills add https://github.com/thananon/9arm-skills --skill post-mortem

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

详细介绍

事后复盘

记录 bug 修复过程的权威工程文档。在调试最终落地真正的修复后编写,供其他工程师(以及 6 个月后早已忘光一切的未来的你)参考。这里可以使用代码标识符——这份文档能让后来者快速恢复问题的心智模型。

如需将同一内容改写成面向组织管理层的版本,请将完成后的事后复盘交给 management-talk。两者各司其职:事后复盘记录工程事实,management-talk 则将其重新组织为领导层视角。

适用场景

  • “/post-mortem”
  • “撰写事后复盘 / postmortem / RCA / 根因分析”
  • “记录这次修复” / “写下根因” / “用文档总结这个 bug”
  • 调试会话明确找到修复方案后,主动提出起草文档。

不适用场景

  • Bug 尚未修复,或修复尚未验证。 对假设进行事后复盘会造成误导。拒绝撰写,并告知用户缺少什么。
  • 客户可见的中断 / 事故。 这类情况需要单独的事故报告(时间线、影响范围、通知历史、沟通记录)。本技能适用于 bug 修复。撰写前应标记并确认。
  • 微不足道的修复(拼写错误、显而易见的一行代码修复)。PR 描述就是记录。不要为了形式而制造流程。

必备输入——缺少时拒绝起草

动笔前,先确认以下四项。如果有任何一项缺失,列出缺少的内容并停止:

  • 存在可靠的复现步骤(不是“偶尔会发生”——而是确定性复现,或高频不稳定复现,且后来者可以执行)。
  • 已知根因(已确定故障机制,而非假设)。
  • 已确定修复方案(PR / 提交 / 分支引用)。
  • 已验证修复(原复现现在可以通过;客户工作负载 / 失败的测试现在可以成功)。

这些要求与 debug-mantra 的第 1–4 步直接对应。如果你是通过 debug-mantra 进入此流程,第 4 步的线索记录就是原始素材——从中提取信息。

结构

按以下顺序使用这些部分。**摘要、根因、修复和验证为必填项。**其余部分视情况填写,但通常也会包含。

1. 摘要 (必填)

一段话。说明出了什么问题(从用户 / 工作负载的角度描述),以及用一句话说明修复方式。写明 JIRA 编号、PR 编号和负责人。读者只看这一部分,也应能得到正确结论。

2. 症状

实际观察到的情况。测试输出、错误消息、日志行、性能数据或客户报告。使用具体标识符——不要改述故障模式。

3. 根因 (必填)

实际的 bug 机制。欢迎并且应使用代码标识符——函数名称、文件路径、结构体字段、分支条件、引入问题的提交 SHA。完整梳理原因链。这是最费精力的部分,也是撰写事后复盘的根本原因。未来的你能否顺利处理问题,取决于这一部分写得是否清晰。

4. 症状成因

说明根因如何导致症状。两者之间的联系往往并不直观——例如,bug 出在 tadaLaunchPrepare,但可见故障却是客户的训练任务在数小时后挂起。梳理完整链路,让只了解症状的读者无需重新推导,也能将其追溯到根因。

5. 修复 (必填)

说明改动内容,以及为什么该改动能解决根因,而不是掩盖症状。附上 PR / 提交链接。如果之前有过掩盖症状的修复尝试,请指出并解释其问题所在——这段历史也是原因的一部分。

6. 如何发现问题

简要说明调试过程:

  • 哪种复现方式让问题可以稳定复现。
  • 哪些工具帮助找到问题(调试器、源码追踪、参数枚举、代码内插桩——即 debug-mantra 第 2 步的递进流程)。
  • 尝试过哪些假设,又为何排除,每项用一句话说明。(从线索记录中提取。)
  • 确认根因的那项实验。

本部分是写给下一位调试者的——要让他们能从中学习。

7. 为什么问题会漏过去

是什么原因让这个 Bug 进入了分支 / 发行版 / 客户环境?选择真实原因:

  • CI 漏洞(没有测试覆盖此路径 / 配置)。
  • 潜伏代码(编写时正确,后来被其他文件中的更改破坏)。
  • 工作负载覆盖不足(直到现在才有真实工作负载触发此代码路径)。
  • 先前修复不完整(防御性检查掩盖了症状;根本原因仍未解决)。
  • 评审遗漏(更改本身可以评审;其影响却未被发现)。

如果真实答案是“没有合理理由——我们本该发现这个问题”,就如实说明。不归咎于个人——描述流程缺口,而不是个人。

8. 验证 (必填)

我们如何确认修复有效。具体说明:

  • 原先失败的测试现在通过了(测试名称、链接)。
  • 客户工作负载现在可以完成(工作负载标识符、运行链接)。
  • 性能回归已解决(修复前数值、修复后数值)。
  • 压力 / 浸泡 / 模糊测试运行顺利完成(时长、规模)。
  • 其他受影响的配置 / 工作负载也已测试。

如果只验证了一种配置,请明确说明——“已在 Llama-2-70B / 8 GPUs / DeepSpeed 上验证;未在其他工作负载上重新测试。” 不要暗示实际覆盖范围之外的情况。

9. 行动项 / 后续跟进

修复 PR 中未包含的具体后续步骤。每项说明:内容 + 负责人 + 跟踪事项。

  • 在 <seam> 添加回归测试。(负责人、测试名称。)
  • 重构以防止此类 Bug。(负责人、工单。)
  • 补上 CI 漏洞:<new check>。(负责人、PR。)
  • 更新文档 / 操作手册。(负责人、链接。)
  • 为 <adjacent issue you noticed> 创建关联工单。(负责人、编号。)

如果没有行动项,请写 “无——此修复已足够,无需针对这类 Bug 进行后续跟进。” 不要为了显得周全而凭空编造行动项。

语气

这是工程师之间的沟通,与 management-talk 不同:

  • 代码标识符至关重要。 tadaLaunchPrepare、tada/prim.h::syncWaitPeer、scratchBuf、提交 SHA、行号——都要保留。关键在于让未来的工程师能够通过搜索追溯到相关更改。
  • 讲清机制,而非叙事。 梳理实际的原因链。不要把它淡化成“同步问题”——要说明哪个函数在什么条件下跳过了哪个事件。
  • 使用主动语态,明确具体主语,段落简短。 其他部分也遵循同样规则。
  • 不要含糊其辞。 删除“我们认为” / “似乎” / “可能是”等说法。要么明确陈述,要么不写。
  • 不归咎于个人。 描述 Bug、流程缺口和修复。不要写“某某本该发现这个问题”。CI 漏洞是失效模式,不是个人。
  • 不夹带倡议。 事后复盘记录发生了什么以及接下来要做什么。如果想主张重构,应单独提出方案,并在行动项中链接。

输出流程

  1. 确认四项必需输入均已提供。 如果有任何一项缺失,列出缺失项并停止。不要起草。
  2. 确认发布位置(默认:源工单的 JIRA 评论)。其他有效位置:PR 描述、docs/postmortems/<ticket>.md、内部 Wiki 页面。格式相同——只需调整外层结构。
  3. 生成草稿,作为单条聊天消息。
  4. 发布前获取批准。 如果要发布到 JIRA,先展示完整的 ADF 负载,等待用户明确回复 “post it” / “go ahead” / “yes,”,然后再执行 POST /rest/api/3/issue/<KEY>/comment。仅输出内容无需批准。
  5. 提供转交给 management-talk 的选项: “想要一个面向管理层的版本吗?我可以交给 management-talk。” 不要自动处理。

示例 — dumbModel 中的 Tada 卡死问题(JIRA-12345)

摘要。 Tada 的单流快速路径跳过了一项必需的跨流同步,导致内核在暂存缓冲区写入操作可见之前就启动了。使用 dumbModel 对 LLM-7B 进行微调时可稳定触发,导致工作负载在每个评估步骤都卡住。通过移除不安全的快速路径并收紧设备端检查修复。JIRA-12345,PR org/platform#5751,负责人 Alex(Tada 团队)。

症状。 使用 dumbModel 对 LLM-7B 进行 8-GPU 微调时,在首次评估步骤无限期卡住。没有错误,也没有超时——在 tadaKernel_AllReduce_f32_RING 中忙等。每次运行都能复现。

根本原因。 tadaLaunchPrepare / tadaLaunchKernel / tadaLaunchFinish 中的单流快速路径(由 scheduler->numStreams == 1 && !plan->persistent 控制)跳过了 launchStream 与 handle->shared->deviceStream 之间的跨流事件同步。dumbModel 恰好满足此条件。内核启动时,deviceStream 上的 IPC 发布操作/暂存缓冲区写入操作(用于填充 scratchBuf)尚未对 launchStream 可见。在内核中:scratchBuf == NULL → 错误解引用指针 → 从垃圾内存读取环形就绪标志 → 线程永久等待永远不会到来的就绪信号。

为何会出现该症状。 卡死发生在全归约环形等待循环中,这是调用栈中最后一个可见位置,但实际错误出在早几个栈帧之前的启动准备阶段。只有当工作负载触发特定条件(单流、非持久化)时,被跳过的同步才会引发问题,而 dumbModel 的归约散射模式在每个评估步骤都会触发该条件。

修复。 PR #5751 完全移除了单流快速路径(与其绕过的安全保障相比,该路径带来的性能收益微不足道),并在解引用 scratchBuf 前添加设备端空值检查,使同类错误能够明确报错,而不是悄无声息地卡住。此前的尝试(PR #5612)在 IPC 发布后添加了主机端防御性检查,在部分路径中掩盖了症状,却没有消除底层竞态——该改动也已回退。

发现过程。 复现范围从“8-GPU LLM-7B 偶尔卡住”缩小到在 2-GPU 子集上固定运行单个评估步骤即可稳定复现,耗时 30 秒。最初的假设是:launchStream 上的内核启动顺序有误。调试器证实这一假设不成立——内核已正确入队。第二个假设是:暂存缓冲区初始化存在竞态。通过在 tadaLaunchPrepare 中添加 [DBG-7af3] 插桩,打印 scratchBuf 和 deviceStream 事件记录时间戳,确认了该问题:启动发生在发布完成之前。锁定问题的单一实验是:强制设为 numStreams = 2 后,错误消失,从而确定了问题出在该条件分支。

为何未能提前发现。 这是潜伏的代码路径。单流快速路径于 3 月添加,当时认为 dumbModel 路径始终会采用多流方案。该假设当时成立。5 月对 dumbModel 启动器的改动开始将评估步骤合并到单流中,此时该条件分支便被触发。Tada 的 CI 未覆盖单流 + IPC + 暂存缓冲区这一组合;客户工作负载是首个触发此问题的场景。

验证。 原始 LLM-7B / 8-GPU / dumbModel 工作负载现在可顺利完成完整评估(连续运行 3 次,每次 2 小时)。tada-tests all_reduce_perf 回归套件全部通过。压力测试:在 8 个 GPU 上运行 6 小时,未卡住。尚未在其他模型规模或非 dumbModel 工作负载上重新测试——两者都会采用多流路径,从未受到影响。

待办事项。

  • 已添加回归测试:tests/single_stream_ipc_publish_test.cpp,用于覆盖之前未测试的条件分支。(Alex,已合并至 PR #5751。)
  • CI 覆盖缺口:在 nightly 中添加单流 + IPC 矩阵条目。(Alex,JIRA-12346。)
  • 更新文档:在 docs/launch_synchronization.md 中记录 Tada 启动快速路径不变量。(Alex,PR #5752。)
  • 相关事项:审查其他 numStreams == 1 快速路径,排查同类错误。(已登记为 JIRA-12347。) 这份复盘做到了管理层话术版本没有做到的事:
  • 列出每个代码标识符(tadaLaunchPrepare、scratchBuf、numStreams、handle->shared->deviceStream)。
  • 端到端梳理原因链,让读者能通过搜索定位到问题代码行。
  • 点明了之前的修复尝试(PR #5612)及其问题所在。
  • 记录了确切的实验,并据此确认原因(将 numStreams = 2 后,问题消失)。
  • 如实说明验证覆盖范围——“未在其他模型尺寸上重新测试”是有价值的信息,不是漏洞。
  • 行动项列明了负责人和跟踪记录。

规则

  • 缺少四项必需输入时,拒绝起草。 对假设进行复盘,甚至不如不写复盘。
  • 绝不编造根因、负责人、验证运行或行动项。 如果某个部分缺少事实依据,就提出询问。不要用看似合理的文字填补空白。
  • 绝不从工程记录中删除代码标识符。 它们是索引。管理层改写是 management-talk 的职责,不是你的。
  • 不归咎于个人。 描述缺口和缺陷,而不是个人。
  • 如实说明验证覆盖范围。 如果只测试了一种配置,就明确说明。暗示覆盖更广会导致回归问题反复出现。
  • 发布到 JIRA 前先取得批准。 仅打印输出无需批准。使用此技能时,绝不发布到非 JIRA 目的地。
  • 一次迭代很正常,三次就值得警惕。 如果用户在第三轮仍在修改,询问具体哪个部分有问题——不要继续盲目调整。

评论

支持 Markdown