review-tickets について
Review Ticketsは、review-deep、review-full、review-diffの実行結果から、検査済みのチケットファイルを作成するスキルです。レビュー対象のコミットに照らして指摘を再確認し、実行可能な修正ごとにまとめ、確認やJiraへの投稿に使える形式に整えます。
主な機能
- 01指摘をファイル単位に再分割
- 02レビュー対象コードに基づく指摘判定
- 03同一修正で解決する単位の統合
- 04コード引用とチケット位置の検証
- 05MarkdownとJSON形式での出力
活用シーン
- 01コードレビューから実行可能なチケットを作成
- 02レビュー対象コミットと合わない指摘を除外
- 03Jira投稿前のチケット確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jimweller/ clanker-code-review-plugin --skill review-ticketsインストール後、Claude Code で/review-ticketsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
prompts/judge.txtsibling.txtscripts/apply.pyassemble.pybundle.pychecks.pycommon.pydupscreen.pyevidence.pyjudge.pyplan.pypool.pyreadback.pyrecut.pysiblings.pywiki.py詳細
STARTER_CHARACTER = 🧾
レビューチケット
review-deep、review-full、または review-diff の実行結果を、レビューを読み直さなくても人やモデルが対応できるチケットファイルに変換します。チケット内の文はすべてレビュアー自身の文であり、コードを読んだ判定者がそのまま残すか、範囲を絞るか、削除したものです。新たに加えるのは Summary のみです。
入力は review-deep、review-full、または review-diff の実行ディレクトリです。ここには run.json、findings.jsonl、issues.jsonl、verify/results.jsonl、verify/second.jsonl、およびレビュー対象のコミットをクリーンにチェックアウトした checkout/ が含まれます。このスキルが書き込むものはすべて <run>/tickets/ 以下に置かれます。スクリプトはこのファイルと同じ場所にある scripts/ にあり、dupscreen.py は uv 経由で実行されることを除き、Python 標準ライブラリを使用します。
引数
review-deep、review-full、または review-diff が出力した実行ディレクトリです。以下のすべてのコマンドでは、これを $RUN として使用します。実行時に実行ディレクトリが指定されなかった場合、候補が1つだけであれば Step 1 で自動検出します。確認のために処理を止めることはありません。
要件
PATH上にclaudeがあり、プロバイダーにログインしていること。各判定者はこの環境を引き継ぐclaude -pプロセスとして実行されるため、opusやfableなどのモデルエイリアスは、呼び出し元のセッションと同じプロバイダー設定で解決されます。gitがあり、run.jsonのrepoに指定されたリポジトリにレビュー対象のコミットが存在すること。- 重複チェック用の
uv。
run.json では、models の下に [alias, effort] ペアとしてモデルを設定します。デフォルトは、judge が xhigh の Opus、critical が high の Fable、sibling が xhigh の Opus です。concurrency のデフォルトは 50 です。
手順
各モデルステップは、バックグラウンドの Bash 呼び出しとして実行します。すべての呼び出しが終了するとプールから制御が戻り、完了通知を受け取ったら次に進みます。実行中のステップを sleep でポーリングしてはいけません。ステップのコマンド自体をバックグラウンド呼び出しとして実行してください。nohup や末尾の & でデタッチしてはいけません。また、tail -f | grep -m1 でログ行を待ってはいけません。tail は次に書き込みがあるまで終了しないため、ログの最後の行が出力された後は、タイムアウトまで呼び出しが停止したままになります。
Step 1: 事前確認と計画
S="${CLAUDE_SKILL_DIR}/scripts" # replace ${CLAUDE_SKILL_DIR} with this SKILL.md's own directory on a non-Claude harness
[ -f "$S/plan.py" ] || { echo "scripts not found at $S"; exit 1; }
PF="${CLAUDE_SKILL_DIR}/../review-deep/scripts" # preflight.py has one copy, in review-deep
[ -f "$PF/preflight.py" ] || { echo "scripts not found at $PF"; exit 1; }
RUN="<user-provided run directory, or empty>"
if [ -z "$RUN" ]; then
cache="${XDG_CACHE_HOME:-$HOME/.cache}"
candidates=$(find "$cache/review-deep" "$cache/review-full" "$cache/review-diff" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
| while read -r d; do [ -f "$d/run.json" ] && echo "$d"; done)
n=$(printf '%s\n' "$candidates" | grep -c .)
if [ "$n" -eq 1 ]; then
RUN="$candidates"
echo "no run directory given, one candidate exists, using $RUN"
else
echo "no run directory given and $n candidates exist, name one explicitly:"
printf '%s\n' "$candidates"
exit 1
fi
fi
python3 "$PF/preflight.py" --tier tickets --run "$RUN" || exit 1
python3 $S/plan.py "$RUN"
preflight.py --tier tickets は、run.json が存在すること、リポジトリとレビュー対象のコミットがまだ存在すること(review-full と review-diff の init_run.py に従い、スナップショット実行から2週間後に git gc でコミットが削除される場合があります)、および checkout/、issues.jsonl、verify/results.jsonl のすべてが存在することを確認します。これは、3種類の入力のいずれも、チケット作成開始前に review-deep の Step 5 の処理を終えている必要があるためです。$S ガードは、プラグインまたはスキルのインストールが不完全または破損していないかを検出します。
レビュー評価が基準値以上(デフォルトでは High)で、検証者が確認済みの issue が計画対象になります。チケットの重大度は、検証者と2回目のチェックの評価のうち低い方であり、基準値を下回る場合もあります。tickets/decisions.json には、すでに別の場所で追跡されている issue を指定する skip_issues と、最初の判定がプレースホルダーの回答などで使用できない issue を指定する include_issues を記載できます。include された issue は判定者自身が検証します。
Step 2: 単位に再構成
python3 $S/recut.py $RUN
1つの単位は、1つのファイルにある1つの issue の指摘です。24ファイルにまたがる77件の指摘をまとめたテーマ別 issue では、引用された箇所が主張の根拠にならないチケットが生成されました。ファイルごとに単位を分けることでこれを解決し、Step 4 では1つの変更で複数の単位を修正できる場合にのみ、単位を再びまとめます。
Step 3: 判定と編集
python3 $S/judge.py $RUN # every unit, background
python3 $S/apply.py $RUN
python3 $S/judge.py $RUN --critical # second model on units still Critical, background
python3 $S/apply.py $RUN
判定者はコードを読み、すべての主張に反論できるかを試し、各指摘を keep、edit、または delete として返します。keep では、主張ごとに根拠となるコードを示す必要があります。edit では、コードと矛盾する部分のみを変更します。すべての edit と delete にはコードの引用が必要で、回答を受け入れる前に、スクリプトがレビュー対象のコミットにおける git の内容と引用を照合します。apply.py は tickets/edits.json を書き込みます。判定者が反証した場合、2つ目のモデルが判定に同意しない場合、または引用がコードではない場合、その単位は除外されます。
judge.py --ids FILE は、JSON 形式の単位 ID リストを再実行し、その他のすべての回答はそのまま保持します。
ステップ 4:同じ修正で解決できる兄弟項目を統合する
python3 $S/siblings.py $RUN # background
python3 $S/judge.py $RUN --merged # background
python3 $S/apply.py $RUN --merged
ファイル単位で分割すると、呼び出し元と呼び出し先の両方から見える同じ不具合も分割されます。準備が整った単位が2つ以上ある各 issue について、1か所での1つの変更により不具合が解決する単位を判定者がグループ化します。独立した2か所にある同じパターンは、別々のチケットのままにします。統合したチケットは全体として再度判定され、新しい Summary が確認され、指摘事項が編集される場合があります。統合したチケットでも、各指摘事項はそれぞれ独立した箇条書きとして保持されるため、呼び出し元と呼び出し先の両方が記載されます。
ステップ 5:組み立てて確認する
python3 $S/assemble.py $RUN
python3 $S/checks.py $RUN
assemble.py は、実行ディレクトリ内の report.md と並んで tickets.md も出力します。これは同じチケットをプレーンな Markdown で記載したもので、冒頭に観点と優先度ごとのチケット数があり、その後にすべてのチケットが優先度順に指摘事項とコードとともに記載されます。ステップ 7 の後にステップ 5 を再実行すると、ファイルが書き換えられます。
投稿する前に、checks.py が problems 0 と出力する必要があります。このスクリプトは、すべてのコードブロックをバイト単位で git と比較し、すべての位置を確認し、Jira で正しく表示されないマークアップにフラグを付けます。
ステップ 6:重複を確認する
対象プロジェクトの issue を、任意の Jira クライアントで JSON としてエクスポートします。各 issue は key、summary、description を持つオブジェクトとし、トップレベルまたは fields の下に配置します。その後、次を実行します。
uv run --with scikit-learn --with numpy python $S/dupscreen.py $RUN existing.json
この確認では、共通する語彙に基づくペアが表示され、人が内容を確認できます。ほとんどのペアは話題が共通しているだけで、不具合が共通しているわけではありません。
ステップ 7:判断を記録する
オペレーターの判断を tickets/decisions.json に記録し、ステップ 5 を再実行します。
| キー | 効果 |
|---|---|
drop | {ID: reason}。たとえば、既存の issue と重複する場合など、そのチケットを削除します。 |
defer_priorities | ["Low"] のようなリストです。該当するチケットは組み立てと確認の対象に残りますが、tickets/postable.json からは除外されます。 |
skip_issues, include_issues | ステップ 1 で読み込まれます。 |
ステップ 8:投稿する
オペレーターから明示的な許可を得てから投稿してください。利用可能な Jira クライアントを使用します。以下のルールは385件のチケットで適用されました。
- 優先度順に、チケットを1件ずつ投稿します。Critical は Highest、High は High、Medium は Medium、Low は Low に対応付けます。
summary、type、対応付けた優先度、labels、およびdescriptionを Jira wiki マークアップとして送信します。 - 作成呼び出しが戻ったら、ほかの処理をする前に、チケット ID と新しいキーをログに追記します。
- すべてのチケットを、説明を ADF として読み戻し、
python3 $S/readback.py TICKET_JSON FIELDS_JSONを実行します。不一致が最初に見つかった時点で停止します。ある実行では、エンダッシュでつながれた2つのコードスパンが1つのスパンとして表示されたため停止しました。チェッカーが、次の300件で同じ問題が繰り返される前に検出しました。 - 最初のエラーで停止します。429 の場合のみ、Retry-After の待機時間後に再試行します。タイムアウトや 5xx の場合は issue が作成済みの可能性があるため、再試行前に照合します。
- 再開する前に、ログを実行時のチケットラベル(
run.jsonから読み込んだreview-deep、review-full、またはreview-diff)による検索結果と照合します。Jira の検索ではアーカイブ済み issue が除外されるため、意図的にアーカイブされた issue が未照合の issue として扱われることはありません。 - 最初のチケットだけを投稿し、フィールドごとに読み戻して確認します。一致した場合にのみ続行します。
ステップ 9:保留中のチケットをまとめる
python3 $S/bundle.py $RUN
tickets/bundle/ticket.json には、保留中の作業すべてを対象とした1件のチケットが記述され、各カテゴリのラベルが含まれます。また、tickets/bundle/<name>-deferred-defects.md には、保留中の各チケットの Summary、カテゴリ、指摘事項が記載されます。チケットを投稿してファイルを添付し、ダウンロードした添付ファイルとローカルファイルをバイト単位で比較します。
Jira wiki マークアップ
アセンブラーがこれらを処理し、見落としがあれば checks.py が検出します。それぞれ、投稿してから読み戻すことで確認しました。
{{...}}の中では、Jira は{ } [ ] * | !の前にあるエスケープ用のバックスラッシュを取り除き、それ以外の文字の前にあるものは保持するため、{{"a\"b"}}は"a\"b"と表示されます。- バックスラッシュだけを含むコードスパンには、機能するエンコード方法がありません。
{{\}}では等幅表示が失われ、{{\\}}はバックスラッシュが2つ表示され、\はデコードされずに残ります。アセンブラーは「バックスラッシュ」という単語を書き出します。 - Jira は、特定の文字の直後または直前でのみ
{{...}}を閉じます。確認できたのは、スパンの後ではスペース、, / . ' - ; )、および改行、スパンの前ではスペース、/、(、および改行です。アセンブラーは、スパンとそれ以外の隣接要素との間にスペースを入れます。 - Markdown のバッククォート列は、同じ長さの列とのみ対応します。検出結果内の
```jsonはテキストのままです。 javascript json yaml bash python sql csharp以外のコード言語は、レンダリングで確認できた他の言語名がなかったため、noneとして書き出します。
設定の根拠
| 設定 | 根拠 |
|---|---|
| 判定役に xhigh の Opus を使用 | 誤りと分かっている文を10件含む10ユニットでは、Opus xhigh は10件中10件を $3.84 で削除し、Fable medium は10件中10件を $6.93 で削除し、Fable high は10件中10件を $9.65 で削除し、Opus high は10件中9件を $1.72 で削除しました。全件の実行では、同時実行数50で618ユニットの処理に17分かかり、定価で $188.59 でした。 |
| 保留にはすべて理由を付ける | 判定を通過した誤った文は、すべて理由が空欄の保留でした。このルールのもとで、そのような保留を含む291ユニットを再判定すると、$98.49 かかりました。 |
分離された claude -p --bare プロセス | ワークフローのサブエージェントには、オペレーターの CLAUDE.md、スキル一覧、MCP の指示、フックが渡されていました。Serena フックが読み取りを拒否し、CLAUDE.md の文章ルールによって引用符で囲まれたコード内の句読点が変更されました。bare プロセスのコンテキストは約3,000トークンで開始するのに対し、もう一方は77,000トークンです。 |
dontAsk のもとで --tools Bash,Read を使用 | --bare には Grep も Glob もなく、それらを要求しても Read だけが返されるため、全実行で検索ができなくなっていました。検索は Bash 経由で実行され、読み取り専用コマンドは実行され、書き込みは拒否されます。実際には cd、awk、xargs、cut、node、変数の代入、$(...) が拒否されたため、プロンプトでそれらを明示しています。 |
| 5分間のプロンプトキャッシュ | ENABLE_PROMPT_CACHING_1H を設定すると、キャッシュへの書き込みはすべて1時間料金で請求され、入力価格の2倍になりました。プールはこの設定を削除し、CLAUDE_CODE_PROMPT_CACHE_TTL=5m を設定します。 |
| すべての回答に内容チェックを実施 | JSON スキーマで形式を確認します。モデルはスキーマによる拒否への回答に、reason: "test" のようなプレースホルダーや、a:1 のようなエビデンスを含め、型注釈を削除したり句読点を書き換えたりしたコードを引用しました。 |
| 長いセッションを1つ使わず、チケットごとに1プロセスを使用 | 多くのチケットを判定するセッションでは、ターンごとに増え続けるコンテキスト全体を読み直します。チケットごとのセッションは約26,000トークンで終了します。 |
| コミットのクリーンなチェックアウト | ライブツリーを検索した判定役は、レビュー状態ディレクトリにある他のレビュアーの検出結果を読み取っていました。チェックアウトでは、run.json の judge_exclude に記載された CLAUDE.md や .claude/ などのファイルも除外されます。 |
| 2つの評価のうち低い方を採用 | オペレーターのルールです。判定役の評価はレビュアーより低くなる傾向があり、618ユニットを処理した本番実行では459ユニットの評価が下がりました。 |
件数の報告
チケットには、そのチケットの検出結果のいずれかを挙げた視点ごとにカテゴリラベルが1つ付き、ほとんどのチケットには複数のラベルが付きます。カテゴリと優先度別のチケット数を表にすると、カテゴリの行はラベル数を数えるため、チケット数を上回る合計になります。各行にはそのカテゴリのラベルが付いたチケット数と明記し、チケットの個別件数は別の合計として示してください。
ファイル
$RUN/tickets/ 内:
plan.json、units.jsonl、計画されたイシューとファイルごとのユニットjudge/results.json、judge/merged.json、試行とコストを含む各判定結果、および各プロセスのイベントストリームを記録したjudge/calls/edits.json、merges.json、merge_edits.json、decisions.jsonfinal/<ID>.json、チケットごとに1つの投稿ペイロード、final/manifest.json、postable.jsondup_pairs.json、bundle/
実行ディレクトリ内の report.md と同じ階層:
tickets.md、優先度別の視点テーブルの後に、すべてのチケットをプレーンな Markdown で記載
テスト
review-deep と review-tickets のテストおよび評価に加え、4つすべてのスキルを対象とするガードテストと事前チェックテストは、このプラグインのリポジトリのルートにある evals/ にあります。インストール済みのスキルの外側です。
| チェック | コマンド(evals/ から実行) | コスト |
|---|---|---|
| ユニットテスト(スキルごとに1ディレクトリ)、事前チェック、ガード | uv run --with pytest pytest -q unit/review_deep、unit/review_tickets、unit/preflight、unit/guard | なし |
| harbor フィクスチャでのエンドツーエンド再生(3種類すべて) | uv run --with pytest pytest -q test_end_to_end.py | なし |
fixture/truth.json を使ったライブ品質評価 | python3 live_eval.py WORK_DIR --run | 定価で約 $3 |
scripts/ を変更した後は、ユニットテストと再生を実行してください。プロンプトを変更すると、再生は「prompt changed」で停止します。python3 pipeline.py WORK_DIR --mode record で再度記録し、live_eval.py で実行結果を確認してから、python3 golden.py save RUN_DIR で保存してください。モデル、エフォートレベル、またはプロンプトを変更した後は、必要に応じてライブ評価を実行してください。
ルール
- オペレーターの明示的な許可なしに投稿してはなりません。また、最初のチケットの内容を読み返す前に一括投稿してはなりません。
- 判定者にライブの作業ツリーを読ませてはなりません。判定者は
run.jsonのcheckoutで実行します。 - コンテンツチェックを行わずにモデルの回答を受け入れてはなりません。また、レビュー対象のコミットに含まれていないコードの引用を証拠として受け入れてはなりません。
prompts/内のプロンプトは.txtのままにしてください。Markdown フォーマッターが.mdのプロンプトを書き換え、意味を変えてしまいました。- 件数は実測値で報告してください。チケット数、ラベル数、指摘数は、それぞれ異なる数です。