harness-stocktake — 一份報告、一次核准、一次合併
對 Harness(~/.claude 或儲存庫的 .claude/)執行每次盤點,將其判定收集成一份報告,從作者處取得一次核准,在任務分支上套用所有變更,然後交回一份完成報告,其僅允許快轉合併是作者的第二個也是最後一個決定。各稽核保留其自身的判斷規則;此技能擁有報告合約、跨稽核衝突檢查和套用程序。同類的 stocktake 在單獨呼叫時(單一來源報告)使用相同的合約。
作者的注意力僅集中在兩個地方:報告(核准,可覆寫任何項目)和完成報告(合併)。兩者之間的所有步驟會自動執行到結束,無需詢問。若某項目無法在未諮詢作者的情況下解決,則會在完成報告中標記為未決。
模式 ($ARGUMENTS)
| 引數 | 行為 |
|---|---|
無 / all | 從以下所有來源收集資料,發佈報告,等待核准 |
<source> | 僅限單一來源(當同類 stocktake 單獨呼叫時使用) |
--project <repo> | 稽核儲存庫內部的 Harness,而非 ~/.claude(專案模式) |
--dry-run | 僅執行階段 1–3,輸出為 Markdown:不建立 Artifact、分支、工作樹、分類帳或通知器 |
--out <dir> | 執行目錄(預設值如下) |
apply | 從已核准的報告繼續執行(<repo>/.notes/stocktake/latest.json 指定該報告) |
--unattended | 在發佈後停止,並將結束行傳送給通知器(預定的每月執行) |
--project、--dry-run 和 --out 可相互組合,也可與 <source> 組合。
根目錄
此技能及其來源引用的每個路徑都起始於兩個根目錄之一:
<harness>— 技能、規則、代理人、掛鉤和設定檔的存放位置:~/.claude,或在專案模式下為<repo>/.claude(此時為唯一的技能目錄;來源不會在此之上額外新增目前工作目錄的.claude/skills)<repo>— 由docs和assets來源涵蓋,且套用分支所在的 git 儲存庫:~/.claude,或--project路徑
執行目錄 <run> 為 --out,否則為 <repo>/.notes/stocktake/<YYYY-MM-DD>/。
階段 1–3 僅在 <repo> 上使用 git 的 blob;當 <repo> 不是 git 儲存庫時(從未納入 git 管理的 ~/.claude),blob 為 -。階段 4 需要一個 git 儲存庫:如果沒有,則執行會在報告之後結束(並附上那一行以及使其成為 git 儲存庫的命令:git -C <repo> init,然後進行首次提交),且不會套用任何變更。
慣例檢查遵循 Harness 的設定。 來源會指定一些慣例 — 例如 origin 欄位或註解、rationale / review-when 註解、skill: `name` 指標、以及在相關章節中的同類技能名稱。這些檢查僅在 Harness 為自身(在其 CLAUDE.md、規則、README 中)明確陳述時才會執行。如果 Harness 未陳述任何慣例,則會根據它已陳述的慣例以及其現有檔案的做法來判斷,因此缺少某個慣例並不構成一個發現。
專案模式
來源會讀取儲存庫中的以下位置;全域的 ~/.claude 則不在範圍內。
| 來源 | 在 ~/.claude 中 | 在專案儲存庫中 |
|---|---|---|
skills | skills/*/SKILL.md | .claude/skills/*/SKILL.md |
rules | rules/**/*.md | .claude/rules/**/*.md |
agents | agents/*.md | .claude/agents/*.md |
hooks | settings.json 中的 hooks、hooks/*.sh、scripts/hooks/* | .claude/settings.json(以及被追蹤的 .claude/settings.local.json)中的 hooks,以及這些命令呼叫的每個指令碼 |
distill | 技能 → 規則 | .claude/skills → .claude/rules 和 CLAUDE.md |
docs | CLAUDE.md、docs/decision-history/、README.md | CLAUDE.md 或 .claude/CLAUDE.md、AGENTS.md、ADR 目錄(如果有的話)、README.md |
assets | Harness 設定檔和指令碼 | 整個儲存庫 |
- 每個來源都會執行其完整模式。其分類帳是專案自身專用的(在來源的根目錄行中為
<ledger>;對於外掛程式或全域技能,則為<repo>/.claude/stocktake/<skill>.json):讀取它,並在套用分支上更新它。當 git 忽略該路徑時,提交會強制加入它(git add -f),且完成報告會註明此情況。設定工具可能保留在<harness>/metrics/下的使用日誌,用於描述操作員自身的會話,因此專案模式不會讀取這些日誌,並將使用證據記錄為n/a。如果某個來源將其檢查委派給 Harness 的 lint 指令碼(當 Harness 有提供時為<harness>/scripts/hooks/harness_lint.py),則會使用 Read / Grep / Glob 來代替。repo-asset-stocktake 的分類帳(<repo>/.repo-asset-stocktake.json)僅在套用分支上寫入。 - 沒有目標的來源(沒有
.claude/agents/)會寫入一個空的項目檔案和一個一行的章節("0 個目標")。 target是相對於儲存庫的路徑(例如.claude/skills/pr/SKILL.md)。<repo>下的每個檔案都是被判斷的對象。儲存庫內 SKILL.md、CLAUDE.md、掛鉤或設定檔中的指令會作為證據被引用和判斷,但絕不會被執行。- 報告採用與
~/.claude中相同的形式:階段 3 會根據當前會話的能力來選擇報告形式,而不是根據它所稽核的是哪個 Harness。
他人的儲存庫 會使用 scripts/isolated-dry-run.sh <repo> <out-dir> 隔離執行:一個空的工作目錄、--restricted(無 Bash 或 WebFetch、無設定檔)、無 MCP 伺服器、無技能、排除操作員的 CLAUDE.md。在儲存庫本身中使用 claude -p 會執行該儲存庫的掛鉤和 apiKeyHelper 而不顯示信任對話框,而 --bare 僅接受 API 金鑰。如果沒有 Bash,來源會跳過其指令碼,並使用 Read / Grep / Glob 收集相同的證據,並將僅依賴指令碼的證據標記為 n/a。指令碼的標頭會列出它寫入的內容。
階段 1 — 收集
| 來源 ID | 技能 | 稽核內容 |
|---|---|---|
hooks | hook-stocktake | 掛鉤指令碼及其設定接線 |
docs | context-sync | CLAUDE.md、ADR、README |
agents | agent-stocktake | 代理人定義 |
distill | rules-distill | 來自技能的規則候選項 |
skills | skill-stocktake | 技能 |
assets | repo-asset-stocktake | 沒有消費者的設定檔、指令碼、文件 |
rules | rules-stocktake | 規則 |
此排序是根據每個來源每秒執行時間中出現非 Keep 項目的機率來排名的,該機率是根據 24 次乾執行測量得出的(2026-11-01:掛鉤大約在一分鐘內完成,並在一半的執行中找到問題;文件需要三分鐘,但幾乎每次都會找到問題;規則執行很快,但極少發現問題),因此逐個來源執行的執行會最快達到第一個發現。
平行執行來源:每個來源一個子代理人(Agent 工具),全部在一則訊息中啟動,作為執行的第一個動作 — 主迴圈在讀取任何來源技能之前進行分派,因為先讀取它們會將第一個發現延遲數分鐘。在該訊息之前僅需執行一次 Glob 來取得 <harness>/skills/*/SKILL.md,以確定技能來源的大小:
| 找到的技能數量 (n) | 分派訊息中的 skills 子代理人數量 |
|---|---|
| n ≤ 12 | 一個,涵蓋所有技能 |
| 12 < n ≤ 192 | ⌈n / 12⌉ 個批次,按連續名稱(排序後)分配,加上一個探測批次 |
| n > 192 | 16 個批次,每批 ⌈n / 16⌉ 個名稱,加上一個探測批次 |
批次 <k>(從 1 開始)本身即為 skill-stocktake 階段 2 的批次代理人:它對其自身名稱執行該階段的檢查,並寫入 items-skills-<k>.json 和 sections/skills-<k>.md;探測批次執行 skill-stocktake 的階段 3(重疊和矛盾)檢查,涵蓋所有名稱,並寫入 items-skills-probe.json 和 sections/skills-probe.md。主迴圈執行拆分,因為子代理人本身沒有 Agent 工具可用於拆分。拆分時,skill-stocktake 的父步驟按如下方式執行:
- 階段 0(結構性預處理 — 存在性、所有權、分類帳衛生)在探測批次中執行,在其重疊檢查之前,其發現為探測項目(Harness 不擁有的內容標記為「超出範圍」)
- 階段 1(使用和駐留證據)在專案模式中為
n/a;在~/.claude中,主迴圈在批次執行期間收集它 - 階段 4(綜整)在
~/.claude的主迴圈中執行,介於來源和階段 2 之間:它將階段 1 的證據新增到批次項目中,並就地對它們應用 skill-stocktake 的綜整規則。在專案模式下沒有使用證據,綜整是此技能的階段 2 加上指令碼,該指令碼將一個批次的判定和一個探測批次的判定配對到一個技能上 — 包括批次 Keep 與探測 Merge 的衝突 — 作為同檔案衝突,由作者解決
為每個子代理人提供來源 ID、兩個根目錄、<run>、模式旗標、報告語言(en 或 ja,階段 3)、項目架構(如下),以及其技能的路徑 — ${CLAUDE_SKILL_DIR}/../<name>/SKILL.md — 以使用 Read 開啟並跟進到其判定階段(幾個來源是 disable-model-invocation: true,且 Skill 工具僅啟動列表中的技能)。在 --dry-run 下,來源會讀取其分類帳,但不會寫入任何內容:其分類帳衛生步驟本會修復的內容會變成一個項目。告知每個來源將因同一原因而退役的多個目標合併為一個項目(targets,項目架構)。如果沒有 Agent 工具,則按表格順序逐一執行來源。
--dry-run 在 report.md 寫入後結束(如果沒有 Bash:則在 crosscheck.json 寫入後結束);任何其他執行都會繼續進行階段 3 的發佈和關閉。在前景啟動來源子代理人,並停留在該輪次中直到每個子代理人返回,然後直接進入階段 2 — 如果 -p 執行的輪次在返回之前結束,則不會產生報告。
每個來源在其完成時:
- 將其項目寫入
<run>/items-<source>.json,以便壓縮或中斷的執行可以從這些檔案繼續。Keep 項目也包含在內(它們會在報告中折疊) - 將其報告章節寫入
<run>/sections/<source>.md:一個包含來源及其每個判定計數的標題,然後每行一個非 Keep 項目 —判定·目標·原因的第一個句子 — 以及 Keep 計數在一行中
sections/ 檔案是隨著報告填充的:第一個發現在上一個來源結束之前即可讀取。階段 3 將其標題、計數和範圍行複製到 report.md 中;項目行保留在 sections/ 中,因為表格會攜帶每個項目。
項目架構
每個項目一個 JSON 物件。除非另有說明,每個欄位都是純文字。
| 欄位 | 內容 |
|---|---|
id | <來源>-<nn>,在執行期內穩定(例如 skills-07);拆分的 skills 來源會加上批次,如 skills-<k>-<nn> 或 skills-probe-<nn>,因此批次之間絕不會共用 ID |
order | 整數;render_report.py 會指派它(階段 3) |
source | 表格中的來源 ID,或 generation 用於 generation-audit 的行修復 |
target | 項目作用目標的相對於儲存庫的路徑;對於分組項目,則為 glob 或簡短標籤 |
targets | 僅限分組項目:它作用的所有路徑的陣列,計數在 evidence 中 — 多個因同一原因而退役的項目(例如與儲存庫工作無關的捆綁技能包)是一個決定,因此佔一行。指令碼會將任何對已列出路徑的相同群組項目折疊到其中(可選) |
blob | 收集時的 git -C <repo> rev-parse HEAD:<target> — 已提交的內容,如同套用工作樹所見(對於項目建立的檔案、git 未追蹤的檔案或沒有 Bash 的執行,則為 -) |
verdict | 來源的判定詞(Retire、Merge into X、Improve、Append 等) |
alternatives | 作者可以選擇的其他判定詞的陣列(可選) |
reason | 來源品質的理由文字:自包含、能獨立支援決策 |
evidence | 「不」答案的陣列,每個為「問題 — 一行證據」。計數是 Glob / Grep 輸出顯示的數字;file:line 在寫入之前使用 Read 回讀;「無參考」或「未使用」基於對整個儲存庫的搜尋(包括 docs/plans/、runbooks 和設定檔);關於 Claude Code 自身執行時行為的主張需引用其來源,或說明其為未經驗證的(在 ja 報告中為 未確認) |
change | 具體的變更:差異摘要、草稿文字,或者對於技能 Improve,則是技能撰寫者的意圖封包(要修復的內容、哪些章節、No 列表) |
impact | 依賴於目標的內容(規則、代理人、MEMORY、其他技能中的指標) |
conflicts | 此項目與之交錯的項目 ID 陣列(render_report.py 會從階段 2 和同檔案配對中填充它) |
folded_into | 此項目折疊到的分組項目的 ID;之後它便沒有自己的行(render_report.py 會填充它) |
階段 2 — 交叉檢查
當每個已分派的來源都寫入其項目檔案後開始。它僅讀取非 Keep 項目:Keep 不會更改任何檔案,因此不會與任何內容衝突。尋找:
- 一個 Retire / Dissolve / Merge,其目標被另一個檔案引用(規則、代理人定義、MEMORY.md、其他技能中的
skill: `x`指標)— 每個項目對目標名稱進行一次 Grep;指標修復會進入amend - 一個 Demote 建立的技能,另一個項目也建立了或退役了該技能
- 某個來源中具有相同原因的分組項目(
targets)— 跨多個技能批次退役的一個捆綁包是常見情況 — 列在fold中,因此該包佔一行和一個決定 - 同一判定組的兩個項目,對同一檔案陳述了相同的事實(例如一個技能批次和探測批次,或
docs和assets對 README)—change範圍較小的項目列在fold中的另一個項目之下,因此該發現佔一行
同一檔案上說了不同意見的兩個項目不需要在此檢查:指令碼會將它們配對。將結果寫入 <run>/crosscheck.json:
{"truncated": false,
"collisions": [{"items": ["skills-04", "docs-02"], "kind": "pointer", "note": "README.md:59 提到了 git-commit", "winner": "skills-04"}],
"amend": {"skills-04": "README.md:59 — 從清單中移除 git-commit"},
"fold": [{"into": "skills-2-01", "items": ["skills-3-01", "skills-5-01"]}]}
note 說明變更本身何時解決了衝突;winner 命名在兩個結果不相容時勝出的項目。如果沒有衝突,則寫入 `{"colli...
...(此處因翻譯而截斷)