掃描方法與成本控制

這份記錄「怎麼跑掃描、花多少、踩過什麼坑」,讓後續批次不必重新摸索。

§1

工具

Claude Code 官方 plugin claude-security@claude-plugins-official v0.10.2.3。

啟動方式是 /claude-security 開選單,選 Scan codebase。 它實際跑的是 claude-security:scan workflow,會派出一群 subagent 分工。

§2

掃描設定

第一批用的參數:

項目 值
掃描根目錄 ~/Projects/Jedicogy/module/jedi-python-package
scope jedi-common,jedi-iam(453 個 tracked 檔)
effort medium
focus attack-surface(tests/dist/docs 只當背景參考,不當稽核目標)
§3

effort 等級與成本

這是踩坑重點。四個等級的差別全在研究階段派多少 agent,驗證面板固定三人。

等級 研究規模 實際 agent 數(約)
low 單一 researcher 掃完整個 scope 5 到 6
medium 每個「元件 × 弱點類別」各一位,再加一輪 breadth sweep 40 以上
high 每格兩位、兩輪 sweep、更細的元件切分 80 以上
max high 再加紅隊逐條反駁 更多

medium 為什麼這麼貴:它先把套件切成 N 個元件,然後展開成矩陣派工。 每個 agent 都從零開始讀檔、沒有共用 context,所以同一支 ldap_adapter.py 被至少 8 個 researcher 各讀了一遍——這也是為什麼 32 筆原始發現去重後只剩 19 條。 重複的發現就是重複的 token。

再加上 researcher 與 verifier 的設定是 effort: xhigh、model: inherit, 等於一百多次前沿模型的高強度推理。

§4

實際發生的事(2026-09-05)

  1. 第一輪 medium:跑約 40 分鐘,派出 40 個 agent,研究階段收到 32 筆候選發現。 因為成本明顯失控,在面板驗證開始前手動停掉。 這 32 筆就是 batch1 清單的來源,屬未驗證的原始宣稱。

  2. 第二輪 low(只掃 jedi-iam):想驗證 low 是否真的便宜。 確實只派了 2 個 researcher(一個主掃、一個 secrets pass), 但跑了 2 小時 15 分後全部 agent 因 session 額度用盡而失敗,零產出。 工具重試了 4 次都撞同一道牆。

§5

給下一批的建議

  • 用 low,除非有特別理由。從第一輪的結果看,medium 多出來的 30 幾個 agent 主要產出的是重複發現,不是新發現。
  • 一次只掃一支套件。453 個檔已經讓 low 跑了兩小時以上。
  • 確認額度充足再開始。中途沒額度時,正式報告與 stamp 一定拿不到, 但不等於整輪報廢——同一個 session 內可以用 resumeFromRunId 續跑, 續不回去也還能從磁碟撈回已完成的部分。做法見下段。
  • 主 session 可以先切便宜模型再啟動 錯,2026-09-06 推翻:subagent 確實繼承主 session 模型, 但便宜模型的 context 上限跟著一起繼承。40 檔以上的範圍用 Sonnet 5(200K)一定被砍,見下段。
§6

撈中斷結果的方法

掃描中止時,正式報告與 stamp 不會產生,但已經做完的工作都還在。處理順序是「先續跑、續不回去才撈檔」。

一、先試續跑:resumeFromRunId

Workflow 工具吃 resumeFromRunId 參數。傳入前一輪的 run ID, 已完成且 (prompt, opts) 未變的 agent 呼叫會回傳快取結果、不重跑, 只有失敗的與沒跑到的才重新派。這正對症我們的死法——被砍的通常是最後階段的 verifier, 前面數小時的研究階段是完整的。

三個限制:

  1. 續跑前要先 TaskStop 停掉原本那輪。
  2. 只能在同一個 session 內續跑。
  3. session 關掉或 /clear 之後就再也接不回去。

所以 runner 的鐵律是:撞到額度上限時停下回報,但那個視窗不要關、不要 /clear, 等額度窗口重置回同一視窗續跑。這條要寫進掃描卡,否則 runner 的直覺就是收工關窗。

看即時進度用 Claude Code 內建的 /workflows(在跑掃描的那個 session 輸入), 啟動後就該跑一次把 run ID 記下來。 順帶澄清一個常見誤會:我們講的「面板」是 panel(三人驗證投票機制),不是儀表板, 找不到「控制面板」是正常的。

二、續不回去才撈檔案

run 目錄下不只有 journal,每個 agent 各有一份自己的逐字工作紀錄,即時寫入,被砍的 agent 也有:

~/.claude/projects/<專案 slug>/<session-id>/subagents/workflows/<run-id>/
├── journal.jsonl                    # 已完成 agent 的結構化回傳
├── agent-<id>.jsonl                 # 每個 agent 的逐字流水帳(含被砍的)
└── agent-<id>.meta.json

兩者用途不同:

  • journal.jsonl —— 乾淨結果,直接可讀。每行一個 JSON, {"type":"result", "result":{"findings":[...]}} 就是該 agent 的發現。 用 Python 逐行解析、按 file + title 去重即可還原清單。中斷後預設撈這個。
  • agent-*.jsonl —— 流水帳,要派人讀完萃取,成本高。 只在「研究員明顯讀了關鍵檔卻被砍、journal 裡沒有它」時才值得撈。 規模參考:2026-09-06 S6 那輪的目錄有 59 個檔,單一 agent 的 jsonl 最大 1.1MB。 本專案至今沒做過,成本未實測。

撈出來的一律標「未經面板驗證」。但人工開檔核對過的照樣能拿去開修正卡—— FR-077 R1(42 檔)面板全滅、正式 findings 是空陣列,runner 從 journal 撈回 8 條候選、 去重成 7 條、逐條核對全屬實,四張修正卡就是照那批開的。 不能宣稱的只有「這個範圍掃過了」。

第一輪的 run id 是 wf_442f835c-ddf。

§7

2026-09-06 追記:S4~S7 首輪全滅,根因是 context 不是 effort

四棒(43/49/47/74 檔)用 Sonnet 5 跑 low,結果:S4 派 7 研究員全砍、S5 派 7 唯一交卷是 343K 硬撐的零發現、 S6 派 6 全砍、S7 第 4 次跑完但 8 條越界重複 S1。

死法一致:plugin 把研究員定義成 model: inherit、effort: xhigh,prompt 要求「讀完每個檔、追每個 caller、 不信任何註解」。Sonnet 5 上限 200K,讀到 20 幾萬 token 時每一步思考變慢,超過 workflow 的 180 秒無 tool call 即判 stalled 門檻(scan.js 的 18e4 毫秒)就被砍掉,從零重派,永遠讀不完。 一顆被砍的 S4 研究員:411 回合、產出 91 萬 token、讀 30 個不同檔加 200 多次 grep,然後死。

effort 等級管不到這層。low/medium/high 控制的是 workflow 派幾個研究員;研究員自己的推理深度 寫死在 agents/scan-researcher.md,改了會被 plugin 更新蓋掉。

解法:runner 主 session 切 Opus 5 (1M context),其他全不動。1M 在 20 幾萬 token 時思考速度正常, 不會觸發 stall。代價是每棒成本約 Sonnet 的三到五倍,但至少有產出。

不要切小範圍:S7 的研究員為了追脈絡讀了 S1 整個 adapter 目錄、主專案的 route、還有 repo 裡既有的 四份掃描報告,然後把 S1 已開卡的 8 條原樣回報成「新發現」。切小不會減少它讀的量。

範圍相鄰時要在卡片明寫「不要讀這些已掃過的路徑」——面板只驗「發現是否成立」, 不驗「發現是否在指派範圍內」,越界要靠人工比對才抓得到。