zwdroid-android-bug-analysis — Android/AAOS 根因分析工作流
有纪律的人机协作:用户是法官,你是检察官兼调查员。你的产出是一条从症状回溯到
责任组件/代码位置的证据链,或一份"证据不足 + 补抓建议"。没有证据链的结论不输出。
日志总量(单文件约 10 万行,常跨多文件)远超 context。因此核心方法是:
脚本做廉价的结构过滤,你只做昂贵的语义判断;一切结论落盘到 ledger 对抗 context 腐烂。
先做:读目标项目的运行时 CLAUDE.md「配置区」
环境事实(时间戳格式、文件命名、源码目录是否配置、各预算参数)都在那里,不要凭记忆假设。
项目还没有这份文件 → 从 ${CLAUDE_SKILL_DIR}/references/runtime-claude-template.md 复制一份到
项目根,和用户确认环境事实后再开始(脚本侧默认值在 scripts/config.py,与模板一致)。
源码目录未配置 → 源码开关默认关,走日志-only 路径。
工作区
每个 bug 一个工作区(建在目标项目内,不在 skill 目录里),从模板复制:
cp -r ${CLAUDE_SKILL_DIR}/workspace-template rca-workspace/<bug-id>
结构:ledger.md(唯一真相源,只有你能写)· manifest/(ingest 索引)· timeline.md ·
slices/ · findings/ · report.md。context 被压缩或换新会话后,读 ledger.md 即可恢复续跑。
脚本(Python3 标准库;详见 references)
一律以 python3 ${CLAUDE_SKILL_DIR}/scripts/<name>.py 调用(相对路径会解析到项目根而非 skill 目录):
| 脚本 |
用途 |
rca-ingest.py <log-dir> --out <manifest> |
一次性索引:boot session/文件统计/进程-pid 表/时钟跳变/损耗区间 |
log-batch.py <anchor> <radius> --manifest <dir> |
session 内产出 batch 文件清单(不跨 session) |
logcat-window.py <start> <dur> --manifest <dir> [--preset|--tags] [--level] --files ... |
通道一:时间窗+tag/level 切片 |
scream-scan.py <files...> |
通道二:异常信号扫描(折叠去重、带 loss 段) |
logcat-around-pid.py <pid> --manifest <dir> --files ... |
某进程上下文,跨 pid 接续 |
aaos-grep.py <pattern> |
源码搜索(仅源码开关开时) |
脚本铁律:输入只接受原始日志(喂切片会报错);输出每行带 原始文件:原始行号| 前缀;
过滤先解析字段不做纯文本 grep;引用证据一律回指原始 文件:行号,不引切片坐标。
主状态机
S0 接收 → S1 Triage → S2 Locate ⇄ S3 Reconstruct → S4 Hypothesize
↓(穷尽) ↑(候选耗尽) ↓
S6b 证据不足 源码开关?——关→ S6a 结论(形态 B)
└开→ S5 Verify → S6a(形态 A)/证伪回 S4
- S0 接收:复述用户输入(时间/现象/线索),全部标注为假设入账 ledger(R8);复制工作区;
跑
rca-ingest.py 产出 manifest(仅首次)。
- S1 Triage:按 references/presets.md 路由表定 bug 类型、时间剖面(fast/slow)、取证范围、tag preset。
取证范围含制品(anr/tombstone/dropbox,按原生文件夹名查找)时先确认文件存在。
- S2 Locate(核心,详见 references/locate.md):查 manifest 两步定位锚点 → batch → 双通道
(通道一 preset 切片建 timeline;通道二 scream-scan 扫异常,两通道并列不合并 R10)→
锚点 sanity check(三态:三对齐命中 / 部分命中→锚点校正 / 无特征→查损耗区间后策略切换,
策略切换 ≤2 次)→ 语义匹配 → 未果则 session 内扩展(禁止跨 session R12);穷尽或达
{{MAX_ROUNDS}} → S6b。找到"哪个进程何时何行为致何结果"→ explore 精定位(预算硬顶
{{SLICE_LINE_CAP}},超限收紧 tag 禁止扩窗 R6)。
- S3 Reconstruct(references/reconstruct.md):精确窗口重建事件序列写 timeline.md
(每条带 userId/进程:pid + 证据 file:line,≤一屏);取证范围含制品则解析制品为必做项
(ANR trace 主线程栈/锁链、tombstone backtrace);跨 pid 查对照表接续;时序空洞可作证据,
但先排除 manifest 损耗/静默区间(R13)。
- S4 Hypothesize(references/hypothesize.md):生成 2-3 个候选根因(禁 1 禁 >3),每个带
支持/反对证据、概率、验证成本,写 ledger。源码开关关 → 最高优先级且有日志证据支撑者
直接 → S6a;开 → 选定假设 → S5。
- S5 Verify(条件,references/verify.md):仅源码开关开时进入。先书面写"若假设为真源码
中应有什么",再
aaos-grep.py 实证,任何类/方法/路径入结论前必须有 grep 命中(R2)。
证实→S6a 形态 A;证伪→S4;无法判定→该候选冻结回 S4。
- S6a 结论:输出 report.md。形态 B(默认)=责任组件+触发条件+行为,指针为日志/制品
file:line,闭源组件指向接口+违约行为;形态 A(源码开关开)=落到源码 path:line + 修复方向。
- S6b 证据不足(合法终态,R7):已确认事实 + 已排除项 + 证据缺口 + 补抓建议 + 下一步。
每次状态转移后执行 ledger 轮次协议(references/ledger.md §轮次协议):新证据入账、
显式声明本轮丢弃项(排除的候选/无关切片不再参与推理)、更新 next_action(R9)。
红线(HARD RULES,优先级最高,逐条不可绕过)
- R1 没有证据链的结论不输出;每个结论可回溯到
文件:行号(日志/制品/源码)。
- R2 源码引用必须 grep 实证,禁止凭记忆引用类/方法/路径。(源码开关开时绝对生效)
- R3 描述→日志映射三步走:先写预期签名 → 再 grep → 再三对齐(时间/包名/参数),禁跳步。
- R4 ledger 单一写者:仅主 agent 可写。
- R5 subagent 返回指针不返回散文:原文+行号落盘 findings,返回只含结论+路径。
- R6 explore 预算硬顶 {{SLICE_LINE_CAP}}:超限收紧 tag,禁止扩窗。
- R7 "证据不足+补抓建议"是合法终态,禁止硬凑结论。
- R8 用户线索一律按假设入账,非事实。
- R9 每轮执行轮次协议,被排除项不再参与推理。
- R10 双通道(时间线/尖叫扫描)结果并列呈现,永不合并、不互相覆盖。
- R11 溯源:一切证据引用回指原始
文件:行号;禁止引用派生切片自身坐标。
- R12 batch 扩展与采样禁止静默跨越 boot session 边界;跨 session 仅在重启类 bug 且 ledger 显式声明。
- R13 缺失不即证据:以"日志中不存在 X"为据前,先对照 manifest 损耗区间与静默区间;
落在区间内则不得作为证据,也不得据此触发策略切换或中断。
references(进入对应状态才读)
locate.md · reconstruct.md · hypothesize.md · verify.md · artifacts.md ·
ledger.md · presets.md。冲突裁决:红线 > 本文件 > references > 自行判断。
1---2name: zwdroid-android-bug-analysis3description: Android/AAOS bug 根因分析(RCA)工作流。当用户提供 logcat 日志(及 ANR trace/tombstone/dropbox 制品)要求定位问题、分析崩溃/ANR/重启/焦点错乱/ 卡顿/泄漏,或追踪某进程/某时间点发生了什么时使用。触发词:logcat、 日志分析、根因、RCA、ANR、crash、tombstone、watchdog、AAOS、 CarService、VHAL、lmk。不处理日志抓取/解压/分包;不解析 kernel log/pstore(硬重启类到此为界);帧级卡顿归因需 perfetto,不在覆盖内; 仅支持 threadtime 格式。4---56# zwdroid-android-bug-analysis — Android/AAOS 根因分析工作流78有纪律的人机协作:**用户是法官,你是检察官兼调查员**。你的产出是一条从症状回溯到9责任组件/代码位置的**证据链**,或一份"证据不足 + 补抓建议"。没有证据链的结论不输出。1011日志总量(单文件约 10 万行,常跨多文件)远超 context。因此核心方法是:12**脚本做廉价的结构过滤,你只做昂贵的语义判断;一切结论落盘到 ledger 对抗 context 腐烂。**1314## 先做:读目标项目的运行时 CLAUDE.md「配置区」15环境事实(时间戳格式、文件命名、源码目录是否配置、各预算参数)都在那里,不要凭记忆假设。16项目还没有这份文件 → 从 `${CLAUDE_SKILL_DIR}/references/runtime-claude-template.md` 复制一份到17项目根,和用户确认环境事实后再开始(脚本侧默认值在 `scripts/config.py`,与模板一致)。18源码目录未配置 → **源码开关默认关**,走日志-only 路径。1920## 工作区21每个 bug 一个工作区(建在目标项目内,不在 skill 目录里),从模板复制:22```23cp -r ${CLAUDE_SKILL_DIR}/workspace-template rca-workspace/<bug-id>24```25结构:`ledger.md`(唯一真相源,只有你能写)· `manifest/`(ingest 索引)· `timeline.md` ·26`slices/` · `findings/` · `report.md`。**context 被压缩或换新会话后,读 ledger.md 即可恢复续跑。**2728## 脚本(Python3 标准库;详见 references)29一律以 `python3 ${CLAUDE_SKILL_DIR}/scripts/<name>.py` 调用(相对路径会解析到项目根而非 skill 目录):30| 脚本 | 用途 |31|------|------|32| `rca-ingest.py <log-dir> --out <manifest>` | 一次性索引:boot session/文件统计/进程-pid 表/时钟跳变/损耗区间 |33| `log-batch.py <anchor> <radius> --manifest <dir>` | session 内产出 batch 文件清单(不跨 session) |34| `logcat-window.py <start> <dur> --manifest <dir> [--preset\|--tags] [--level] --files ...` | 通道一:时间窗+tag/level 切片 |35| `scream-scan.py <files...>` | 通道二:异常信号扫描(折叠去重、带 loss 段) |36| `logcat-around-pid.py <pid> --manifest <dir> --files ...` | 某进程上下文,跨 pid 接续 |37| `aaos-grep.py <pattern>` | 源码搜索(仅源码开关开时) |3839脚本铁律:输入只接受**原始日志**(喂切片会报错);输出每行带 `原始文件:原始行号|` 前缀;40过滤先解析字段不做纯文本 grep;引用证据一律回指原始 `文件:行号`,不引切片坐标。4142## 主状态机4344```45S0 接收 → S1 Triage → S2 Locate ⇄ S3 Reconstruct → S4 Hypothesize46 ↓(穷尽) ↑(候选耗尽) ↓47 S6b 证据不足 源码开关?——关→ S6a 结论(形态 B)48 └开→ S5 Verify → S6a(形态 A)/证伪回 S449```5051- **S0 接收**:复述用户输入(时间/现象/线索),**全部标注为假设**入账 ledger(R8);复制工作区;52 跑 `rca-ingest.py` 产出 manifest(仅首次)。53- **S1 Triage**:按 references/presets.md 路由表定 bug 类型、时间剖面(fast/slow)、取证范围、tag preset。54 取证范围含制品(anr/tombstone/dropbox,按原生文件夹名查找)时先确认文件存在。55- **S2 Locate**(核心,详见 references/locate.md):查 manifest 两步定位锚点 → batch → **双通道**56 (通道一 preset 切片建 timeline;通道二 scream-scan 扫异常,**两通道并列不合并** R10)→57 锚点 sanity check(三态:三对齐命中 / 部分命中→锚点校正 / 无特征→查损耗区间后策略切换,58 策略切换 ≤2 次)→ 语义匹配 → 未果则 session 内扩展(禁止跨 session R12);穷尽或达59 {{MAX_ROUNDS}} → S6b。找到"哪个进程何时何行为致何结果"→ explore 精定位(预算硬顶60 {{SLICE_LINE_CAP}},超限收紧 tag 禁止扩窗 R6)。61- **S3 Reconstruct**(references/reconstruct.md):精确窗口重建事件序列写 timeline.md62 (每条带 userId/进程:pid + 证据 file:line,≤一屏);取证范围含制品则**解析制品为必做项**63 (ANR trace 主线程栈/锁链、tombstone backtrace);跨 pid 查对照表接续;时序空洞可作证据,64 **但先排除 manifest 损耗/静默区间**(R13)。65- **S4 Hypothesize**(references/hypothesize.md):生成 **2-3 个**候选根因(禁 1 禁 >3),每个带66 支持/反对证据、概率、验证成本,写 ledger。源码开关关 → 最高优先级且有日志证据支撑者67 直接 → S6a;开 → 选定假设 → S5。68- **S5 Verify**(条件,references/verify.md):**仅源码开关开时进入**。先书面写"若假设为真源码69 中应有什么",再 `aaos-grep.py` 实证,任何类/方法/路径入结论前必须有 grep 命中(R2)。70 证实→S6a 形态 A;证伪→S4;无法判定→该候选冻结回 S4。71- **S6a 结论**:输出 report.md。**形态 B(默认)**=责任组件+触发条件+行为,指针为日志/制品72 file:line,闭源组件指向接口+违约行为;**形态 A(源码开关开)**=落到源码 path:line + 修复方向。73- **S6b 证据不足**(合法终态,R7):已确认事实 + 已排除项 + 证据缺口 + **补抓建议** + 下一步。7475每次状态转移后执行 **ledger 轮次协议**(references/ledger.md §轮次协议):新证据入账、76**显式声明本轮丢弃项**(排除的候选/无关切片不再参与推理)、更新 next_action(R9)。7778## 红线(HARD RULES,优先级最高,逐条不可绕过)79- **R1** 没有证据链的结论不输出;每个结论可回溯到 `文件:行号`(日志/制品/源码)。80- **R2** 源码引用必须 grep 实证,禁止凭记忆引用类/方法/路径。(源码开关开时绝对生效)81- **R3** 描述→日志映射三步走:先写预期签名 → 再 grep → 再三对齐(时间/包名/参数),禁跳步。82- **R4** ledger 单一写者:仅主 agent 可写。83- **R5** subagent 返回指针不返回散文:原文+行号落盘 findings,返回只含结论+路径。84- **R6** explore 预算硬顶 {{SLICE_LINE_CAP}}:超限收紧 tag,禁止扩窗。85- **R7** "证据不足+补抓建议"是合法终态,禁止硬凑结论。86- **R8** 用户线索一律按假设入账,非事实。87- **R9** 每轮执行轮次协议,被排除项不再参与推理。88- **R10** 双通道(时间线/尖叫扫描)结果并列呈现,永不合并、不互相覆盖。89- **R11** 溯源:一切证据引用回指**原始** `文件:行号`;禁止引用派生切片自身坐标。90- **R12** batch 扩展与采样禁止静默跨越 boot session 边界;跨 session 仅在重启类 bug 且 ledger 显式声明。91- **R13** 缺失不即证据:以"日志中不存在 X"为据前,先对照 manifest 损耗区间与静默区间;92 落在区间内则不得作为证据,也不得据此触发策略切换或中断。9394## references(进入对应状态才读)95`locate.md` · `reconstruct.md` · `hypothesize.md` · `verify.md` · `artifacts.md` ·96`ledger.md` · `presets.md`。冲突裁决:**红线 > 本文件 > references > 自行判断**。