work-exhaust について
work-exhaust は、結果の規模が不明な課題を網羅的に調査するエージェントスキルです。並列検索を複数回行い、連続する空のラウンド数で収束を判定します。ソースの再確認や決定的な検査で結果を検証し、進捗を記録して再開できます。
主な機能
- 01並列の調査ラウンドを反復
- 02各ラウンドで複数の検索観点を使用
- 03発見項目のソースを再確認
- 04決定的な検査で網羅性を確認
- 05状態を記録して再開
活用シーン
- 01ある動作に関わるすべてのコード経路を探す
- 02コードベース全体のエッジケースを発見
- 03移行が必要なファイルや連携箇所を特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ djalmajr/ skills --skill work-exhaustインストール後、Claude Code で/work-exhaustを入力して読み込まれたか確認します。
詳細
/work-exhaust — 飽和状態になるまで徹底的に探索
このスキルは、結果数が事前にわからない探索の問題に対して、見つからなくなるまでループする品質パターンを実装します。
決定論的な実行モデル(3つの形、構造化出力のルール、null の処理、ジャーナリング)は
../work/references/workflow-mode.md に記載されています。
このスキルが防ぐべき失敗モードについては
../work/references/anti-error-lessons.md
を参照してください(以下では L1〜L8 として参照します)。このスキルはそのモデルにおける標準のLOOPです。まずこれらを読んでください。このファイルでは、ループを単独で実行するために必要な内容のみをインラインで記載しています。
オーケストレーションの形(決定論的)
このスキルはLOOPの形(見つからなくなるまで繰り返す)で、各ラウンド内に他の2つの形を組み込んでいます。
- ラウンド内 → PARALLEL によるファンアウト。 各ラウンドのファインダーは、独立したサブエージェント群として一斉に起動されます(読み取り専用 → worktree は不要)。結果は
.filter(Boolean)で収集します。停止した、またはエラーになったファインダーは何も返しませんが、空のラウンドとして扱うことはありません(「null の処理」と「誤った DRY 判定の防止」を参照)。各ファインダーは../work-sweep/SKILL.mdに基づく異なる観点を担当します。 - ラウンド終了 → 真の BARRIER。
seen[]との重複排除と dry カウンターの判定には、そのラウンドのファインダーすべてからの戻り値が必要です。すべての結果がそろうまでは「新規項目が 0 件」と判断できません。ワークフローモード §1 にあるとおり、これは(結果をマージするために)実際にアイテム間の依存関係があることから、バリアが正当化される唯一の箇所です。重複排除自体は単純なセット演算で行い、エージェント呼び出しは決して行いません。 - ラウンド間 → LOOP。 終了条件(条件を満たす dry ラウンドが K 回連続すること)を満たすまで繰り返します。
seen[]、anglesUsed[]、dryCounterはラウンド間、および再開後も保持されます(「ジャーナリング」を参照)。
簡易チェック(ワークフローモード §1):重複排除とマージが真のバリアなのは、ラウンド全体を使って「新規か、既知か」を判断する必要があるという、アイテム間の依存関係があるためです。一方、ファインダーごとのファンアウトはそうではありません。ラウンド内で、あるファインダーの処理を別のファインダーの完了待ちにすることは決してありません。
使用する場面
- 結果セットの件数が事前にわからない場合。
- 1 回の探索では見落としが起きる可能性が高い場合(バグ、エッジケース、統合ポイント、データソース、移行が必要なファイルなど)。
- 何ラウンドか連続して新しい項目が見つからなくなるまで探索を続けたい場合。
マルチモーダルなスイープ(work-sweep)を補完します。スイープでは 1 ラウンドで異なる探索観点を使い、work-exhaust では飽和状態になるまでラウンドを繰り返します。
使用方法
/work-exhaust [K=3] "what we are discovering" [context]
例:
/work-exhaust "all places in the codebase that touch job state transitions"/work-exhaust 4 "edge cases in the async job draft flow" "focus on error paths and partial failures"/work-exhaust "document parsing failure modes"
オーケストレーターは、調査またはレビューのフェーズでこのスキルを使用することがよくあります。
オラクルは3つの箇所でゲートします(L1)
発見ループは純粋なエージェントパネルではありません。低コストで決定論的なオラクル(grep/rg、テスト、型チェック、アプリの実行)が、3つの異なる時点でゲートします。確信度の高いファインダーを使うのは、オラクルがない領域に限られます。
- ループ前のオラクルによるシード設定(ラウンド1の前)。 直接オラクルを実行し、正確なパターンで列挙できる項目を
seen[]にシードとして設定します。たとえば「Xに触れるすべての箇所」を調べるにはrg -n "<core-entity>"を実行します。これによりループを事実に基づかせ、1回のgrepで列挙できるものをファインダーが「発見」するのを防ぎます(L1:まず直接オラクルを実行)。 - 各項目を
file:lineで個別に再読み込み(L2/L8)。 ファインダーが返した項目はすべて、そのlocationで再度開き、現在のソースで確認してからseen[]に追加します。ファインダーが引用したevidenceをそのまま信用してはいけません。再読み込みの結果は、workDone:trueを付けてその項目のreReadSourcesに記録します。利用可能なオラクルがまだ実行されていない項目は、受け入れません(フェイルクローズ)。まずgrep/テストを実行してください。 - ファインダーのシード設定とは独立した、最終的な網羅性オラクル(L5)。 調査完了と宣言する前に、ファインダーの観点を再利用しないグローバルオラクルを少なくとも1つ実行します。たとえば、リポジトリ全体に対するコアエンティティの
rg検索、スコープ網羅率の差分、または「モジュール内で、どのファイルがどのファインダーのリストにも含まれていないか」の確認です。新しい項目が見つかった場合は、seen[]に追加し、dryCounterを0にリセットします(ファインダーはある観点を見落としており、領域の調査は完了していません)。これが、グラウンディングのケースで見落とされた「孤立ファイル5件」を検出するゲートです。
False-DRY ガード(観点の枯渇 ≠ 領域の調査完了)
新しい項目が0件のラウンドがKにカウントされるのは、そのラウンドで実際に新しい作業を行った場合だけです。
- すでに枯渇した観点(
anglesUsed[]に含まれ、前回0件だった観点)を再実行したラウンドは、ソフト空ラウンドです。dryCounterは増加しません。softEmptyとして記録し、新しい観点で再試行します。 - Kは条件を満たすラウンドのみで測定します。各ラウンドでまだ
anglesUsed[]に含まれていない新しい観点を導入したか、最終的な網羅性オラクルを実行したラウンドです。観点の枯渇は、領域の調査完了を意味しません。 - したがって、各ラウンドで、少なくとも1つの未枯渇の観点に必ず切り替えてください(出典は
../work-sweep/SKILL.md)。新しい観点も未実行のオラクルも残っていない場合、領域の調査は本当に完了しています。停止し、stoppedReason:"angles-and-oracles-exhausted"を記録してください。
仕組み
-
初期化
seen[]、anglesUsed[]、dryCounterを読み込むか、クリアします(再開に備えてジャーナルに永続化します —seen[]は決してリセットしません。L4)。- ループ前のオラクルシード(ゲート1)を実行し、オラクルで列挙可能な項目を
seen[]に追加します。 - この調査の発見ミッションと角度のプールを定義します(
work-sweepから取得)。 - ドライしきい値 K を設定します(デフォルトは3、K≥2)。ハーネスの todo/plan ツールを使って、形状 + K + シード数を通知します。
-
ラウンドループ(各ラウンドは並列ファンアウト → バリア)
anglesUsed[]に含まれていない未使用の角度を少なくとも1つ選びます(False-DRY ガード)。選択した角度をanglesUsed[]に追加します。- ラウンドのファインダーサブエージェントを1回の並列ウェーブで起動します(適切なサブエージェントの役割を持たせ、各ファインダーにつき1つ起動します。読み取り専用とし、ワークツリーを使用せず、バックグラウンドで実行します)。ファインダーには前のラウンドで見つかった具体的な内容を伝えず、繰り返しを避けるためのハイレベルな目標と
seen[]の概要を渡します。 - **バリア:**すべてのファインダーを待ってから、各結果を
.filter(Boolean)で収集します(Null の処理)。停止したファインダーは除外してログに記録し、空の結果としては数えません。 - 返された各項目に対して、ゲート2(
file:lineの独立した再読み取り)を実行します。再読み取りに失敗した項目は除外してログに記録します。未実行のオラクルがある項目は、先にオラクルを実行します。 - 生き残った項目をグローバルな
seen[](却下された項目も含め、これまでに発見されたすべての項目との重複を排除します — L4)と照合して重複を除去します(単純なセット処理で行い、エージェントは使用しません)。真に新しい項目をseen[]とこのラウンドのnewItems[]に追加します。 - ドライカウンターの更新(False-DRY ガード付き):
- ラウンドで新しい角度/オラクルを導入し、新しい項目が1つ以上見つかった →
dryCounter = 0。 - ラウンドで新しい角度/オラクルを導入し、新しい項目が0個だった →
dryCounter += 1(条件を満たすドライラウンド)。 - ラウンドで使い尽くした角度のみを再実行した →
softEmpty(dryCounterは変更しません)。新しい角度で再試行します。
- ラウンドで新しい角度/オラクルを導入し、新しい項目が1つ以上見つかった →
- ハーネスの todo/plan ツールを使って、
Round N: X new (T total) · angle=<a> · dryCounter Y/Kをログに記録します。 dryCounterが K に達する場合は、まずゲート3(最終完全性オラクル)を実行します。新しい項目が見つかった場合はdryCounter = 0として続行し、それ以外の場合は停止します。
-
収束
dryCounter >= K(ゲート3で新しい項目がないことを確認した後)、stoppedReason:"angles-and-oracles-exhausted"、または呼び出し元が明示した予算/目標(ログに記録済み)に達した場合にのみ停止します。- 完全なマスターオブジェクト(構造化 I/O 契約を参照)と、ラウンドごとの概要および完全性に関する注記を返します。
- 予算/並行性/時間の都合で優先度を下げた内容はすべて、
droppedDueToBudget[]に明示的に記録します(黙って上限を設けないでください。L7)。
-
直接検証と引き継ぎ
- 受け入れられた各項目には、ゲート2で取得した
reReadSources/workDoneがすでに含まれています。より強力なオラクル(失敗するテストや正確な再現コマンドなど)が使える項目は、ここで実行して記録します。 - 重複排除済みのセットは通常、
../work-refute/SKILL.md(各項目が反証対象の主張になります)と../work-gaps/SKILL.mdに渡します。
- 受け入れられた各項目には、ゲート2で取得した
構造化 I/O 契約
スキーマは2つあります。各ファインダーはファインダーごとの項目オブジェクトを出力します(検証済みで、不正な出力の場合は1回再試行し、その後はワークフローモード §3 に従って除外し、ログに記録します)。ループは検証を行い、呼び出し元が監査できるマスターオブジェクトを返します。
1. ファインダーごとの戻り値(ファインダーごとに1つ、items 配列)
{
"angle": "error-paths",
"items": [
{
"id": "job-state-partial-fail",
"title": "partial failure leaves job in DRAFT with orphaned artifacts",
"description": "…",
"location": "src/jobs/transition.ts:142",
"evidence": "transition.ts:142 — catch swallows error, no rollback of artifact write",
"reReadSources": ["src/jobs/transition.ts:142"],
"workDone": true,
"severity": "high"
}
],
"roundNotes": "string — what this angle covered and where it felt thin"
}
severity列挙値:critical | high | medium | low(L6 に従って調整します。これは発見時のヒントであり、正式な重大度は後続のwork-refuteで決定されます)。locationはfile:lineです(該当するファイルがない場合は、名前付きの概念)。reReadSources+workDoneにはゲート2の再読み取り結果が記録されるため、作業を怠ったファインダーを検出し、その項目を除外できます(L2/L8)。
2. マスター戻り値(ループが検証して返すもの)
{
"rounds": [
{
"n": 1,
"anglesUsed": ["grep-seed", "error-paths"],
"newItems": ["job-state-partial-fail", "…"],
"dryCounterAfter": 0
}
],
"seen": ["job-state-partial-fail", "…"],
"stoppedReason": "k-consecutive-dry",
"droppedDueToBudget": [],
"completenessGaps": ["module X has 3 files no angle swept; ran gate-3 grep — clean"]
}
stoppedReason列挙型:k-consecutive-dry | angles-and-oracles-exhausted | budget | caller-target。seen[]は永続的な重複排除の信頼できる情報源です(リセットしません。L4)。droppedDueToBudget[]とcompletenessGaps[]により、上限とスコープの見落としが明示されます(L5/L7)。dryCounterAfterは条件を満たしたカウンターです(ソフトな空振りでは 進みません)。- 呼び出し元はこのオブジェクトを検証します。ループ結果の形式が不正な場合は一度再リクエストし、その後は失敗として提示します。「完了」と 黙って扱うことはありません。
実行の仕組み
これらは workflow-mode §3 の LOOP 固有の適用例です。一般的なルールについてはそのドキュメントを参照してください。
- ジャーナリング/再開(決定的):各ラウンドの後に
quality-journals/work-exhaust-<task>.json(エージェントのデータ ディレクトリ内)へ書き込みます。
再開時は、{ "seen": [], "anglesUsed": [], "dryCounter": 0, "rounds": [], "droppedDueToBudget": [] }seen[]、anglesUsed[]、dryCounterの3つすべてを読み込み、そこから続行します。seen[]は決してリセット しません(L4 — 却下した項目を再び入れてはなりません)。また、anglesUsed[]も決してリセットしません(使い切った角度を 再実行すると、ソフトな空振りが発生し、K が不正になります)。 - null の処理:失敗/エラー/空のファインダーはフィルタリング(
.filter(Boolean))してログに記録します。これは新規項目ゼロの ラウンドではなく、dryCounterにも影響しません。条件を満たしたラウンドで、生き残ったファインダーがすべて新規項目を返さなかった 場合にのみ、ドライカウンターが進みます。ファインダーが1つ停止しているだけで、「探索対象が尽きた」と判断してはなりません。 - 並行処理の上限:ファインダーを
min(16, cores−2)(または設定された上限)の制御されたウェーブで起動します。超過した角度は次の ウェーブに回します。適用した上限はログに記録します(黙って上限を適用しません)。 - 予算の考慮:予算が少なくなった場合は、角度数を明示的に減らすか、早めに停止し、その内容を
stoppedReason/droppedDueToBudget[]に記録します。ラウンドを黙って縮小してはなりません。 - ラウンド内のマルチモーダル処理:
../work-sweep/SKILL.mdから探索角度を選びます。 False-DRY ガードでは、条件を満たす各ラウンドでそのプールから少なくとも1つの新しい角度を使う必要があります。 - 上限を黙って適用しない(L7):実行しなかった角度、スキップしたラウンド、除外した項目(再読み込みの失敗、予算)、優先度を下げた残りの項目はすべて、 判断記録に名前付きの行として記載し、ハーネスの todo/plan ツールでも提示します。
- ログ/フェーズ:ハーネスの todo/plan ツールを通じて、ループを人間が途中で停止できるよう、
Round N: X new (T total) · angle=<a> · dryCounter Y/K、softEmpty (exhausted angle) — retry、gate-3 completeness: +Z new → dryCounter resetなどを出力します。
実例(K=2、「すべてのジョブ状態遷移箇所」)
Gate 1 (oracle seed): rg -n "transition\(|setState\(" → 4 sites → seen=[s1..s4]
Round 1 · angle=grep-seed + error-paths (fresh)
finders → 3 items; gate-2 re-read drops 1 (line moved, not a transition)
dedup vs seen → +2 new (s5,s6) → dryCounter 0/2
Round 2 · angle=concurrency/races (fresh)
finders → 1 item; gate-2 OK; +1 new (s7) → dryCounter 0/2
Round 3 · angle=error-paths (ALREADY in anglesUsed) → 0 new
→ softEmpty (exhausted angle), NOT a dry round → retry with fresh angle
Round 3' · angle=event-emitters (fresh) → 0 new (qualifying) → dryCounter 1/2
Round 4 · angle=cron/background-jobs (fresh) → 0 new → dryCounter would hit 2/2
→ run Gate 3 (final completeness): rg across whole module for the entity,
independent of finder angles → finds s8 in a file no angle swept
→ s8 enters seen, dryCounter RESET to 0/2, continue
Round 5 · angle=migrations (fresh) → 0 new → dryCounter 1/2
Round 6 · angle=admin-tooling (fresh) → 0 new → Gate 3 clean → dryCounter 2/2 → STOP
stoppedReason="k-consecutive-dry"; seen=[s1..s8]; completenessGaps logged.
Round 3 では K が進んでいない点に注意してください(False-DRY ガード:使い切った角度は探索対象全体を使い切ったことを意味しません)。また、Round 4 の Gate 3 では カウンターがリセットされています(L5:完全性の確認はファインダーのシードとは独立しています)。
統合
- ラウンドごとの角度は
../work-sweep/SKILL.mdから取得します(そのスイープの1ラウンド ≈ ここでの1ラウンドです。このスキルでは探索対象が尽きるまで繰り返します)。 - 重複排除されたセットは
../work-refute/SKILL.mdに渡され(検出した各項目が反証を試みる主張になります)、../work-gaps/SKILL.mdにも渡されます(gate 3 はその軽量なインライン版です。完全な批評は収束後に実行されます)。 ../work/SKILL.mdがオーケストレーションを行い、1回のスイープではなくこのループを実行するほど、発見セットの規模が大きいか不明かを判断します(workflow-mode §5「依頼に合わせて規模を調整する」— grep 1回で片付く3項目の列挙にループを実行しないでください)。
停止ルール(厳格)
dryCounter >= K の場合にのみ停止します。K は条件を満たすラウンドのみをカウントします。
- 条件を満たすラウンドとは、新しい観点(
anglesUsed[]に含まれていないもの)を導入したか、ゲート3の完全性オラクルを実行し、重複を除いたseen[]との照合後に新しい項目が0件だったラウンドです。 - ソフト空振り(使い尽くした観点を再実行した場合)はカウントせず、新しい観点で再試行します。
- K 回目の条件を満たす空振りラウンドが確定する前に、ゲート3を実行してクリーンな結果を得る必要があります。新しいものが見つかった場合は、
dryCounterを 0 にリセットします。 - その他の停止条件は、
angles-and-oracles-exhausted(新しい観点も未実行のオラクルも残っていない場合)と、呼び出し元が明示したbudget/targetのみです。どちらもstoppedReasonに記録します。「もう十分だ」と判断して早期に停止してはいけません。
このパターンは、1 回のパス、あるいは同じ観点を繰り返す数回のパスで網羅できたふりをせず、「すべて見つける」問題に確実に取り組む方法です。