# v0.7.0：重回 Chrome 域工具（5 个 row-preserving thin wrapper）+ list_stdlib_modules（辅助）

## Context

### 为什么回归 c93466b 方向（行为证据驱动）

**主论据：行为证据**。v0.3/v0.4/v0.6 六个观察样本显示：

| 样本 | tools/list 通道 | description URL / instructions 通道 |
|---|---|---|
| v0.3 #1（PDF 首屏）| — | 0/N INCLUDE，LIKE 扫 slice |
| v0.3 #2（Chrome 冷启）| — | 0/N INCLUDE |
| v0.4 #3（trace5 复测）| chrome_scroll_jank_summary 被调用 | 0/N INCLUDE 其他 stdlib |
| v0.6 #1（Claude, trace5）| — | 0/8 INCLUDE，手搓 4 表 JOIN |
| v0.6 #2（Codex, trace5）| — | 0/N INCLUDE，主动 list_tables schema discovery |
| v0.6 #3（Claude, trace4）| — | **≥4 INCLUDE**，但 **2 次模块名错误**（chrome.chrome_scrolls / chrome.chrome_loads 不存在），行级深挖质量极高 |

结论修订（更严谨版）：

1. **工具存在 = agent 稳定使用**（v0.4 chrome_scroll_jank_summary 被调用、v0.6 #2 主动 list_tables 验证）
2. **信息通道的 stdlib 激活率高方差 + 易错**：既有 0/N（#1/#2），也有 ≥4 with errors（#3）。不稳定且用错模块名——这正是 `list_stdlib_modules`（提供正确名字）+ domain tools（跳过名字猜测）要解决的
3. **行级数据支撑深度诊断**：#3 样本深挖到调用栈 depth 7 定位 DWriteFontProxy 同步 IPC 阻塞、forced synchronous layout 等细粒度问题——这是预聚合工具做不到的，row-preserving 设计被正面验证

**次论据：Perfetto 官方 stdlib 定位**。官方文档把 stdlib 定义为"共享常用查询 / 提升抽象层级"的机制（PerfettoSQL stdlib intro）。`chrome.page_loads` / `chrome.startups` / `chrome.tasks` / `chrome.web_content_interactions` 本身就是高层领域表。薄封装工具与官方意图**不冲突**——但这只说明方向合理，不能独立证明优于其他方案。真正的证明来自上面的行为数据。

**v0.6 删除域工具的错归因**：v0.6 把 c93466b 的"答案形"批评当成"域工具方向错"，但真正的问题是 **SQL 实现形态**（预聚合、列窄、过滤错）——方向没错。v0.7 恢复域工具，但用下面的工具形态原则避免 v0.5 的具体实现问题。

### 工具形态原则：精选展示、保持行级、不预聚合

所有 domain tool 遵循三条：

1. **精选展示**（non-goal：纯 `SELECT *` 薄封装）：tool 选一个常用排序 + LIMIT + 派生列（如时间单位换 ms），是有 opinion 的展示层；但 opinion 只限于"默认视角"，不限制 agent 后续分析维度。
2. **保持行级**（non-goal：预聚合 `GROUP BY`）：返回原始行，agent 自己决定是否 aggregation。
3. **不预聚合**：agent 看到每个 event 的细节，而不是"cause X 出现 42 次"这种预答案。

v0.5 `chrome_scroll_jank_summary` 违反了 (2)(3)（`COUNT(*) GROUP BY cause_of_jank`），因此被 v0.6 删除。v0.7 恢复时这 3 条全部满足。

c93466b 的具体问题：

| 工具 | 问题 |
|---|---|
| `chrome_scroll_jank_summary` | 只返回 `cause_of_jank + COUNT(*)`，丢失 `sub_cause_of_jank`、`delay_since_last_frame` 等关键列。v0.7 保留原名，**结构从聚合改为行级**（与同批 `chrome_page_load_summary` / `chrome_startup_summary` 命名一致，都是该领域的 top-N rows 概览）|
| `chrome_main_thread_hotspots` | 名字说"main thread"但 SQL 没有过滤——返回所有线程任务；缺少 `task_type`、`cpu_pct` |
| `chrome_page_load_summary` | 缺 `id`（无法用于 JOIN 或关联分析） |
| `chrome_startup_summary` | 缺 `id`、`browser_upid` |

本次 v0.7.0 **重新 ship 4 个 Chrome 域工具，基于 Perfetto stdlib 真实 schema 修正 SQL**，同时附带 `list_stdlib_modules`（M5 Foundation）供覆盖范围外的分析场景。

### Stdlib 真实 schema（来自 vendored source 审计）

**`chrome_janky_frames`**（`chrome.scroll_jank.scroll_jank_v3`）：
`cause_of_jank`, `sub_cause_of_jank`, `delay_since_last_frame` (DOUBLE, ms), `event_latency_id`, `vsync_interval`, `hardware_class`, `scroll_id`

**`chrome_tasks`**（`chrome.tasks`）：
`id`, `name`, `task_type`, `thread_name`, `utid`, `process_name`, `upid`, `ts`, `dur`, `track_id`, `category`, `arg_set_id`, `thread_ts`, `thread_dur`, `full_name`
— 视图覆盖所有线程（含 CrBrowserMain / CrRendererMain / 其他）；过滤主线程需显式 `WHERE thread_name IN (...)` 

**`chrome_page_loads`**（`chrome.page_loads`）：
`id`, `navigation_id`, `navigation_start_ts`, `fcp`, `fcp_ts`, `lcp`, `lcp_ts`, `dom_content_loaded_event_ts`, `load_event_ts`, `mark_fully_loaded_ts`, `mark_fully_visible_ts`, `mark_interactive_ts`, `url`, `browser_upid`

**`chrome_startups`**（`chrome.startups`）：
`id`, `activity_id`, `name`, `startup_begin_ts`, `first_visible_content_ts`, `launch_cause`, `browser_upid`

## 改动总览

### 1. `src/server.rs`

#### 1.1 恢复 `ChromeTraceParams` struct

```rust
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct ChromeTraceParams {
    /// Absolute path to the trace file (must be a Chrome trace).
    pub trace_path: String,
}
```

#### 1.2 恢复 4 个 SQL 常量（修正版）

```rust
/// SQL for chrome_scroll_jank_summary. Exported for integration tests.
/// Returns row-level janky frames (not pre-aggregated) so agents can do
/// their own grouping, correlation, and deep-dive queries after the first call.
/// Structure changed from v0.5 (pre-aggregated COUNT(*) GROUP BY cause_of_jank)
/// to row-level output — same tool name kept for consistency with the other
/// chrome_*_summary tools (all are row-level top-N overviews of their domain).
/// CHANGELOG marks this as breaking behavior for v0.5 upgraders.
pub const CHROME_SCROLL_JANK_SUMMARY_SQL: &str =
    "INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3; \
     SELECT \
       cause_of_jank, \
       sub_cause_of_jank, \
       delay_since_last_frame, \
       event_latency_id, \
       scroll_id, \
       vsync_interval \
     FROM chrome_janky_frames \
     ORDER BY delay_since_last_frame DESC \
     LIMIT 100";

/// SQL for chrome_page_load_summary. Exported for integration tests.
pub const CHROME_PAGE_LOAD_SUMMARY_SQL: &str =
    "INCLUDE PERFETTO MODULE chrome.page_loads; \
     SELECT \
       id, \
       url, \
       navigation_start_ts, \
       fcp / 1e6 AS fcp_ms, \
       lcp / 1e6 AS lcp_ms, \
       CASE WHEN dom_content_loaded_event_ts IS NOT NULL \
            THEN (dom_content_loaded_event_ts - navigation_start_ts) / 1e6 \
       END AS dcl_ms, \
       CASE WHEN load_event_ts IS NOT NULL \
            THEN (load_event_ts - navigation_start_ts) / 1e6 \
       END AS load_ms \
     FROM chrome_page_loads \
     ORDER BY navigation_start_ts ASC";

/// SQL for chrome_main_thread_hotspots. Exported for integration tests.
/// Uses thread.is_main_thread = 1 (tid == pid in trace_processor).
/// CAVEAT: is_main_thread is CppOptional and may be NULL for traces that
/// lack complete thread creation metadata — in that case the tool returns
/// empty rows (no SQL error). If empty, agents can fall back to execute_sql
/// with WHERE thread_name IN ('CrBrowserMain', 'CrRendererMain').
pub const CHROME_MAIN_THREAD_HOTSPOTS_SQL: &str =
    "INCLUDE PERFETTO MODULE chrome.tasks; \
     SELECT \
       ct.id, \
       ct.name, \
       ct.task_type, \
       ct.thread_name, \
       ct.process_name, \
       ct.dur / 1e6 AS dur_ms, \
       CASE WHEN ct.thread_dur IS NOT NULL AND ct.dur > 0 \
            THEN ROUND(ct.thread_dur * 100.0 / ct.dur, 1) \
       END AS cpu_pct, \
       ct.thread_dur / 1e6 AS thread_dur_ms \
     FROM chrome_tasks ct \
     JOIN thread t ON ct.utid = t.utid \
     WHERE t.is_main_thread = 1 \
       AND ct.dur > 16000000 \
     ORDER BY ct.dur DESC \
     LIMIT 100";

/// SQL for chrome_web_content_interactions. Exported for integration tests.
pub const CHROME_WEB_CONTENT_INTERACTIONS_SQL: &str =
    "INCLUDE PERFETTO MODULE chrome.web_content_interactions; \
     SELECT \
       id, \
       ts, \
       dur / 1e6 AS dur_ms, \
       interaction_type, \
       renderer_upid \
     FROM chrome_web_content_interactions \
     ORDER BY dur DESC \
     LIMIT 100";

/// SQL for chrome_startup_summary. Exported for integration tests.
pub const CHROME_STARTUP_SUMMARY_SQL: &str =
    "INCLUDE PERFETTO MODULE chrome.startups; \
     SELECT \
       id, \
       name, \
       launch_cause, \
       (first_visible_content_ts - startup_begin_ts) / 1e6 AS startup_duration_ms, \
       startup_begin_ts, \
       first_visible_content_ts, \
       browser_upid \
     FROM chrome_startups \
     ORDER BY startup_begin_ts ASC";
```

**与 c93466b 的差异说明**：

| 常量 | 差异（与 c93466b 对比）|
|---|---|
| `CHROME_SCROLL_JANK_SUMMARY_SQL` | **结构改为行级**（同名，behavioral breaking）：不再 `GROUP BY cause`，返回每个 janky frame 的 `cause_of_jank`, `sub_cause_of_jank`, `delay_since_last_frame`, `event_latency_id`, `scroll_id`, `vsync_interval`，按 `delay_since_last_frame DESC` 排序 LIMIT 100 |
| `CHROME_PAGE_LOAD_SUMMARY_SQL` | 新增 `id`（用于后续关联查询）|
| `CHROME_MAIN_THREAD_HOTSPOTS_SQL` | **关键修正**：`JOIN thread t ON ct.utid = t.utid WHERE t.is_main_thread = 1`（替代 c93466b 的无过滤）+ 新增 `id`, `task_type`, `cpu_pct`, `thread_dur_ms`；`dur > 0` 改 `dur > 16000000`（1 帧预算）|
| `CHROME_WEB_CONTENT_INTERACTIONS_SQL`（新增）| c93466b 没有该工具；v0.7 新增 INP 分析入口 |
| `CHROME_STARTUP_SUMMARY_SQL` | 新增 `id`, `browser_upid` |

#### 1.3 新增 `STDLIB_MODULE_LIST` 常量

```rust
/// Curated PerfettoSQL stdlib modules as a JSON array. Targets the default
/// downloaded trace_processor_shell version. If PERFETTO_TP_PATH points to
/// a custom binary, some modules may not be available — use list_table_structure
/// after INCLUDE to confirm. Exported for integration tests.
pub const STDLIB_MODULE_LIST: &str = r#"[
  {
    "domain": "chrome",
    "module": "chrome.page_loads",
    "views": ["chrome_page_loads"],
    "description": "Chrome navigations with FCP, LCP, DCL, and load timing in ms",
    "usage": "INCLUDE PERFETTO MODULE chrome.page_loads; SELECT id, url, fcp / 1e6 AS fcp_ms, lcp / 1e6 AS lcp_ms FROM chrome_page_loads ORDER BY navigation_start_ts"
  },
  {
    "domain": "chrome",
    "module": "chrome.scroll_jank.scroll_jank_v3",
    "views": ["chrome_janky_frames"],
    "description": "Scroll jank cause distribution — cause_of_jank, sub_cause_of_jank, delay_since_last_frame",
    "usage": "INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3; SELECT cause_of_jank, sub_cause_of_jank, COUNT(*) AS n FROM chrome_janky_frames GROUP BY cause_of_jank, sub_cause_of_jank ORDER BY n DESC"
  },
  {
    "domain": "chrome",
    "module": "chrome.tasks",
    "views": ["chrome_tasks"],
    "description": "Chrome main-thread and background task durations (id, name, task_type, thread_name, process_name, dur, thread_dur)",
    "usage": "INCLUDE PERFETTO MODULE chrome.tasks; SELECT name, task_type, thread_name, dur / 1e6 AS dur_ms FROM chrome_tasks WHERE thread_name IN ('CrBrowserMain','CrRendererMain') AND dur > 16000000 ORDER BY dur DESC LIMIT 50"
  },
  {
    "domain": "chrome",
    "module": "chrome.startups",
    "views": ["chrome_startups"],
    "description": "Chrome browser startup events — name, launch_cause, startup_duration (first_visible_content_ts - startup_begin_ts)",
    "usage": "INCLUDE PERFETTO MODULE chrome.startups; SELECT id, name, launch_cause, (first_visible_content_ts - startup_begin_ts) / 1e6 AS startup_ms FROM chrome_startups ORDER BY startup_begin_ts"
  },
  {
    "domain": "chrome",
    "module": "chrome.web_content_interactions",
    "views": ["chrome_web_content_interactions"],
    "description": "Input latency and Interaction to Next Paint (INP) in Chrome traces",
    "usage": "INCLUDE PERFETTO MODULE chrome.web_content_interactions; SELECT * FROM chrome_web_content_interactions LIMIT 20"
  },
  {
    "domain": "android",
    "module": "android.startup.startups",
    "views": ["android_startups"],
    "description": "Android app cold/warm startup phases and total launch duration",
    "usage": "INCLUDE PERFETTO MODULE android.startup.startups; SELECT * FROM android_startups LIMIT 20"
  },
  {
    "domain": "android",
    "module": "android.anrs",
    "views": ["android_anrs"],
    "description": "Android ANR (Application Not Responding) detection",
    "usage": "INCLUDE PERFETTO MODULE android.anrs; SELECT * FROM android_anrs LIMIT 20"
  },
  {
    "domain": "android",
    "module": "android.binder",
    "views": ["android_binder_txns"],
    "description": "Android Binder IPC transactions with caller/callee and duration",
    "usage": "INCLUDE PERFETTO MODULE android.binder; SELECT * FROM android_binder_txns LIMIT 50"
  },
  {
    "domain": "generic",
    "module": "slices.with_context",
    "views": ["thread_slice", "process_slice"],
    "description": "Slice with thread and process names pre-joined — use this INSTEAD OF the manual slice→thread_track→thread→process JOIN chain",
    "usage": "INCLUDE PERFETTO MODULE slices.with_context; SELECT name, thread_name, process_name, dur / 1e6 AS dur_ms FROM thread_slice WHERE dur > 10000000 ORDER BY dur DESC LIMIT 50"
  },
  {
    "domain": "generic",
    "module": "linux.cpu.frequency",
    "views": ["cpu_frequency_counters"],
    "description": "CPU frequency over time per core",
    "usage": "INCLUDE PERFETTO MODULE linux.cpu.frequency; SELECT * FROM cpu_frequency_counters LIMIT 50"
  }
]"#;
```

#### 1.4 新增 `ListStdlibModulesParams` struct

```rust
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct ListStdlibModulesParams {}
```

#### 1.5 恢复 4 个 Chrome tool handler + 新增 `list_stdlib_modules`

**Chrome 4 个 handler**（与 c93466b 结构相同，description 更新反映修正）：

`chrome_scroll_jank_summary`（保留原名，结构改为行级）— description：
```
Return the worst scroll jank frames in a Chrome trace — one row per janky
frame, sorted by delay_since_last_frame DESC. Columns: cause_of_jank,
sub_cause_of_jank, delay_since_last_frame, event_latency_id, scroll_id,
vsync_interval. Row-level data lets agents group, filter, and correlate
further. Uses chrome.scroll_jank.scroll_jank_v3. Chrome traces only.
```

`chrome_page_load_summary` — description 不变，保留 `id` 字段说明：
```
Summarize page loads: navigation id, URL, FCP / LCP / DCL / load times
in ms. Uses chrome.page_loads. Chrome traces only.
```

`chrome_main_thread_hotspots` — description 说明使用 `is_main_thread = 1` 及 fallback：
```
Top Chrome main-thread tasks by wall duration, 16 ms threshold.
Uses thread.is_main_thread = 1 (tid == pid per Linux convention) to identify
main threads. Returns id, name, task_type, thread_name, process_name, dur_ms,
cpu_pct, thread_dur_ms. Uses chrome.tasks. Chrome traces only.
If the result is empty (trace missing thread metadata), retry with execute_sql
filtering on thread_name IN ('CrBrowserMain', 'CrRendererMain').
```

`chrome_startup_summary` — description 说明新增字段：
```
Summarize Chrome browser startup events: id, name, launch_cause,
startup_duration_ms (first_visible_content_ts - startup_begin_ts),
browser_upid. Uses chrome.startups. Chrome traces only.
```

`chrome_web_content_interactions`（新增）— description：
```
Return web content interactions (clicks, taps, keyboard input, drags) in a
Chrome trace, sorted by duration DESC. Columns: id, ts, dur_ms,
interaction_type, renderer_upid. Useful for INP (Interaction to Next Paint)
analysis. Uses chrome.web_content_interactions. Chrome traces only.
```

**`list_stdlib_modules` handler**：
```rust
#[tool(
    name = "list_stdlib_modules",
    description = "List a curated set of PerfettoSQL stdlib modules for the default \
                   trace_processor_shell version. Returns a JSON array — each entry \
                   has `module` (for INCLUDE PERFETTO MODULE), `domain` \
                   (chrome / android / generic), `views`, `description`, and an \
                   illustrative `usage` query (verify column names with \
                   list_table_structure if needed).\n\
                   \n\
                   Takes no parameters — call this before loading a trace to \
                   discover which modules cover analyses not handled by the \
                   dedicated chrome_* tools. Then use execute_sql with \
                   `INCLUDE PERFETTO MODULE <module>` — INCLUDE and SELECT can be \
                   in a single call.\n\
                   \n\
                   If PERFETTO_TP_PATH points to a custom binary, some modules may \
                   not be available in that version."
)]
async fn list_stdlib_modules(
    &self,
    Parameters(_params): Parameters<ListStdlibModulesParams>,
) -> Result<String, String> {
    Ok(STDLIB_MODULE_LIST.to_owned())
}
```

#### 1.6 恢复 `format_chrome_tool_error`

```rust
fn format_chrome_tool_error(tool_label: &str, err: PerfettoError) -> String {
    match err {
        PerfettoError::QueryError {
            kind: QueryErrorKind::MissingTable | QueryErrorKind::MissingModule,
            message,
        } => format!(
            "Failed to run {tool_label}: {message}\n\nHint: this tool \
             requires a Chrome trace. For non-Chrome traces, use execute_sql \
             with a different query."
        ),
        PerfettoError::QueryError { message, .. } => {
            format!("Failed to run {tool_label}: {message}")
        }
        other => format!("Failed: {other}"),
    }
}
```

#### 1.7 更新 `execute_sql` description

在现有 description 的 stdlib URL 段落前插入一行：

```
Call `list_stdlib_modules` (no trace needed) to see curated modules with \
usage examples. The dedicated chrome_* tools cover the most common Chrome \
analyses directly.\n\
\n\
```

#### 1.8 更新 `tool_router_exposes_expected_tools`（12 个工具按字母序）

```rust
vec![
    "chrome_main_thread_hotspots",
    "chrome_page_load_summary",
    "chrome_scroll_jank_summary",
    "chrome_startup_summary",
    "chrome_web_content_interactions",
    "execute_sql",
    "list_processes",
    "list_stdlib_modules",
    "list_table_structure",
    "list_tables",
    "list_threads_in_process",
    "load_trace",
]
```

#### 1.9 恢复 Chrome hint 单测 + 新增 stdlib 两个单测

**恢复**（针对 `format_chrome_tool_error`，重命名以反映共用 helper 覆盖所有 chrome_* 工具）：
- `chrome_tool_hint_fires_on_missing_table`（原 `chrome_scroll_jank_hint_fires_on_missing_table`，泛化命名）
- `chrome_tool_hint_fires_on_missing_module`（同上泛化）
- `chrome_tool_skips_unrelated_query_errors`（同上泛化）

**新增**（之前 plan 设计的两个）：
- `list_stdlib_modules_returns_all_domains`（精确 10 模块 + 3 域 + 字段校验）
- `stdlib_module_list_and_instructions_are_in_sync`（cross-check 防漂移）

### 2. `tests/e2e_chrome_domain_tools.rs`（重建）

每个工具一个 e2e 测试，分两档强度：

**强断言**（fixture 已知有数据）：
- `e2e_chrome_scroll_jank_summary_against_fixture`（port 19_101）—— `scroll_jank.pftrace`，断言 `!rows.is_empty()` + 列字段齐全（cause_of_jank, sub_cause_of_jank, delay_since_last_frame, event_latency_id, scroll_id, vsync_interval）
- `e2e_chrome_page_load_summary_against_fixture`（port 19_201）—— **恢复 `page_loads.pftrace`**（见 2.1），断言 `!rows.is_empty()` + 列字段（id, url, fcp_ms, lcp_ms）

**弱断言**（SQL 跑通即可，允许空结果）：
- `e2e_chrome_main_thread_hotspots_sql_runs_cleanly`（port 19_301）—— `scroll_jank.pftrace`；**需实测确认** `is_main_thread` 在该 fixture 被填充，否则改用其他 Chrome fixture 或接受空
- `e2e_chrome_startup_summary_sql_runs_cleanly`（port 19_401）—— 当前无专用 fixture；接受空结果，CHANGELOG 标注 "strong e2e pending fixture"
- `e2e_chrome_web_content_interactions_sql_runs_cleanly`（port 19_501）—— 新增工具的最小覆盖

### 2.1 fixture 策略（分层）

**恢复**：
- `tests/fixtures/page_loads.pftrace` 从 git history 恢复（v0.6 commit 832bf98 前存在，~2.3 MB）：
  ```sh
  git show 183c757:tests/fixtures/page_loads.pftrace > tests/fixtures/page_loads.pftrace
  ```
  （commit `183c757` = v0.3.0 release commit，是已确认含该 fixture 的稳定 commit）

**接受空结果 + TODO**：
- startup e2e 没有专用 fixture；使用 `scroll_jank.pftrace` 跑通 SQL 即可，后续补录 Chrome 冷启动 trace 时再升级为强断言

**先验证再决定**：
- `chrome_main_thread_hotspots` 依赖 `thread.is_main_thread` 被填充。执行 plan 时第一步先跑：
  ```sql
  INCLUDE PERFETTO MODULE chrome.tasks;
  SELECT COUNT(*) FROM chrome_tasks ct
    JOIN thread t ON ct.utid = t.utid
    WHERE t.is_main_thread = 1
  ```
  on `scroll_jank.pftrace`。若返回 > 0，用 scroll_jank.pftrace 做强断言；若返回 0，降级为弱断言 + 在 plan 执行时记录结果。

### 3. 文档更新

#### 3.1 `README.md` Tools 表（新增 5 行：4 Chrome + list_stdlib_modules）

```markdown
| `chrome_scroll_jank_summary` | Worst janky frames with cause, sub-cause, delay_since_last_frame (Chrome trace) |
| `chrome_page_load_summary` | Page loads: URL, FCP, LCP, DCL, load timings in ms (Chrome trace) |
| `chrome_main_thread_hotspots` | Top main-thread tasks by duration with cpu_pct, uses is_main_thread (Chrome trace) |
| `chrome_startup_summary` | Browser startup events and time-to-first-visible-content (Chrome trace) |
| `chrome_web_content_interactions` | Web content interactions (clicks, taps, INP) ranked by duration (Chrome trace) |
| `list_stdlib_modules` | List available PerfettoSQL stdlib modules with usage examples (no trace needed) |
```

#### 3.2 Typical flow 更新（按 trace 类型分流）

```markdown
Typical flow depends on trace type:

- **Chrome traces**: `load_trace` → dedicated `chrome_*` tools
  (`chrome_scroll_jank_summary`, `chrome_page_load_summary`,
  `chrome_main_thread_hotspots`, `chrome_startup_summary`,
  `chrome_web_content_interactions`) → `execute_sql` for deeper analysis
  on the returned rows.

- **Other traces**: `load_trace` → `list_tables` / `list_table_structure`
  for schema discovery → `execute_sql` for queries. Call
  `list_stdlib_modules` as an auxiliary when stdlib modules might cover
  your analysis (Android, generic modules like `slices.with_context`).
```

#### 3.3 `README.zh-CN.md` 镜像同步

#### 3.4 `CHANGELOG.md` 新增 v0.7.0 条目

```markdown
### [0.7.0](https://github.com/0xZOne/perfetto-mcp-rs/releases/tag/v0.7.0) (April 17, 2026)

- **Breaking**: tool surface change from v0.6. Four Chrome domain tools return as row-preserving
  thin wrappers over PerfettoSQL stdlib views, plus one new tool. v0.5→v0.7 upgraders see a fully
  new Chrome tool set; v0.6→v0.7 see these as additions.
- **Breaking (behavioral)**: `chrome_scroll_jank_summary` keeps its v0.5 name but **changes
  output structure** from pre-aggregated frequency (`COUNT(*) GROUP BY cause_of_jank`) to
  row-level worst-frame output (sorted by `delay_since_last_frame DESC`, limit 100). The name
  is preserved for consistency with the other `chrome_*_summary` tools — all five Chrome tools
  return row-level top-N overviews of their domain entity. New columns: `sub_cause_of_jank`,
  `event_latency_id`, `scroll_id`, `vsync_interval` — per-frame data lets agents group,
  filter, and correlate rather than stopping at a pre-baked table. v0.5.0 users must adapt
  to the new output shape even though the tool name is unchanged.
- `chrome_main_thread_hotspots` uses `thread.is_main_thread = 1` (Linux convention: `tid == pid`,
  populated in `trace_processor` when thread metadata is present) to identify main threads —
  more robust than filtering by thread name strings that could break across Chrome versions.
  Adds `task_type`, `cpu_pct` (thread_dur/dur), `thread_dur_ms`. **Caveat**: `is_main_thread` is
  `CppOptional` and may be NULL for traces lacking complete thread creation metadata; empty
  result means retry with `execute_sql WHERE thread_name IN ('CrBrowserMain', 'CrRendererMain')`.
- `chrome_page_load_summary` and `chrome_startup_summary` add `id` (and `browser_upid` for
  startup) for correlation queries. New `chrome_web_content_interactions` surfaces clicks, taps,
  and keyboard interactions ranked by duration for INP analysis.
- Design rationale: v0.3/v0.4/v0.6 observation samples (six sessions across Claude and Codex)
  show `tools/list` channel drives stable behavior — tools get called reliably. The description-URL
  / `instructions` channel shows high variance: four sessions had 0/N INCLUDE, one had ≥4
  INCLUDE but with two wrong module names (`chrome.chrome_scrolls` and `chrome.chrome_loads`
  don't exist). Both failure modes — not using stdlib, and using it with wrong names — are
  solved by domain tools (skip the name) + `list_stdlib_modules` (provide the correct name).
  One session demonstrated agents depth-drilling to call-stack depth 7 (DWriteFontProxy sync
  IPC, forced synchronous layout) — only possible with row-level output, validating the
  row-preserving design. v0.6 removed Chrome domain tools based on the "answer-shaped"
  critique of v0.5's pre-aggregated SQL — but that critique applied to implementation, not
  direction. v0.7 restores the tools with row-preserving SQL following the Perfetto stdlib's
  own "high-level domain tables" design intent.
- Add `list_stdlib_modules` tool: returns a curated JSON array of PerfettoSQL stdlib modules
  (module name, domain, views, description, illustrative usage). Takes no parameters — call
  before loading a trace to discover which stdlib module covers analyses **not** handled by the
  dedicated `chrome_*` tools (Android tools, `slices.with_context`, etc.). Positioned as
  auxiliary discovery, not the primary path.
```

#### 3.5 `Cargo.toml` version bump

`0.6.0` → `0.7.0`

### 4. `docs/roadmap.md` / `docs/roadmap.zh-CN.md` 更新

M5 Chrome Tools 的 deferred 注释修改：将 "walked back in v0.6, reopened in v0.7 with corrected SQL" 的历史括注加入，替换"延后"措辞。M5 Foundation 的 `list_stdlib_modules` 条目标记为已完成（`[x]`）。

## 执行顺序

1. **前置验证**（先于任何代码改动）：
   - 恢复 `tests/fixtures/page_loads.pftrace`：`git show 183c757:tests/fixtures/page_loads.pftrace > tests/fixtures/page_loads.pftrace`
   - 用 `scroll_jank.pftrace` 跑 `is_main_thread = 1` 的验证查询（见 2.1），确认 chrome_tasks 在该 fixture 上是否有主线程数据。结果决定 `e2e_chrome_main_thread_hotspots` 是强断言还是弱断言。记录结果于 commit message
2. `src/server.rs`（顺序：1.1 → 1.2 → 1.3 → 1.4 → 1.5 → 1.6 → 1.7 → 1.8 → 1.9）
3. 创建 `tests/e2e_chrome_domain_tools.rs`（5 个测试，端口 19_101-19_501）
4. `Cargo.toml` version `0.6.0` → `0.7.0`
5. `CHANGELOG.md` 新增 v0.7.0（含 breaking 标注）
6. `README.md` + `README.zh-CN.md`（Tools 表 + 分流的 typical flow）
7. `docs/roadmap.md` + `docs/roadmap.zh-CN.md`（M5 Chrome Tools 状态更新 + Foundation list_stdlib_modules [x]）
8. CI gate：`cargo fmt --all --check && cargo clippy --all-targets -- -D warnings && cargo test --all-targets`
9. Commit：`feat: re-ship Chrome domain tools with row-preserving SQL, add list_stdlib_modules (v0.7.0)`

## 验证

### 测试层
- `tool_router_exposes_expected_tools`：12 个工具按字母序（含 `chrome_scroll_jank_summary`（行级）/ `chrome_web_content_interactions` / `list_stdlib_modules`）
- `chrome_tool_hint_fires_on_missing_table/module`：Chrome hint 路径（适用于全部 5 个 chrome_* 工具）
- `list_stdlib_modules_returns_all_domains`：精确 10 模块 + 3 域
- `stdlib_module_list_and_instructions_are_in_sync`：防漂移 cross-check
- `e2e_chrome_scroll_jank_summary_against_fixture`：非空 + 行级字段齐全
- `e2e_chrome_page_load_summary_against_fixture`：非空 + fcp_ms / lcp_ms 字段（依赖恢复的 `page_loads.pftrace`）
- `e2e_chrome_main_thread_hotspots_sql_runs_cleanly` / `e2e_chrome_startup_summary_sql_runs_cleanly` / `e2e_chrome_web_content_interactions_sql_runs_cleanly`：SQL 能执行（弱断言）

### 行为观察（v0.7 首个样本）
用 trace5.json 重新采样（与 v0.6 样本 1/2 同一 trace 以保证可比），记录：
1. agent 是否在分析开始时直接调用 `chrome_main_thread_hotspots` / `chrome_scroll_jank_summary`（v0.4 `chrome_scroll_jank_summary` 被调用的复现验证——新结构输出是否仍激活 agent）
2. `is_main_thread = 1` 过滤在 trace5.json 上是否返回非空（验证 nullable caveat 不触发）
3. agent 在工具返回行级数据后是否继续自写 SQL 做深度分析（验证"保持行级"不止步于工具输出）
4. 对比 v0.6 样本：stdlib usage rate 是否从 0/N 跃升

## 关键文件

- `src/server.rs`（ChromeTraceParams / ListStdlibModulesParams structs + 5 chrome_* + 1 list_stdlib_modules handlers + 5 SQL constants + STDLIB_MODULE_LIST + format_chrome_tool_error + execute_sql description update + 12-tool router test + 5 new unit tests）
- `tests/e2e_chrome_domain_tools.rs`（重建，5 个测试）
- `tests/fixtures/page_loads.pftrace`（从 git history 恢复）
- `Cargo.toml`（0.6.0 → 0.7.0）
- `CHANGELOG.md`（新增 v0.7.0 含 breaking 标注）
- `README.md` + `README.zh-CN.md`（Tools 表 5 新行 + 按 trace type 分流的 typical flow）
- `docs/roadmap.md` + `docs/roadmap.zh-CN.md`（M5 Chrome Tools 状态从 "deferred" 更新为 v0.7 已发布 + Foundation list_stdlib_modules 勾选）

## Non-goals

- 不做 `chrome_blocking_calls_summary`（无对应 stdlib 模块，需要原始 slice 扫描，不符合 thin wrapper 原则）
- 不录制 / 新增 startup 专用 fixture（使用现有 Chrome fixture 跑通 SQL，强 e2e 作为 post-v0.7 improvement）
- 不修改 `STDLIB_INSTRUCTIONS` 常量文本（cross-check 测试已保证两者同步）
- 不把 `list_stdlib_modules` 当 P0 foundation（行为数据显示 domain tools 是主激活路径；list_stdlib_modules 仅覆盖 5 个 Chrome 工具之外的场景）
- 不在 v0.7 扩张到 android / generic domain tools（见下 scaling 原则）

### Scaling 原则（防未来无序膨胀）

每新增一个 domain tool 前验证：

1. **有稳定 stdlib 视图支撑**（不手搓 SQL 聚合）
2. **能承载 thin wrapper 形态**（精选展示 + 行级 + 不预聚合）
3. **有对应的 e2e fixture**（强断言覆盖，不是只跑通 SQL）
4. **不是已能通过现有工具 + `list_stdlib_modules` 覆盖的场景**

满足以上 4 条才考虑加入。v0.7 的 5 个 Chrome 工具定下模板，后续 android / generic 工具复用。
