• Status: Accepted
  • Mode: Standard
  • Kruchten Type: Property/Executive
  • Scope: platform
  • Implementation Status: Phase A 完了 (本番稼働, PR #1226: LLM 呼出前 short-circuit + audit カラム + migrate-v6 適用 + 本番デプロイ + スモーク 2 経路確認) / Phase B (maxRetries=1) 未着手 (Phase A 1 週観測後に判断、main 領分)
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-06-01 10:09
  • 承認日時 (JST): 2026-06-01 (v1 通常 flow Standard Score 49/50 通過 + auto-PR #1223 merged。v2 強化版は Cross-Validation reject (48/50) のため v1 シンプル版を採用、v2 で表面化した懸念は実装時 follow-up PR で吸収)
  • Deciders: [email protected] (単独)

コンテキスト

§1.1 背景

ADR-0094 Phase B/C 検証中 (2026-06-01) に、Gate0 triage で not-adr-worthy 系 (typo 修正 / 軽微変更 等) の短文草案について LLM retry が常態化していることを発見した。本来 1-3 秒で完了すべき triage 判定が 23-30 秒 (ChatModel maxRetries=3 フル消費濃厚) を要しており、起案者 UX とコストの双方を悪化させている。

§1.2 現状 (As-Is)

PR #1216 で実装した event=triage_llm_invoke_complete log の retry_suspicion フィールド (>=20000ms で "high") により、以下を観測:

  • typo 系 draft の triage_ms: 22903ms / 24082ms / 30747ms / 23446ms — n=4/4 で 22-30 秒 (100% retry 疑い)
  • pre-gate-block 系 (cost-missing / placeholder-remaining): 3556ms / 4843ms — 単発で正常 (3-5 秒)
  • Pipeline-completed (web UI Phase B キャッシュ skip): 31-895ms (median 50ms、n=60)
  • 実ログ例: {"event":"triage_llm_invoke_complete","model":"gemini-flash","llm_ms":23002,"retry_suspicion":"high"}

Gemini Flash 自体の typical latency は 1-3 秒。typo 系の短文・確信度低い is_adr_worthy=false 判定で LLM 応答 JSON malformed → ChatModel maxRetries=3 (ADR-0081) で 3 回 retry → 20-30 秒 という構造的問題と推定。

§1.3 課題

起案者にとって「ADR 対象外」表示まで 25 秒以上待たされる UX 劣化、LLM call コストも 3 倍 (1 回 → 3 回 retry)。queue concurrency=1 環境 (ADR-0066) では 1 起案あたりの占有時間が伸び、後続 queue throughput も低下。

§1.4 制約・要件

  • triage 判定の正確性 (false positive) を維持
  • 他 gate (socratic / scoring 等の heavy operation) の ADR-0081 retry 戦略には影響を与えない
  • プロンプト変更を伴わない (ADR-0042 prompt lifecycle の重い評価を回避)
  • PR #1216 observability 実装との整合を維持し、効果を SQL/log で自動集計可能にする
  • short-circuit 閾値は環境変数化し、即座に調整・無効化できる

§1.5 目標 (To-Be)

typo 系 triage_ms を 23 秒 → 1 秒以下 (>=95% 削減)、retry_suspicion=high 件数を Phase A/B 適用後 90% 削減。Non-Goals: プロンプト改善 (案 E、別 ADR)、モデル変更 (案 D)、ADR-0081 全体の retry policy 改訂。

決定

2 層対策を Phase 分割で同時導入する。Phase A: triage LLM 呼出前に early short-circuit を追加。context の Grapheme クラスタ数が TRIAGE_SHORT_CIRCUIT_MAX_CHARS (デフォルト 80) 以下で、ADR-worthy 識別子 (## H2 セクション / URL / 環境変数名 / 関数名等の技術識別子) を含まない草案は is_adr_worthy=false で early-return する。Phase B: triage 専用に createLlm(... maxRetries: 1) を適用し、他 gate には影響させない。両 Phase は独立 PR・48 時間以上の観測間隔で段階リリースする。

判断基準 (Decision Drivers)

3.1 評価軸

#重要度 (係数)案件特有の解釈
1#reliable[Must] (×2.0)triage 判定の正確性を維持 (false positive 抑制が最優先)、retry 削減後も transient error への耐性を確保
2#efficient[High] (×1.0)typo 草案の triage_ms を 23 秒 → 1 秒以下 (>=95% 削減)、LLM コストを 3 回 → 1 回または 0 回
3#operable[High] (×1.0)起案者 UX 改善 (早期応答)、観測継続性 (PR #1216 ログとの整合)
4#maintainable[Medium] (×0.5)heuristic ルールの単純さ、prompt 変更を伴わない、ADR-0081 全体に波及しない
5#flexible[Medium] (×0.5)short-circuit 閾値を環境変数化、Phase B のみ rollback 可能

K.O. criterion: Must 軸 (#reliable) score < 3 は不採用。

3.2 評価軸 × 案スコア表

係数採択案 (A+B)案 B 単独 (maxRetries=1 のみ)案 C 単独 (short-circuit のみ)案 D (モデル変更)案 E (プロンプト改善)
#reliable×2.044434
#efficient×1.053443
#operable×1.053433
#maintainable×0.544423
#flexible×0.543423
加重和 (正規化)0.8930.7000.8200.5870.687
K.O. 通過 (Must ≥3)

検討した代替案 (Alternatives Considered)

  • 採択案 (Phase A short-circuit + Phase B maxRetries=1): 80 字以下 + ADR 識別子なしで LLM 呼出前 early-return + triage 専用 maxRetries=1。PR #1216 の retry_suspicion=high を直接削減、コスト + 待ち時間の 2 軸で改善。
  • 案 B 単独 (maxRetries=1 のみ): triage 専用に maxRetries=1 適用、short-circuit なし。却下理由: typo 草案でも LLM 呼出は走り 8-10 秒、起案者の待ち時間は 25 秒 → 8 秒に短縮されるが依然不十分。LLM コストも 1 回分かかる。
  • 案 C 単独 (short-circuit のみ): 80 字以下を LLM 呼出前 short-circuit、maxRetries は ADR-0081 設定 (3) のまま。却下理由: short-circuit を抜けた transient error 系 retry が残る (n=2/4 のサンプルで短文以外でも retry 可能性、観測継続)。Phase B が安全網として有用。
  • 案 D (モデル変更: Gemini Flash → Claude Haiku): triage 専用に Claude Haiku を使用。却下理由: LiteLLM gateway / KV prompt active pointer / コスト体系の連動が必要、影響範囲大。Gemini Flash の typical latency (3-5 秒、Phase B cache hit で <1 秒) は十分高速で、問題は retry 経路のみ。
  • 案 E (プロンプト改善で確信度向上): triage prompt を改訂し is_adr_worthy=false の確信度を高め retry を抑制。却下理由: プロンプト変更は ADR-0042 prompt lifecycle で慎重な評価 (推定 1-2 週) が必要、本 ADR の即時性 (起案者 UX 劣化が継続中) と合わない。Phase A merge 後 8 週以内に retry_suspicion=high の根本原因レポートを作成し、必要なら別 ADR として起案 (将来 backlog)。

影響 (Consequences)

§5.1 正の影響 (Good)

  • typo 系草案の triage_ms 23 秒 → 1 秒以下 (>=95% 削減)、起案者 UX 大幅改善
  • LLM コスト削減: typo 起案 1 件あたり 3 LLM call → 0 LLM call (short-circuit hit) または 1 call (Phase B maxRetries=1 hit)、年 ~100 件で $5-15/年削減 ($0.05-0.15/起案)
  • Pipeline 全体の throughput 向上 (queue concurrency=1 環境で 1 起案あたりの占有時間短縮)
  • 観測継続: PR #1216 の retry_suspicion=high 件数が Phase A/B 適用後 0 に近づく想定、対策効果を SQL で自動集計可能

§5.2 負の影響 (Bad)

Short-circuit 過剰拒否 (false positive): 80 字以下で ADR-worthy な草案を early-return するリスク。対策: (1) ## H2 セクションを含む草案は除外、(2) URL / 環境変数名 / 関数名等の技術識別子を含む草案は除外、(3) 閾値を環境変数 TRIAGE_SHORT_CIRCUIT_MAX_CHARS で可変化 (デフォルト 80)、(4) short-circuit hit 時に event=triage_short_circuit log で観測可能化、誤検知時は閾値を絞れる。

多バイト文字 (日本語) 文字数カウント方式の曖昧性 (盲点 #1): JavaScript String.length は UTF-16 コードユニット数を返すため、日本語タイトルが視覚的に長くても length=25 程度で short-circuit に捕捉される可能性。対策: 判定単位を Grapheme クラスタ数 (Intl.Segmenter 使用) に統一し、ADR 本文・環境変数ドキュメント・コード内コメントに「閾値 80 = Grapheme クラスタ 80 個以下」と明記。単体テストに日本語草案・絵文字混在草案を必ず含める。

maxRetries=1 削減で transient error 失敗率上昇: ADR-0081 maxRetries=3 が想定する Gemini Flash side 5xx / rate limit / network blip での自動回復が無効化。対策: (1) triage 専用に限定、他 gate (socratic / scoring) の ADR-0081 設定は維持、(2) maxRetries=1 で失敗時は throw ではなく is_adr_worthy=false, rejection_reason='triage_error' として graceful degradation させ、queue concurrency=1 環境 (ADR-0066) で consumer 停止 / dead-letter 化を回避 (盲点 #4)、(3) 4 週後の failure 率 > 1% なら maxRetries=2 へ rollback (撤退条件 #2)。

audit_runs のデータ汚染リスク (盲点 #2): short-circuit 由来と LLM 由来の triage_reject_kind='not-adr-worthy' が区別不能だと、将来の triage 精度評価・モデル選定 (案 D/E) が汚染される。対策: audit_runs テーブルに triage_decision_source ('llm' | 'short_circuit') カラムと triage_short_circuit_reason カラム (例: length=45,no_h2,no_url) を追加し、Phase A 適用前後のデータを区別可能・説明可能にする。false positive 判明時に該当起案を再 triage する管理コマンドを撤退条件 #1 の代替アクションに追加。

n=4 サンプルの統計的脆弱性と根本原因未解明 (盲点 #3): 同一時刻帯・Gemini Flash 一時的 rate limit 期間の偶発サンプルである可能性を排除できていない。short-circuit 適用後は JSON malformed の根本原因が永遠に不明のまま残り、80 字超の軽微変更草案で同様の retry 多発が起きた際に whack-a-mole に陥るリスク。対策: Phase A/B 実装前に既存 audit_runs から文字数分布と LLM 判定結果のクロス集計 (n≥30) を実施し、80 字以下かつ ADR-worthy=false の実績割合・false positive 候補件数を定量化。LiteLLM gateway ログで「1 回目 call の latency」と「2-3 回目 retry の latency」を分離し JSON malformed 発生を直接確認。案 E (プロンプト改善) の検討期限を Phase A merge 後 8 週以内と明示。

Phase A/B 同時 merge による原因切り分け困難 (盲点 #5): 両 Phase が同一 PR・連続 PR で merge されると、本番障害時に short-circuit 誤検知か maxRetries=1 failure か即座に判断できない。対策: Phase A と Phase B を別 PR・別 deploy として最低 48 時間の観測期間を挟んでリリース。event=triage_short_circuit (Phase A) と event=triage_llm_invoke_complete, retry_count (Phase B) を別系統のログとして集計する SQL クエリを事前準備。rollback 手順を Phase 単位で文書化。

false positive のサイレント蓄積 (盲点 #6): short-circuit hit 草案は LLM を呼ばないため retry_suspicion ログが発生せず、PR #1216 既存監視では捕捉されない。対策: wrangler tail または Logpush で event=triage_short_circuit を専用クエリとしてアラート化し、hit 率が週次で 20% 以上変動した場合に通知。short-circuit hit 草案の先頭 50 文字をサンプリングログに含め週次で目視確認する運用手順を追加。Confirmation KPI に「Phase A false positive 率の週次自動集計クエリ」を明示。

§5.3 中立・トレードオフ

  • ADR-0081 (LLM 耐障害性) との関係 (盲点 #7): 本 ADR は ADR-0081 の triage 特例として位置付け、他 gate の retry policy は不変。ADR-0081 §決定の「retry 3 回」が全 gate 統一の意図か gate 別 override を許容する設計かは ADR-0081 原文を参照し、本 ADR 実装時に引用・明記する。統一意図だった場合は本 ADR が特例として override する根拠 (light operation の 1-3 秒 vs heavy operation の 5-30 秒という latency profile 差異) を追記。
  • 将来の監査・説明可能性 (盲点 #8): チーム拡大・OSS 化・外部監査時に「LLM を呼ばずに文字数ヒューリスティックで ADR 不要と判断」する仕組みの透明性が問われる可能性。triage_short_circuit_reason カラム (盲点 #2 対策と統合) で判定根拠を構造化記録し、将来の監査証跡を担保。
  • short-circuit hit 率の予測難 (本セッション 4/4 だが起案者の typo 投入頻度に依存)、初回観測は Phase A merge 後 1 週間サンプリング
  • Phase A の heuristic は本質的に false positive と false negative の trade-off、閾値設計が次のレビューポイント
  • LLM 側 (Gemini Flash) が将来安定化した場合、Phase B maxRetries=1 は no-op 化、撤退コストゼロ

コスト試算

作業工数 (人日)工数 (h)Phase
事前: audit_runs クロス集計 (文字数 × LLM 判定、n≥30)0.251.5事前
Phase A: triage.ts に short-circuit heuristic 追加 (Grapheme クラスタ数判定 + 技術識別子除外 + log + reason 構造化)0.63.6A
Phase A: 環境変数 TRIAGE_SHORT_CIRCUIT_MAX_CHARS の wrangler.toml 配線 + ドキュメント (単位明記)0.150.9A
Phase A: audit_runs に triage_decision_source / triage_short_circuit_reason カラム追加 (migration)0.21.2A
Phase B: createLlm で triage 専用 maxRetries=1 引数を受け取れるよう拡張0.251.5B
Phase B: triage.ts で maxRetries=1 を明示指定 + graceful degradation (throw → is_adr_worthy=false)0.150.9B
動作確認 (Phase A: 80 字以下 / 以上 / 技術識別子有無 / 日本語 / 絵文字の 6 シナリオ、Phase B: typo + cost-missing で retry 観測)0.352.1A/B
合計約 1.95 人日~12 h

金額換算: 個人開発、自工数のみ、直接金銭支出 0 円。LLM コスト削減効果: 年 $5-15/年削減

撤退条件 (Rollback Plan)

#判定指標期限 / 検知方法代替アクション
1Phase A short-circuit 適用後、起案者から「ADR 対象だったのに ADR 不要判定された」報告 1 件以上、または週次 triage_short_circuit_reason サンプリングで false positive 発見起案者報告 / audit_runstriage_decision_source='short_circuit' 週次手動レビュー (先頭 50 文字サンプリング)TRIAGE_SHORT_CIRCUIT_MAX_CHARS を 80 → 50 → 30 段階縮小、または short-circuit 完全 disable (Phase A rollback、Phase B のみ維持)。該当起案を再 triage する管理コマンドで past data も復旧
2Phase B maxRetries=1 適用後 4 週で triage failure 率 > 1% (event=triage_error ログを直接集計)4 週後の wrangler tail / Logpush 集計maxRetries=2 に rollback、または ADR-0081 の retry 戦略を見直し
3retry_suspicion=high 件数が Phase A/B 適用後も削減せず (目標 90% 削減未達)4 週後の wrangler tail / Logpush 集計仮説崩壊、別原因 (Gemini Flash 側鈍化 / プロンプト問題) として案 E (プロンプト改善) or 案 D (モデル変更) へ移行。8 週以内に根本原因レポート作成
4Pipeline 全体 throughput が短縮されない (queue concurrency=1 環境で 1 起案あたり所要時間が改善せず)月次 audit_runs duration_ms 集計Phase B のみ revert (Phase A short-circuit は維持)、原因切り分けへ

Confirmation

  • 検証手段:
    1. Phase A e2e: 80 字以下 typo 草案 (日本語・絵文字含む) を CI route で fire → event=triage_short_circuit, hit=true, reason='length=45,no_h2,no_url' ログ確認、triage_ms < 100ms (LLM 呼出スキップ)、triage_reject_kind='not-adr-worthy' (ADR-0094 整合維持)、audit_runs.triage_decision_source='short_circuit' 記録確認
    2. Phase A regression: 80 字以上または ## H2 含む草案で従来通り LLM 経由判定 (regression なし)、日本語 80 Grapheme クラスタ ちょうどの境界ケーステスト
    3. Phase B e2e: short-circuit を抜けた typo 草案で retry_suspicion=high が消滅 (llm_ms < 5000 期待)、failure 時 graceful degradation 動作確認 (throw されず triage_error ログ + is_adr_worthy=false で返る)
    4. 対策効果指標: 4 週後 wrangler tail で retry_suspicion=high 件数を Phase A/B 適用前後で比較、目標 90% 削減
    5. ADR-0081 不変性: socratic / scoring 等の他 gate で retry 3 回設定が維持されることを createLlm 呼出箇所 grep で確認
    6. false positive 監視: event=triage_short_circuit 専用ログクエリで週次 hit 率変動 20% 超アラート、先頭 50 文字サンプリングを週次目視
  • 実行頻度: 修正 PR マージ時の e2e、週次 short-circuit サンプリング目視、月次 audit_runs サンプリング (50 件)、4 週後の retry_suspicion 削減率集計、Phase A merge 後 8 週で根本原因レポート作成判定
  • 違反時対応: 撤退条件 (§撤退条件) 発動
  • 観測可能 KPI: typo 草案の triage_ms p50 (目標 ≤ 1000ms、許容上限 5000ms)、retry_suspicion=high 件数 / 月 (目標 ≤ 2、許容上限 10)、Phase A short-circuit hit 率 (目標 ≥ 80% of typo drafts、許容下限 50%)、Phase A false positive 率 (目標 0%、許容上限 1%、週次自動集計)、triage failure 率 (目標 < 0.5%、許容上限 1%、event=triage_error 直接集計)

参照: Pipeline 審査履歴 (2026-06-01 実行時, 通常 flow)

本セクションは Decision Pipeline (LangGraph TS on Cloudflare Workers) で本 ADR 草案を通常 flow (retroactive=false) で審査した結果のログ。v1 (Phase A short-circuit + Phase B maxRetries=1 同時) が Score 49/50 で通過し、auto-PR #1223 (Proposed) として生成された。その後、構造的強化を狙った v2 (Phase 0 観測追加 + Phase A/B 順次リリース + 8 findings mitigation) を再投入したが、Cross-Validation で critical 3 件 reject (Score 48/50) となったため、v1 のシンプル版を採用し、v2 で表面化した懸念は実装時の follow-up で吸収する方針とした。デフォルトでは折りたたまれており、 をクリックで展開する。

📋 Pipeline 審査履歴 (採用 v1: Score 49/50 通過、v2 強化版: 48/50 reject) — クリックで展開

Gate 0-4 結果 (採用 v1 実行)

  • Gate 0 Triage: needsAdr=true / triageMode=Standard
  • Gate 1 Socratic: 盲点 8 件検出 (#1 多バイト文字数カウント / #2 audit_runs データ汚染 / #3 n=4 サンプル脆弱性・根本原因未解明 / #4 maxRetries=1 の consumer 停止・dead-letter / #5 Phase A/B 同時 merge の切り分け困難 / #6 false positive のサイレント蓄積 / #7 ADR-0081 との関係 / #8 監査・説明可能性)。全て §5.2 影響範囲・§撤退条件・§Confirmation に mitigation 反映済。
  • Gate 2 Consistency: ADR-0081 (retry 戦略の triage 特例) / ADR-0073 (seed regression) / ADR-0094 (triage 意味論分離) / ADR-0042 (prompt lifecycle・本 ADR は code レイヤーのみ) / ADR-0066 (async queue throughput) と並立・矛盾なし。Supersede なし。
  • Gate 3 Parallel Review: 指摘は §影響範囲・撤退条件に反映済。
  • Gate 4 Scoring: 49 / 50(Critical 閾値 45 + 4pt 余裕)。採点軸の詳細は §3.2 評価軸 × 案スコア表(採択案 A+B の正規化加重和 0.893)を参照。

v2 (強化版) が Cross-Validation reject となった理由

v2 は Phase 0 観測フェーズ追加 + Phase A/B の順次リリース + 8 findings の追加 mitigation で構造的強化を図ったが、Cross-Validation で critical 3 件を指摘され Score 48/50 で reject:

  1. false positive 検知が観測依存: short-circuit 誤検知の検知が週次目視サンプリングに依存し、自動 fail-safe がない。
  2. フォーマット drift: 追加した観測ログ/カラムのスキーマが既存 audit_runs と drift しうる。
  3. dead-letter サイレント消失: maxRetries=1 graceful degradation 時に dead-letter 化した起案が無音で失われるリスク。

採用判断 (v1)

  • v1 (PR #1223, Score 49/50) は既に Pipeline 自動生成済で内容も十分(盲点 #1-#8 を §5.2 で網羅 mitigation 済)。
  • v2 の critical 3 件は 実装フェーズの follow-up PR で吸収可能(queue 詰まり・サイレント消失・観測自動化)と判断し、v1 を Accepted とする。
  • 本 curation PR で Status: Proposed → Accepted + 承認日時記載 + nav 登録 (A.09.5.57) + 本審査履歴セクション追記を反映。

参照 (References)

  • 関連 ADR:
    • ADR-0081 (Decision Pipeline LLM 耐障害性 retry/Promise.allSettled/JSON parse): 本 ADR は 0081 の retry 戦略を triage gate に限定して maxRetries 削減する特例。他 gate (socratic / scoring 等の heavy operation) の retry 設定は不変。並立・特例
    • ADR-0073 (Gate0 triage seed regression test): Phase A short-circuit は seed 再現性に影響しない (LLM 呼出前なので決定論的判定)。Phase B maxRetries=1 は retry seed の non-determinism を排除、0073 の前提を強化。並立・補完
    • ADR-0094 (triage rejection 経路の意味論分離): 本 ADR は ADR-0094 Phase B/C 検証中に発見した retry 問題への対応。triage_reject_kind='not-adr-worthy' の retry_suspicion=high を直接対象とする。並立・補強
    • ADR-0042 (Prompt Lifecycle): プロンプト変更を伴わない (案 E プロンプト改善は将来別 ADR)。本 ADR は code レイヤーのみで実装。並立・独立
    • ADR-0066 (Pipeline async queues + DO): Phase B maxRetries 削減は queue consumer 内の triage 実行時間を短縮、queue throughput 向上に寄与。triage failure 時の graceful degradation 設計も ADR-0066 の queue consumer エラーハンドリングと整合。並立・補強
    • Supersede / Conflict なし
  • 関連 PR/Issue: PR #1216 (triage_llm_invoke_complete log + retry_suspicion 実装) / PR #1223 (本 ADR 草案 v1 の Pipeline 自動生成・Proposed) / 本 curation PR (Proposed → Accepted + §審査履歴 + nav 登録)
  • 外部資料: -
  • 将来 backlog: 案 E (プロンプト改善) は本 ADR Phase A/B 適用後 4-8 週で効果不十分な場合に別 ADR 起案。retry_suspicion=high が依然多発する場合は案 D (モデル変更) も検討対象