[
 {
  "id": "T01",
  "target": "ai_session_search_archive",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不能直接拿旧文件列表跑。**\n\n语义搜索的 file-list 同时也是**结果允许名单**：任何不在名单里的文件，即使缓存里有向量，也不会被返回。\n\n### 正确做法\n\n1. **重新生成文件列表**（用当前归档目录实时扫描）：\n   ```bash\n   FILELIST=\"$(mktemp)\"\n   trap 'rm -f \"$FILELIST\"' EXIT\n   rg --files contexts/ai_sessions/{opencode,claude_code,codex,antigravity,second_mind}/ \\\n     -g '*.md' > \"$FILELIST\"\n   ```\n\n2. **再跑语义搜索**：\n   ```bash\n   semantic-search query \\\n     --file-list \"$FILELIST\" \\\n     --cache-dir .knowledge_cache \\\n     --query '你记得的内容' \\\n     --top-k 10 \\\n     --no-refresh\n   ```\n\n### 为什么旧列表会漏结果\n\n- 旧列表可能**遗漏了后来新增的会话文件**\n- 旧列表可能**包含了后来被删除或移动的文件**\n- 缓存里的向量不会被丢弃，但如果文件不在 `--file-list` 里，就不会参与检索\n\n### 缓存的正确用法\n\n- `--no-refresh` 已经正确：它只做**只读查找**，不会重建索引\n- 缓存本身是安全的，不会因为重新生成 file-list 而失效\n- 唯一需要注意的是：**file-list 必须反映当前归档的真实状态**\n\n一句话：**先用 `rg --files` 实时生成名单，再跑语义搜索**，这样既能利用已有向量缓存，又不会漏掉新文件。",
  "error": null,
  "n_skills": 10,
  "target_pos": 9,
  "prompt_tokens": 14094,
  "completion_tokens": 346,
  "sec": 6.477895975112915,
  "cost": 0.018482500000000002,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T02",
  "target": "compressor",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "把 `-format json` 紧跟在 `-monitor` 后面即可：\n\n```bash\n\"/Applications/Compressor.app/Contents/MacOS/Compressor\" \\\n  -monitor -format json \\\n  -batchid \"<batch_id>\" \\\n  -once\n```\n\n`-format json` 必须放在 `-monitor` 之后、其他参数之前，否则会报 `Invalid parameter: -format`。",
  "error": null,
  "n_skills": 10,
  "target_pos": 4,
  "prompt_tokens": 15502,
  "completion_tokens": 79,
  "sec": 2.936089038848877,
  "cost": 0.019575,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T03",
  "target": "deployment_github_actions_koyeb",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "在 GitHub Actions 的 `koyeb/action-git-deploy` 步骤里，必须显式指定下面两个参数：\n\n```yaml\n- name: Deploy\n  uses: koyeb/action-git-deploy@v1\n  with:\n    ...\n    git-builder: \"docker\"\n    git-docker-dockerfile: \"Dockerfile\"\n```\n\n- `git-builder: \"docker\"` —— 强制 Koyeb 使用 Docker 构建，而不是 buildpack 自动检测  \n- `git-docker-dockerfile: \"Dockerfile\"` —— 指明 Dockerfile 的相对路径（通常就是 `\"Dockerfile\"`）\n\n这两个参数同时出现，Koyeb 才会走容器构建路径。",
  "error": null,
  "n_skills": 10,
  "target_pos": 4,
  "prompt_tokens": 14399,
  "completion_tokens": 133,
  "sec": 8.668458938598633,
  "cost": 0.01833125,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T04",
  "target": "mac_universal_clipboard",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "```bash\ndefaults delete ~/Library/Preferences/com.apple.coreservices.useractivityd.plist ClipboardSharingEnabled 2>/dev/null; \\\ndefaults write ~/Library/Preferences/com.apple.coreservices.useractivityd.plist ClipboardSharingEnabled 1; \\\nkillall useractivityd 2>/dev/null; \\\nkillall sharingd 2>/dev/null; \\\nkillall pboard 2>/dev/null; \\\nsleep 2; \\\npgrep -fl 'useractivityd|sharingd|pboard'\n```",
  "error": null,
  "n_skills": 10,
  "target_pos": 3,
  "prompt_tokens": 13992,
  "completion_tokens": 104,
  "sec": 4.876236915588379,
  "cost": 0.01775,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T05",
  "target": "openreview",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**立刻恢复做法：**\n\n1. 找到并删除这个文件：\n   ```bash\n   rm tmp/openreview_token.json\n   ```\n\n2. 重新执行你的查询命令。\n\n**原因**：OpenReview 的 login rate limit 是「每时间窗口最多 3 次登录」。连续查询触发了这个限制，token 缓存文件 `tmp/openreview_token.json` 里的 token 已失效。删除它后，下次查询会重新登录一次（只触发 1 次），即可恢复。\n\n**注意**：不要连续快速重试多次登录，否则会再次触发 rate limit。删除 token 后执行一次查询即可。",
  "error": null,
  "n_skills": 10,
  "target_pos": 5,
  "prompt_tokens": 14475,
  "completion_tokens": 130,
  "sec": 4.560824871063232,
  "cost": 0.018418749999999998,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T06",
  "target": "workflow_bilibili_whisper_transcription",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "根据 `workflow_bilibili_whisper_transcription` 文档的明确建议：\n\n**单线程下载 + 每次间隔 2-3 秒** 是推荐做法。\n\n具体做法：\n- 每次下载完一个视频后，sleep 2-3 秒再请求下一个\n- 始终使用单线程（不要开多线程/多进程并发下载）\n- 加上合理的 User-Agent 和 Referer headers\n\n这个间隔是文档里给出的实测稳妥值，能显著降低触发 B 站 352 风控的概率。",
  "error": null,
  "n_skills": 10,
  "target_pos": 4,
  "prompt_tokens": 14570,
  "completion_tokens": 114,
  "sec": 5.057734251022339,
  "cost": 0.0184975,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T07",
  "target": "workflow_watchdog",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**默认 30 分钟（1800 秒）**。\n\n用 `ScheduleWakeup` 动作来设这个唤醒。醒来后检查任务状态：有新输出就继续等或再设一次；长时间无进展就 kill 掉（用 `TaskStop`）。",
  "error": null,
  "n_skills": 10,
  "target_pos": 0,
  "prompt_tokens": 13442,
  "completion_tokens": 60,
  "sec": 8.361317157745361,
  "cost": 0.016952500000000002,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T08",
  "target": "workflow_parallel_subagents",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不行**。\n\n`subagent_type` 必须是已在 `opencode.json` 或 `.opencode/agents/*.md` 里注册过的 **agent 名**，而不是 provider/model 字符串。直接写 `deepseek/deepseek-v4-pro` 会报 `Unknown agent type`。\n\n### 正确做法\n\n1. **先注册一个 agent**（推荐做法）：\n\n   在项目或用户配置里新建/编辑 `.opencode/agents/writer_deepseek.md`（或任意你想用的名字），内容示例：\n\n   ```markdown\n   ---\n   model: deepseek/deepseek-v4-pro\n   ---\n   ```\n\n   然后在 `task` 调用里用：\n\n   ```json\n   {\"subagent_type\": \"writer_deepseek\", ...}\n   ```\n\n2. **如果只是临时用**，也可以在 `opencode.json` 的 `agents` 段直接声明：\n\n   ```json\n   {\n     \"agents\": {\n       \"my_deepseek\": {\n         \"model\": \"deepseek/deepseek-v4-pro\"\n       }\n     }\n   }\n   ```\n\n   然后用 `{\"subagent_type\": \"my_deepseek\", ...}`。\n\n### 总结\n\n- `subagent_type` 只能是**已注册的 agent 名**。\n- 想用 `deepseek/deepseek-v4-pro`，必须先把它包成一个 agent，再用 agent 名去调用。",
  "error": null,
  "n_skills": 10,
  "target_pos": 8,
  "prompt_tokens": 17562,
  "completion_tokens": 292,
  "sec": 5.672255992889404,
  "cost": 0.0226825,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T09",
  "target": "codex_cli",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "在 Codex CLI 的 `config.toml`（或 profile 文件）里，用**裸字符串**写在最顶层：\n\n```toml\nweb_search = \"disabled\"\n```\n\n- **位置**：文件最外层（不要放在任何 `[section]` 下面）。\n- **值**：必须是字符串 `\"disabled\"`，**布尔值 `false` / `on` / `off` / `true` 都会被拒绝**，报错 `expected string only in 'web_search'`。\n- **生效范围**：该文件加载的 profile；如果 profile 里没写，会继承 base config 的值。\n\n写完后重新运行 `codex exec`，后端就不会再收到 `web_search` tool definition。",
  "error": null,
  "n_skills": 10,
  "target_pos": 1,
  "prompt_tokens": 14927,
  "completion_tokens": 150,
  "sec": 4.650004863739014,
  "cost": 0.019033750000000002,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T10",
  "target": "ai_session_profanity_rate",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不可以**把两种来源一起喂进同一次统计。\n\n**原因**：两种来源会把**同一批消息计数两次**，导致统计结果严重失真。\n\n**正确做法**：\n\n1. **优先使用原始会话库**（native stores），因为它保留了精确时间戳和更强的 per-message 模型归因。\n2. 如果需要跨工具的可移植性，先用 **AI Session Export** 生成归档，然后**只选 archive 作为输入**，不要同时混用 archive 和 native stores。\n\n**命令示例**（只用 archive）：\n\n```bash\nai-session-profanity-rate prepare \\\n  --source archive \\\n  --archive-dir ~/.local/share/ai-session-export \\\n  --timezone America/Los_Angeles \\\n  --classifier-profile ollama-cloud-glm-5.2-profanity-v2 \\\n  --output tmp/profanity_runs/<stamp>-7d\n```\n\n**关键规则**：不要把 archive 和 native stores 同时传给同一次 `prepare` 运行。",
  "error": null,
  "n_skills": 10,
  "target_pos": 6,
  "prompt_tokens": 14398,
  "completion_tokens": 214,
  "sec": 5.263658285140991,
  "cost": 0.0185325,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T11",
  "target": "apple_photos",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不能一次性发起 120 对的删除。**\n\n根据 skill 文档的硬性限制：\n\n- **每批上限 50 对**：`Delete Batch Bound` 明确规定“Limit each delete manifest to 50 pairs”。120 对必须至少拆成 3 批（例如 50 + 50 + 20）。\n- **授权不可复用**：每个批次都需要“fresh review and authorization”。之前拿到的 token 是 single-use、manifest-bound 的，用过即消耗，不能跨批次复用。\n- **不确定结果不能重试**：如果某批出现 `partial` 或 `outcome_unknown`，必须把该批视为失败，**停止并做只读对账**，不能自动重试或继续下一批。\n\n**正确做法**：\n1. 把 120 对拆成 3 个独立的 manifest（≤50 对/批）。\n2. 每批都走完整的流程：生成 plan → 人工审查 → 拿到新的 HMAC token → 交互式确认 → 执行。\n3. 任何一批出现非终态结果，立即停止，改用只读命令对账该批的 candidate local identifiers。\n\n简而言之：分 3 批、每批新授权、遇不确定立即停手。",
  "error": null,
  "n_skills": 10,
  "target_pos": 8,
  "prompt_tokens": 14989,
  "completion_tokens": 261,
  "sec": 3.5651140213012695,
  "cost": 0.01938875,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T12",
  "target": "asr",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不能直接处理。**\n\n根据 skill 文档的明确限制：\n\n> A single file longer than 7200 seconds is rejected before a model loads.\n\n你的录音 2 小时 10 分钟 ≈ **7800 秒**，超过 7200 秒（2 小时）的硬性上限，会在 **diarize 阶段加载模型前就被拒绝**，不会开始任何声学推理。\n\n### 切分能解决吗？\n\n**能。** 文档明确说明：\n\n> Splitting a file makes separate files; a speaker letter does not continue across slices.\n\n把音频切成多段（每段 ≤ 7200 秒）即可绕过这个限制。切分后：\n\n- 每段独立处理，speaker label（A/B/C...）不会跨文件延续\n- 后续可以用 `prepare-edit` + `finalize-edit` 把多段结果合并成一份 transcript\n\n### 建议做法\n\n1. 先用 `ffmpeg` 把原文件切成多段（例如每段 60–90 分钟）：\n   ```bash\n   ffmpeg -i input.wav -f segment -segment_time 5400 -c copy part_%03d.wav\n   ```\n2. 每段分别跑 `asr-skill run` 或 `diarize + align`\n3. 用 `prepare-edit` 把所有段的 rich.json 合并到一个 task_dir，再人工编辑 speaker 映射和 uncertain 行\n4. `finalize-edit` 输出最终 CSV\n\n这样既能绕过 7200 秒限制，又能得到一份完整的带 speaker 的 transcript。",
  "error": null,
  "n_skills": 10,
  "target_pos": 6,
  "prompt_tokens": 15033,
  "completion_tokens": 328,
  "sec": 4.730524063110352,
  "cost": 0.01961125,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T13",
  "target": "ct_education",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不行**。`ct-edu serve` 的 `--video-file` / `--rendered-video-file` 参数**只接受相对路径**，且该路径必须解析到已完成的工作区（workspace）内部。\n\n### 正确做法\n\n1. 把 MP4 复制（或硬链接）到已完成的工作区目录下，例如：\n   ```bash\n   cp /tmp/my-tour.mp4 /path/to/external/workspace/\n   ```\n\n2. 再用相对路径启动 serve：\n   ```bash\n   ct-edu serve --workspace /path/to/external/workspace \\\n                --video-file my-tour.mp4\n   ```\n\n### 为什么不能直接给绝对路径\n\n- CLI 会把相对路径解析到 workspace 内部，绝对路径、符号链接、父目录遍历都会被拒绝（触发 `E_VIDEO_FILE`）。\n- 运行时文件必须留在 workspace 里，`serve` 才能通过 loopback-only 的 HTTP server 提供 Range 请求和下载。\n\n### 额外提示\n\n- 两个视频参数可以单独使用；`--video-file` 对应“Interface demo”，`--rendered-video-file` 对应“Rendered film”。\n- 如果你想用 cinematic 渲染的最终 MP4，需要先显式批准后才能把它复制进 workspace，再用 `--rendered-video-file` 提供。",
  "error": null,
  "n_skills": 10,
  "target_pos": 0,
  "prompt_tokens": 19333,
  "completion_tokens": 269,
  "sec": 4.438063144683838,
  "cost": 0.02483875,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T14",
  "target": "dataforseo",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**40104 的含义**：账号已通过 Basic Auth，但尚未完成邮箱/电话验证（unverified account）。此时余额查询等只读接口仍可工作，但所有关键词相关端点（包括 sandbox）都会被封。\n\n**解决办法**（按优先级）：\n\n1. 打开 DataForSEO 控制台（https://app.dataforseo.com），用同一账号登录。\n2. 找到 **Verification / Account verification** 入口，完成邮箱或电话验证。\n3. 验证通过后，等待 1–2 分钟，再次运行关键词命令即可恢复。\n\n如果控制台里没有看到验证提示，尝试：\n- 检查注册邮箱是否有来自 DataForSEO 的验证邮件（含链接）。\n- 在 Account → Billing 或 Profile 里找 “Verify” 按钮。\n\n验证完成后，CLI 的 `keyword-suggestions`、`related-keywords` 等命令即可正常返回数据。",
  "error": null,
  "n_skills": 10,
  "target_pos": 9,
  "prompt_tokens": 13848,
  "completion_tokens": 188,
  "sec": 4.190927743911743,
  "cost": 0.01778,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T15",
  "target": "eink_diary",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "不需要自己改时间窗，直接用：\n\n```bash\neink-diary run --full-day\n```\n\n根据 skill 文档的说明：\n\n- **当前时间是 00:00–02:00 之间**，`--full-day` 会**自动解释为昨天完整一天**（`yesterday 00:00..today 00:00`）。\n- 也就是说，它会采集 **昨天 00:00 到今天 00:00** 的全部数据，生成昨天全天的视觉日记（8 幅图）。\n\n所以你现在执行 `eink-diary run --full-day` 就能得到昨天的完整 8 幅图，无需手动算时间窗。",
  "error": null,
  "n_skills": 10,
  "target_pos": 0,
  "prompt_tokens": 16275,
  "completion_tokens": 143,
  "sec": 6.823364019393921,
  "cost": 0.02070125,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T16",
  "target": "skill_google_maps_routing",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**是的，只能提供其中一个**：`--departure-time` 或 `--arrival-time` 必须二选一，不能同时提供。\n\n**关于接近当前时刻的 ISO 时间戳**：**会**被 Routes API 拒绝，错误信息通常是 `Timestamp must be set to a future time`。\n\n**正确做法**：\n\n- 如果你想用「当前交通状况」计算，**直接传 `now`**：\n  ```bash\n  scripts/google_maps_routing route \\\n    --origin \"San Francisco International Airport\" \\\n    --destination \"1600 Amphitheatre Parkway, Mountain View, CA\" \\\n    --departure-time now \\\n    --format json\n  ```\n\n- 不要自己构造接近「现在」的 ISO 时间戳（如 `2026-04-05T12:00:00-07:00`），API 会认为它「不够未来」而拒绝。\n\n- 如果你确实需要一个未来的到达时间（比如「下午 1 点前必须到」），再用 `--arrival-time` 传一个明确未来的时间戳。\n\n总结：**「现在出发」就用 `--departure-time now`**，这是 skill 里明确推荐的用法。",
  "error": null,
  "n_skills": 10,
  "target_pos": 4,
  "prompt_tokens": 14249,
  "completion_tokens": 243,
  "sec": 4.890660047531128,
  "cost": 0.01841875,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T17",
  "target": "hybrid_ai_video",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不能直接再发一次同样的创建请求。**\n\n根据 `hybrid_ai_video` skill 的明确规则：\n\n> **Ambiguous POST timeout or missing task ID**  \n> Retain the attempt and budget reservation; block automatic resubmission pending owner/provider reconciliation.\n\n当 POST 创建请求超时、没有返回 task ID 时，**必须保留这次尝试记录和预算预留**，**禁止自动重发**。原因：\n\n1. 任务可能已经在服务端创建，只是响应没回来；\n2. 重复提交会造成重复扣费或配额消耗；\n3. 无法区分是“已创建但未返回 ID”还是“根本没创建”。\n\n**正确做法**：把这次超时尝试记录下来（含时间、prompt、image 引用、预算预留），然后向任务所有者/凭证提供者报告，由人工或外部流程确认后再决定是否重试或恢复轮询。",
  "error": null,
  "n_skills": 10,
  "target_pos": 8,
  "prompt_tokens": 17784,
  "completion_tokens": 188,
  "sec": 4.5199408531188965,
  "cost": 0.0227,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T18",
  "target": "ecg_plot",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "直接拒绝出图。\n\n根据 `ecg_plot` skill 的核对规则：\n\n- 全部样本必须落在 0 到 30 秒内\n- 超出 6 条 5 秒窗口的点不会被裁掉，而是拒绝作图\n\n核对失败时仍会写完整性 JSON（`--integrity-output`），但不会生成 PNG。",
  "error": null,
  "n_skills": 10,
  "target_pos": 3,
  "prompt_tokens": 14892,
  "completion_tokens": 77,
  "sec": 1.8850300312042236,
  "cost": 0.0188075,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T19",
  "target": "koyeb",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不可以**。Koyeb CLI 的 `--tail` 是一个**布尔开关**（true/false），不是行数参数。\n\n### 正确做法\n\nKoyeb CLI **不支持**“只拉取最后 N 行并实时跟随”的组合命令。它的日志命令只有两种模式：\n\n1. **历史查询**（带时间范围）：\n   ```bash\n   koyeb services logs <service> --app <app> \\\n     --start-time 2026-01-01T00:00:00Z \\\n     --end-time 2026-01-01T00:10:00Z\n   ```\n\n2. **实时跟随**（从当前时刻开始，无行数限制）：\n   ```bash\n   koyeb services logs <service> --app <app> --tail\n   ```\n\n### 推荐的实时跟随用法\n\n```bash\npython3 scripts/koyeb_env.py -- \\\n  services logs <service-name> --app <app-name> --tail\n```\n\n- `--tail` 就是“从现在开始持续输出新日志”，没有行数参数。\n- 如果你只想看最近的少量历史 + 实时跟随，Koyeb CLI 本身不支持。你需要先用带时间范围的查询拿到最近日志，再另起一个 `--tail` 进程。\n\n### 总结\n\n| 需求 | Koyeb CLI 支持吗 | 做法 |\n|------|------------------|------|\n| 只看最后 100 行 | 不支持 | 用 `--start-time` 自己控制时间窗口 |\n| 实时跟随最新日志 | 支持 | `--tail`（布尔开关） |\n| 最后 100 行 + 实时跟随 | 不支持 | 需分两次执行 |\n\n所以 `--tail 100` 这种写法**无效**，`--tail` 后面不能跟数字。",
  "error": null,
  "n_skills": 10,
  "target_pos": 9,
  "prompt_tokens": 15514,
  "completion_tokens": 383,
  "sec": 5.554478168487549,
  "cost": 0.02035,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T20",
  "target": "narwal",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**原因**：`start`（全屋清扫）命令只在机器人**已停靠在基站**时才会被接受。如果它不在基站上，机器人会返回 `NOT_READY`（退出码 20）。\n\n**解决办法**：\n\n1. 先把机器人手动推回基站（或用 `narwal-local dock` 让它自己回去）。\n2. 确认它已停靠（通过 `narwal-local snapshot` 看 `battery_percent` 是否在上升，或用 `watch` 窗口观察）。\n3. 再执行全屋清扫：\n   ```bash\n   narwal-local start --yes\n   ```\n\n**注意**：`start` 命令不需要 `--dry-run`，但必须带 `--yes` 确认。",
  "error": null,
  "n_skills": 10,
  "target_pos": 2,
  "prompt_tokens": 17159,
  "completion_tokens": 161,
  "sec": 3.182000160217285,
  "cost": 0.02185125,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T21",
  "target": "skill_open_router_data_scraper",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "根据 `skill_open_router_data_scraper` 文档中的「已知陷阱」部分：\n\n**短 slug 不被 model-activity 接受**，返回空数组。\n\n**正确做法**：CLI 会自动解析 canonical_slug（末尾 8 位日期）；手动传时需用 canonical_slug。\n\n因此你应该传 **canonical_slug**（带日期后缀的完整标识），而不是短 slug。",
  "error": null,
  "n_skills": 10,
  "target_pos": 2,
  "prompt_tokens": 14283,
  "completion_tokens": 85,
  "sec": 2.455376148223877,
  "cost": 0.018066250000000002,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T22",
  "target": "skill_opencode_data",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "是的，默认会把子会话一起算进去。\n\n根据 `skill_opencode_data` 文档：\n\n> By default, CLI selectors expand descendants through the session `parent_id` chain. This keeps root sessions and subagent child sessions together. Use `--no-expand` only when the user explicitly wants the raw selector result.\n\n所以：\n\n- **默认行为**：`--selector title --prefix batch-` 会把匹配标题前缀的会话及其所有子会话都选中。\n- **只动被选中的那些本身**：加上 `--no-expand` 参数。\n\n示例：\n\n```bash\n.venv/bin/python -m opencode_skill plan \\\n  --selector title --prefix batch- \\\n  --no-expand\n```\n\n确认无误后再执行 `apply`：\n\n```bash\n.venv/bin/python -m opencode_skill apply \\\n  --selector title --prefix batch- \\\n  --no-expand \\\n  --dest ~/.local/share/opencode/opencode_archive.db \\\n  --confirm\n```",
  "error": null,
  "n_skills": 10,
  "target_pos": 2,
  "prompt_tokens": 14396,
  "completion_tokens": 203,
  "sec": 3.415328025817871,
  "cost": 0.0185025,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T23",
  "target": "video-scoring-audio",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**是的，测法有问题。**\n\n`-ac 1` 在 ffmpeg 里是**下混（downmix）**，不是简单取平均。它会把左右声道相加再除以一个系数（通常是 0.707 或 1.0，取决于具体实现）。当左右声道高度相关（比如单声道素材复制到双声道、或人声居中）时，下混会产生 **+3 dB** 的增益，导致原本峰值 -1.5 dBFS 的混音被测成 1.19（削波）。\n\n### 正确做法\n\n**不要用 `-ac 1` 做峰值测量**。保留双声道，用 `ebur128` 直接测真峰值（true peak）：\n\n```bash\nffmpeg -i mix.wav -af ebur128=peak=true -f null -\n```\n\n输出里看 `True peak:`，这个值才是正确的。\n\n### 交叉验证（三路独立验证）\n\n1. **ffmpeg ebur128**（上面那条命令）\n2. **Python numpy**（不经过 ffmpeg 下混）：\n   ```python\n   import numpy as np, subprocess\n   raw = subprocess.run(\n       [\"ffmpeg\",\"-v\",\"quiet\",\"-i\",\"mix.wav\",\"-f\",\"f32le\",\"-ar\",\"48000\",\"-\"],\n       stdout=subprocess.PIPE).stdout\n   x = np.frombuffer(raw, \"<f4\").reshape(-1, 2)   # 保持双声道\n   peak = float(np.max(np.abs(x)))                # 直接看最大绝对值\n   print(peak)  # 必须 < 1.0\n   ```\n3. **Audacity / iZotope / Adobe Audition** 的真峰值表（如果有这些工具）\n\n三者都应该给出一致的结论：**原始混音没有削波**。\n\n### 总结\n\n- `-ac 1` 下混 → 相关声道会 +3 dB → 误报削波\n- 保留双声道，用 `ebur128=peak=true` 或 numpy 直接测最大绝对值\n- 交叉验证用三路独立工具，确认一致才算可靠",
  "error": null,
  "n_skills": 10,
  "target_pos": 4,
  "prompt_tokens": 18158,
  "completion_tokens": 455,
  "sec": 7.060349225997925,
  "cost": 0.023835,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T24",
  "target": "speaker_notes",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**普通句子长度建议：12–20 词。**\n\n**硬性上限：22 词。** 超过 22 词的普通句子必须拆分。\n\n这是《Speaker Notes》技能里明确写好的「Hard gate」（硬门）。理由是：一句话要在一次呼吸里说完，超过 22 词就会让听众喘不过气，也容易把多个意思塞进一句，造成信息过载。\n\n**例外情况**：  \n- 引用原文、法律条款、数据列表等「必须保持原样」的句子可以例外，但要尽量少用。  \n- 口播稿里真正需要长句的地方，建议用「停顿 + 下一句」的方式拆解，而不是写成一个超长句。\n\n**检查方法**：写完后逐句数词，超过 22 词的立刻拆。",
  "error": null,
  "n_skills": 10,
  "target_pos": 8,
  "prompt_tokens": 15980,
  "completion_tokens": 177,
  "sec": 4.040771245956421,
  "cost": 0.020417499999999998,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T25",
  "target": "skill_tavily",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不可以**。`--stdout` 不是一个受支持的参数。\n\n根据 skill 文档：\n\n- **默认行为**：`search` 命令总是把完整结果写入文件（`tmp/tavily/` 或 `TAVILY_CLI_OUTPUT_DIR`），**stdout 只返回一个轻量级的 status JSON**，包含 `command`、`status`、`output_path` 等字段。\n- **显式 `--stdout`**：会被识别为 **未识别参数**，触发 **usage error，退出码 2**，**不会发起任何网络请求**。\n\n如果你想在控制台看到完整结果，正确做法是：\n1. 先运行 search（它会写文件）\n2. 用 Python 打开 status JSON 里返回的 `output_path`，再打印你需要的字段。\n\n文档明确指出：**Passing `--stdout` to `search` or `extract` is an unrecognized-argument usage error (exit 2) and makes no network request.**",
  "error": null,
  "n_skills": 10,
  "target_pos": 0,
  "prompt_tokens": 17962,
  "completion_tokens": 196,
  "sec": 4.070481061935425,
  "cost": 0.0229425,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T26",
  "target": "zun_music",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不可以**。在 4/4 的八小节里，**强拍上只能改动一处**，而且只能在结尾的终止式。\n\n### 合规标准（来自 `skeleton_allow` 的明确限制）\n\n1. **上限**：**每四小节最多一处**，八小节里最多一处。\n2. **位置**：只能在**结尾的终止式**（cadence）。\n3. **性质**：必须是**故意偏离**，且必须在 `skeleton_allow` 列表里显式声明。\n\n### 为什么不能多改\n\n`check()` 会做 **skeleton check**：在每个强拍（4/4 的第 1 拍和第 3 拍）上，编曲的音高类（pitch class）必须与**未改动的原始转录**完全一致。只有在 `skeleton_allow` 里列出的位置才允许例外。\n\n### 合规示例\n\n```python\nskeleton_allow = [\n    (7, 1, \"C\")   # 第 8 小节第 1 拍，允许从原旋律的 D 改为 C（小调终止式）\n]\n```\n\n### 不合规示例\n\n- 第 2 小节第 1 拍改动\n- 第 4 小节第 3 拍改动\n- 第 6 小节第 1 拍改动\n- 同一首曲子改动两处\n\n这些都会让 `check()` 失败，测试不通过。\n\n### 实际做法\n\n- **保持强拍骨架不变**：把 ZUN 风格的改动（3-3-2 切分、16 分音符填充、装饰音等）放在**弱拍或次强拍**之间。\n- **只在终止式做一次例外**：如果想在结尾用和声小调的 V7→i，就把这个改动写进 `skeleton_allow`。\n- **测试命令**：`python scripts/render_example.py <tune> --midi-only` 会运行 `check()`，不通过就直接 non-zero 退出。\n\n一句话：**强拍骨架是神圣的**，最多只允许在结尾改一次，而且必须显式声明。",
  "error": null,
  "n_skills": 10,
  "target_pos": 2,
  "prompt_tokens": 15675,
  "completion_tokens": 484,
  "sec": 7.509057998657227,
  "cost": 0.02080375,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T27",
  "target": "skill_image_generation",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**Gemini 模型下 `--quality` 参数不会生效。**\n\n根据 skill 文档的说明：\n\n> Gemini accepts the flag but ignores it because quality tiers are provider-specific.\n\nGemini 本身没有 quality tier（`low`/`medium`/`high`），它接受这个 flag 但直接忽略。质量控制只对 OpenAI 的 GPT Image 模型（`gpt-image-2`、`gpt-image-2.5-*`）有效。\n\n如果你想获得更高质量的 Gemini 输出，应该改用 `--model gemini-pro`（或 `gemini-3-pro-image-preview`），而不是加 `--quality high`。",
  "error": null,
  "n_skills": 10,
  "target_pos": 1,
  "prompt_tokens": 14878,
  "completion_tokens": 128,
  "sec": 2.6009888648986816,
  "cost": 0.0189175,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T28",
  "target": "skill_process_launcher",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "要让两条同一时刻运行的定时任务同时保留，或用新的替换旧的，关键在于 `confirm_conflict` 参数。\n\n### 同时保留两条任务\n\n在 `POST /run` 或 `PATCH /scheduled/{job_id}` 请求中，加入：\n\n```json\n{\n  \"confirm_conflict\": true\n}\n```\n\n- **效果**：请求返回 HTTP 409 时不会被拒绝，而是创建/更新任务，与已存在的冲突任务并存。\n- **适用场景**：你确实需要两条任务在同一时刻执行（例如不同命令、不同 label）。\n\n### 用新的替换掉旧的\n\n1. 先取消旧任务：\n   ```bash\n   curl -sf -X POST http://127.0.0.1:7997/scheduled/{旧job_id}/cancel\n   ```\n\n2. 再提交新任务（此时不会冲突）。\n\n或者，如果新任务与旧任务是同一 label 或同一意图，直接用 `PATCH /scheduled/{旧job_id}` 更新 `run_at`（同样需要带 `confirm_conflict: true` 才能覆盖同一时刻的冲突）。\n\n### 快速检查当前冲突\n\n```bash\ncurl -sf 'http://127.0.0.1:7997/scheduled?status=pending&include_terminated=false'\n```\n\n查看哪些任务处于 pending 且 `run_at` 相同，即可判断冲突来源。",
  "error": null,
  "n_skills": 10,
  "target_pos": 7,
  "prompt_tokens": 16859,
  "completion_tokens": 288,
  "sec": 4.784915924072266,
  "cost": 0.021793749999999997,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T29",
  "target": "skill_resend_email",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**不可以**直接把 `--after/--before` 设成 ISO 时间。\n\n这些参数是 **Resend 的分页游标（cursor）**，不是时间戳过滤器。传入 ISO 时间戳会返回校验错误。\n\n### 正确的按日期筛选流程\n\n1. 先导出最近一批邮件（用 `--limit` 控制数量）：\n   ```bash\n   .venv/bin/python -m resend_email_skill.cli received export-all-md \\\n     --limit 100 \\\n     --output-dir data/received/markdown \\\n     --format json\n   ```\n\n2. 导出完成后，在本地 Markdown 文件里按 `created_at` frontmatter 过滤日期：\n   ```bash\n   # 例如筛选最近两天\n   find data/received/markdown -name \"*.md\" -newermt \"2 days ago\" -exec cat {} +\n   ```\n   或者用 Python 脚本读取 frontmatter 中的 `created_at` 字段做筛选。\n\n3. 如果需要更精确的日期范围，逐步增加 `--limit` 或多次调用 `export-all-md` 直到覆盖目标时间窗口。\n\n**注意**：`--after` / `--before` 只能接收 Resend 返回的 UUID 游标，用于翻页，不是时间过滤。",
  "error": null,
  "n_skills": 10,
  "target_pos": 6,
  "prompt_tokens": 15012,
  "completion_tokens": 254,
  "sec": 3.1602041721343994,
  "cost": 0.0194,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 },
 {
  "id": "T30",
  "target": "skill_typefully_twitter",
  "model": "grok-4-fast",
  "condition": "full_10",
  "answer": "**默认用长帖模式（long-post）。**\n\n**理由**：Typefully 的 `post count`、`post draft`、`post create` 在 `TYPEFULLY_TWITTER_DEFAULT_LONG_POST=true` 时默认走长帖校验；即使没设该 env，只要内容是分析性、研究性或需要完整表达的文章，都应优先用长帖（`--long-post`）。\n\n**切到普通 280 字模式**：显式加 `--no-long-post` 参数即可。",
  "error": null,
  "n_skills": 10,
  "target_pos": 7,
  "prompt_tokens": 15346,
  "completion_tokens": 105,
  "sec": 2.7836110591888428,
  "cost": 0.019445,
  "web_tools": [],
  "n_calls": 1,
  "finish_reason": "stop"
 }
]