自動トリアージを組んで二週間、月曜の朝に届いたレポートを見て手が止まりました。先週「確認コメント」を入れたはずの古い Issue に、今週もう一度ほぼ同じコメントが付いていたのです。通知を受け取ったユーザーからすれば、ボットが二度同じことを言ってきただけ。さらに数件、先週まで type: feature だった Issue が、今週は type: improvement に書き換わっていました。中身は何も変わっていないのに、です。
初回の手動実行はうまくいっていました。未分類の Issue を一気に片づけ、優先度が並んだバックログを見て「これは使える」と感じた直後の出来事でした。問題は、トリアージを一度きりの作業として設計してしまい、毎週繰り返し走る前提で設計しなかったことにあります。
私自身、複数のアプリと四つの技術ブログを一人で回していて、GitHub のバックログは常に後回しになります。だからこそ無人で動く仕組みに価値があるのですが、無人運用は「初回の精度」よりも「百回目も同じ結果になるか」で評価しないと、気づかないうちに信頼を損ねます。この記事は、その「静かなズレ」を生む三つの源と、それぞれを塞ぐ実装をまとめた運用メモです。GitHub MCP の基本接続は終えていて、これから定期実行に載せたい方を想定しています。
無人トリアージが崩れる三つのドリフト源
手動なら問題にならず、無人にした途端に表面化する崩れ方には、はっきりした型があります。原因を切り分けないまま「精度が悪い」と片づけると、プロンプトをいくら磨いても直りません。
| ドリフト源 | 症状 | 根の原因 |
| 状態の非冪等 | 同じ Issue に毎回コメントが増える/ラベルが付け直される | 過去の自分の処理結果を参照せずに毎回ゼロから判断している |
| ラベル境界のズレ | 中身が変わっていないのに分類が週ごとに揺れる | 分類基準が言語化されておらず、その週の文面の印象で決まる |
| レートの連鎖枯渇 | 後半の Issue が処理されず、途中で静かに止まる | 取得・更新・コメントのリクエスト総量が上限に当たる |
順に、なぜ起きるのかと、どう塞ぐのかを見ていきます。いずれも「指示を強くする」のではなく「状態を観測してから動く」方向で解きます。
ドリフト源1:冪等でない処理がコメントを二重に積む
冪等性(同じ操作を何度繰り返しても結果が変わらない性質)は、無人運用の土台です。手動なら「あ、これ先週やったな」と人間が覚えていますが、定期実行のセッションは毎回まっさらな状態で始まります。前回何をしたかを記録し、毎回それを読みに行く設計にしない限り、二重処理は必ず起きます。
最も実害が出るのが、ユーザーへ通知が飛ぶコメント追加です。ラベルの付け直しは静かですが、コメントは相手の受信箱を鳴らします。解決策は、自分が付けたコメントに機械可読のマーカーを埋め込み、次回はそれを見て判断することです。
[owner/repo] の Issue #<番号> に確認コメントを入れる前に、
まず get_issue でその Issue の既存コメントを取得してください。
スキップ条件(いずれかを満たせば、コメントを追加せず "skipped: already-marked" と記録):
- 本文に文字列 "<!-- cowork-triage:stale-check -->" を含むコメントが既にある
上記に当てはまらない場合のみ、次のコメントを add_issue_comment で追加してください。
末尾のHTMLコメントは目印なので必ず残してください。
---
このIssueは長く更新がないため、状態確認のコメントです。
現在のバージョンでも再現する場合はお知らせください。再現しなければクローズで構いません。
<!-- cowork-triage:stale-check -->
---
ポイントは、人間の目には見えない <!-- ... --> を識別子にしていることです。GitHub のコメント本文は HTML コメントを表示しないので、読者には自然な一文だけが見え、機械は確実に「処理済み」を判定できます。文面そのものでマッチさせると、文言を少し変えただけで重複判定が外れて二重投稿に戻るため、必ず固定マーカーで判定してください。
ラベル更新も同じ発想で冪等にします。「未ラベルのものだけ」「status: triage がまだ無いものだけ」と、現在状態を述語にして対象を絞るのが基本形です。「全 open Issue を分類し直す」は一見強力ですが、毎回全件を触るので揺れの温床になります。
[owner/repo] の open Issue のうち、type: で始まるラベルが
一つも付いていないものだけを対象にしてください(既に分類済みのものは触らない)。
無人パイプラインの重複検知については、トピック生成側で同じ問題に向き合った記録もあります。あわせて読むと、冪等性を「状態の述語化」で解く発想がつかみやすいはずです(Cowork の無人パイプラインで重複トピックを検知するゲート設計)。
ドリフト源2:ラベル境界が曖昧だと分類が毎週揺れる
type: feature と type: improvement、priority: high と priority: medium。この種の隣り合う分類は、人間でも迷います。基準を明文化しないまま AI に委ねると、その週の Issue の書きぶり次第で判定が振れます。中身が変わっていない Issue のラベルが週をまたいで書き換わるのは、ほぼこれが原因です。
直し方は二つあります。第一に、境界を決定表として固定し、プロンプトに毎回同じものを渡すこと。第二に、一度自動で付けた分類は原則上書きしないルールにして、揺れの入り込む余地そのものを減らすことです。
決定表は、迷いやすいペアだけに絞って書くと効きます。全分類を網羅するより、誤りやすい隣接ペアの線引きを一つ決めるほうが実用的です。
| 迷うペア | 判定の分かれ目 | 具体例 |
| feature / improvement | 今は存在しない振る舞いを足す=feature。既存の振る舞いを速く・使いやすくする=improvement | 「ダークモードを追加」=feature/「一覧の表示が遅い」=improvement |
| bug / improvement | 仕様どおり動いていない=bug。仕様どおりだが不便=improvement | 「保存が失敗する」=bug/「保存に確認ダイアログが欲しい」=improvement |
| critical / high | データ損失・課金・ログイン不能など回避策が無い=critical。主要操作が阻害されるが回避策あり=high | 「課金後に反映されない」=critical/「特定端末でレイアウト崩れ」=high |
そして「上書きしない」を明示します。これが揺れを止める一番効く一文です。
分類は新規付与のみとし、既に type: または priority: ラベルが付いている
Issue のラベルは変更しないでください。判定が変わったと思っても上書きせず、
"reclassify-candidate" として番号だけを別途リストアップして報告してください。
人間が見直すべき境界事例だけを候補としてリストに上げ、実際の付け替えは手動の確認に回す。これで、自動分類の揺れがそのまま本番ラベルに反映される事故が止まります。週次レポートに reclassify-candidate が数件並ぶようになり、私はこのやり方、つまりそこだけ目視で直す運用に落ち着きました。三ヶ月回した体感で、毎週見直すのは平均で二〜三件程度。全件を疑うより、ずっと軽い負担です。
ドリフト源3:レート上限の連鎖枯渇で後半が静かに落ちる
GitHub API の認証付きレート上限は 5,000 リクエスト/時 です。一見余裕がありますが、トリアージは一件の Issue に対して「取得 → ラベル更新 → 既存コメント確認 → コメント追加」と複数リクエストを積みます。仮に一件あたり 4 リクエストなら、コメント確認を伴う処理は実質 1,000 件強で上限に届きます。さらに search_issues や list_issues のページングが加わると、体感はもっと早く尽きます。
怖いのは、上限に当たったときに処理の後半だけが静かに欠落することです。前半の Issue は片づき、レポートも一見正常に返るので、気づくのが遅れます。これは無人運用で見落としがちな落とし穴で、対処の基本は、一回の実行で触る件数に**明示的なバジェット(上限)**を置くことです。
1回の実行で処理する Issue は最大 25 件までにしてください。
取得は list_issues を1回だけ使い、ページングはしないでください。
コメント追加は priority: critical または high と判定したものに限定し、
medium / low はラベル更新のみ(コメント不要)としてください。
処理が上限に達したら、残りの番号だけをリストして "deferred" と報告してください。
ここには三つの節約が同時に効いています。件数の上限で総リクエストを抑え、コメント条件の限定で最も重い書き込みを減らし、ページング禁止で取得リクエストを一定に保つ。25 件・一件あたり約 4 リクエストなら、一回はおよそ 100 リクエスト前後で、上限 5,000 の約 2% に収まります。deferred を返させておけば、次回の実行が続きから拾えるので、件数を絞っても取りこぼしになりません。むしろ「毎回少しずつ・確実に」のほうが、無人運用では一回完結より安定します。
スケジュール実行が「成功したように見えて実は途中で止まっていた」事故は、トリアージに限らず無人運用全般の弱点です。実行の終わりに件数の整合を自分で検算させる作法は、別の運用メモにまとめてあります(Cowork スケジュールタスクの空振りを末尾アサーションで検知する運用)。
守りの認証 — 静的 PAT から短命資格情報へ寄せる
無人で回す仕組みほど、認証情報の置き方が事故の起点になります。Fine-grained PAT を使う場合の最低限は、スコープを操作対象に絞ることです。Issue と Pull requests を Read and Write、Metadata を Read-only、対象リポジトリは「Only select repositories」で明示列挙する。Organization 配下なら、Organization 側で PAT 利用の承認も要ります。ここまでは従来どおりです。
そのうえで、2026 年に入ってからの流れも踏まえておくと安心です。2026-06-28 の Claude Code アップデートでは、静的 API キーを WIF(Workload Identity Federation) へ置き換える方向が示されました。OIDC 準拠のプロバイダと連携し、リクエスト時に発行される短命・スコープ付きの資格情報を使う考え方です。GitHub 側でも、長命の PAT をベタ置きするより、必要時に発行され自動失効する資格情報へ寄せるほど、放置運用で一番怖い「キーが漏れていたことに後から気づく」リスクが下がります。同じ更新で MCP のレジリエンスとリモート MCP の安定性も上がっており、定期実行で MCP を叩く構成の取りこぼしが減る方向です。
すぐに全部を切り替えられなくても、原則は一つです。無人運用の資格情報は「露出面を小さく・寿命を短く」。スコープ最小化と対象リポジトリの明示は今日からでき、短命資格情報への移行はロードマップに置いておく、という温度感で十分だと考えています。
実運用で固まった週次トリアージ・プロンプト
ここまでの三つの守り(冪等・境界固定・件数バジェット)を一つのスケジュールプロンプトに畳み込みます。実運用で固まった形です。毎週月曜の朝、無人で走らせる前提です。
[owner/repo] の週次 Issue トリアージを、確認を求めず最後まで自律実行してください。
【バジェット】1回で触る Issue は新規分類・状態確認あわせて最大 30 件。
list_issues は最小回数で。上限に達したら残りを "deferred" として番号のみ報告。
【Step 1: 新着の新規分類(上書き禁止)】
過去7日以内に作成され、type: ラベルが一つも無い open Issue を対象に、
次の境界で type と priority を新規付与する。既存ラベルがある Issue は触らない。
- feature=今は無い振る舞いの追加 / improvement=既存の振る舞いを速く・楽にする
- bug=仕様どおり動かない / question=使い方・設定の問い合わせ / docs=記述の誤り・改善
- critical=データ損失・課金・ログイン不能で回避策なし / high=主要操作が阻害(回避策あり)
- medium=あると便利・影響軽微 / low=時間があれば
【Step 2: 既分類の揺れ検知(付け替えはしない)】
type: が既に付いた Issue を読み、上の境界に照らして分類が違うと判断したものは
付け替えず、"reclassify-candidate: #番号 現ラベル→提案ラベル" として列挙のみ。
【Step 3: 長期未対応の状態確認(冪等)】
90日以上更新が無く status: triage が未付与の open Issue を最大10件、status: triage を付与。
コメントは、本文に "<!-- cowork-triage:stale-check -->" を含むコメントが
既にある Issue にはつけない。無いものだけに確認コメント+当該マーカーを付ける。
【Step 4: レビュー待ち PR の確認(操作なし)】
open でレビュアー未アサインの PR を番号とタイトルだけ列挙。
【末尾アサーション】
report に必ず含める: 新規分類した件数(type別) / reclassify-candidate件数 /
状態確認した件数 / deferred件数 / 使用したおおよそのAPI呼び出し数。
deferred>0 の場合は「次回の先頭から再開」と明記。
このプロンプトの肝は、四つの Step がすべて現在状態を読んでから動く点と、書き込みを伴う操作(コメント・付与)に必ずスキップ条件が付いている点です。スケジュール実行の組み方そのものは、Cowork スケジュールタスク自動化の実践に基本形があります。
三ヶ月回して分かった、効いたことと限界
良かったのは、何よりラベルの揺れが止まったことです。「上書き禁止+候補リスト」に変えてから、本番ラベルが勝手に書き換わる事故はゼロになりました。月曜のレポートで reclassify-candidate を二〜三件だけ目視で直す運用は、想像していたより軽く、続けられています。重複コメントもマーカー方式にしてから一度も再発していません。
限界も正直に書きます。自動分類は、境界事例では今も人間の判断に及びません。「バグ報告に見える機能要望」のような Issue は、決定表をもってしても揺れます。だからこそ付け替えを自動化せず候補出しに留めたわけで、ここは仕組みで完全に消すより「軽く人が見る前提」で設計するのが現実解だと感じています。もう一つ、コミュニティが活発な公開リポジトリでは自動コメントが冷たく映ることがあります。この仕組みは、コントリビューターの少ない個人開発・小規模リポジトリにこそ向いています。
最後に一つだけ。まだ無人化していないなら、今日試すのは Step 2 の「揺れ検知(付け替えはしない)」だけで十分です。既存の分類を読んで、おかしいと思うものを番号で挙げさせる。これは何も書き換えないので安全で、しかも自分のラベル基準がどれだけ曖昧だったかが一目で分かります。そこから一つずつ、冪等な書き込みへ広げていけば、無人でも崩れないトリアージに着実に近づいていけるはずです。