TL;DR(このノードは何をする・専門語ゼロ): 「この決定にどれくらいの工数・お金がかかるか」を本文中に粗くでも書いてもらうための関所。書いてあれば通す。書いていなければ差し戻す。ただし「不可逆な害」が確定している起案だけはコスト未記載でも先へ通す。Light 起案は最初から免除。AI は呼ばず、本文を正規表現で見るだけの単純な処理。

実装: drp/src/nodes/cost_gate.ts プロンプト: なし (LLM 呼出なし) テスト: drp/test/cost_gate.test.ts (純関数 decideCostGate / hasCostEstimate) マイグレーション: drp/migrate-v13-cost-gate.sql (cost_gate_verdict / cost_gate_ms 列) 基盤 ADR: ADR-0088 (コスト試算の義務化) / ADR-0158 (triage から独立ノードへ分離) / ADR-0157 (ABC スクリーン)


1. 役割と位置づけ

Standard 以上の起案にコスト試算の数値が 1 つ以上書かれているかを機械的に検査し、欠落していれば差し戻す。

  • 解決する課題: コスト試算なしの ADR が量産され「決めた後で工数膨張」が頻発する状況の防止 (ADR-0088)。
  • 設計思想: 差戻し型ゲートだが fail-open ではない。LLM を呼ばずコード合成だけで判定するため、判定の揺らぎゼロ・コスト増分ゼロ。
  • 由来: ADR-0088 が当初 triage ノード内部のゲートとして稼働していたが、ADR-0157 で価値スクリーン (ABC) が triage と pregate の間に入った結果、不可逆 harm (A) はコスト未記載でも先に進めたい という要件 (#safe 穴) が生じ、ADR-0158 で abc_screen の後段へ独立ノード化して切り出した。

本ノード化により、cost gate は triage の ADR-0091 ゲートよりに位置する。cost-missing と ADR-0091 違反を同時に持つ起案は ADR-0091 が先に発火する (旧は cost-missing 優先)。両者とも pre-gate-block 差し戻しのため起案者への情報損失はなく、構造欠落を先に示すのは精度向上方向。


2. フロー図

flowchart LR
    AS[abc_screen] --> CG[cost_gate]
    CG -->|PASS / A_AWARE_PASS / EXEMPT_LIGHT / SKIP| P[problem_space_pregate]
    CG -->|BLOCK| E[END 差戻し]

3. トリガー条件

条件挙動
abc_screen を通過して遷移実行
state.costGateVerdict が既に確定済何もしない (再入時の冪等)
環境変数 COST_GATE_ENABLED='false'SKIP を返して素通し (緊急停止用)

スキップ条件は緊急停止のみ。それ以外は無条件で評価される。


4. 入力 (State)

State フィールド用途
triageMode'Light' なら免除。null'Standard' 扱い (triage 旧挙動と同則)
contextコスト試算の有無を正規表現で検査する対象
abcVerdict'A' (不可逆 harm) ならコスト未記載でも通過させる
costGateVerdict既存値があれば冪等で no-op

外部入力:

  • env.COST_GATE_ENABLED'false' で SKIP

5. 処理ロジック

1. 冪等性: state.costGateVerdict が確定済なら何もしない (return {})
2. decideCostGate({ enabled, mode, hasCost, abcVerdict }) を純関数で評価
   - enabled=false                          → SKIP
   - mode='Light'                           → EXEMPT_LIGHT
   - hasCost=true                           → PASS
   - hasCost=false & abcVerdict='A'         → A_AWARE_PASS (#safe 穴対策)
   - hasCost=false & 非 A                   → BLOCK
3. BLOCK のみ rejected=true / needsAdr=false / rejectionReasonCode=COST_MISSING /
   triageRejectKind='pre-gate-block' を立てて差し戻し
4. A_AWARE_PASS は costGateNote にコスト試算を促す note を載せて通過
5. 各分岐で構造化ログ (cost_gate_skipped / cost_gate_a_aware_pass / cost_gate_block) を出力

コスト試算検出 (hasCostEstimate):

  • 単位付き数値 (0.5 人日 / $10 / 5 万円 / 2 hrs) を 1 つでも含めば PASS
  • または「コスト / 工数 / 予算 / effort / cost / budget」と数値が 15 文字以内に隣接していれば PASS
  • 「コスト高」「予算面」「工数懸念」など形容詞的修飾は false positive 抑制のため除外
  • 2026-05-31 精度改善で距離を 40→15 文字に縮小し節境界をまたぐ誤マッチを抑制

6. LLM 設定

LLM 不要 (コード合成のみ)。判定は決定論的な純関数 decideCostGate と正規表現ベースの hasCostEstimate だけで完結する。LLM/subrequest の増分はゼロ。


7. 副作用

なし。構造化ログ (event: 'cost_gate_skipped' | 'cost_gate_a_aware_pass' | 'cost_gate_block') のみ出力。


8. 出力 (State)

PASS / EXEMPT_LIGHT / SKIP (通過のみ)

{ costGateVerdict: 'PASS' | 'EXEMPT_LIGHT' | 'SKIP' }

A_AWARE_PASS (通過 + コスト試算を促す note)

{
  costGateVerdict: 'A_AWARE_PASS',
  costGateNote: '[コスト試算 推奨] 不可逆 harm (A) と判定したため審査は継続します...',
}

BLOCK (差戻し)

{
  costGateVerdict: 'BLOCK',
  needsAdr: false,                                  // triage 旧コストゲートと同じ telemetry 整合
  rejected: true,
  rejectionMsg: '[コスト試算欠落] Standard 以上の ADR 起案では...',
  rejectionReasonCode: 'COST_MISSING',
  triageRejectKind: 'pre-gate-block',
}

costGateNote は webhook ノードが PR 本文へ転記する。


9. 分岐 (次ノード)

.addEdge('abc_screen', 'cost_gate')
.addConditionalEdges('cost_gate', (s) => s?.rejected ? END : 'problem_space_pregate')
rejected次ノード
true (BLOCK)END (差戻し)
false (PASS / A_AWARE_PASS / EXEMPT_LIGHT / SKIP)problem_space_pregate

10. エラー時の挙動

エラー無し (deterministic)。外部依存は環境変数 1 つだけで例外を投げる経路がない。COST_GATE_ENABLED が未定義 (undefined) なら有効扱い、'false' の文字列のみで無効化される。


11. 既知の弱点・運用注意

  • A_AWARE_PASS の運用監視 (撤退条件): A 免除率を週次集計し、4 週連続 50% 超で A 免除自体を無効化する (ADR-0158 §Confirmation)。A 判定への偏りで「A だからコスト要らない」が常態化するリスクを抑える。
  • コスト試算検出の粒度: 「粗くてよい・数値 1 つでよい」(ADR-0088 §決定) のため、$0.5 だけでも PASS する。質を上げる判定ではなく「書いてある/書いてない」を最低限機械化するだけ。質は Gate 4 Scoring と人間レビューに委ねる。
  • 形容詞的修飾の blacklist 漏れ: 「コスト懸念があるが採用」のような数値なし表現は false 判定で意図通り。一方、「コスト 5 億円規模になり得る」のような誇張表現も PASS してしまう (悪用は想定せず、悪用検出は人間レビュー)。
  • gate 順序の入替えに伴う reason_code 帰属: 旧 triage 内コストゲート時代は cost-missing が ADR-0091 違反より先に発火していた。本ノード化で順序が逆転したため telemetry の reason_code 集計でこの境目に注意。
  • 緊急停止の徹底: COST_GATE_ENABLED='false' は素通しになるため、設定変更時は SKIP 率を観測する。

12. テストケース

drp/test/cost_gate.test.ts (vitest workerd プール) で純関数 2 つを検証:

ID観点期待
TC-01環境変数 OFFSKIP
TC-02Light モードEXEMPT_LIGHT
TC-03コスト試算ありPASS
TC-04コスト試算なし × abcVerdict='A'A_AWARE_PASS
TC-05コスト試算なし × 非 ABLOCK
TC-06hasCostEstimate の正規表現 golden (単位パターン / 隣接マッチ / 形容詞除外)true / false が期待通り

13. 過去の設計判断ログ

日時変更経緯
ADR-0088 採択triage 内部に起案前ゲートとして実装コスト未記載 ADR の量産抑止
2026-05-31hasCostEstimate の距離を 40→15 文字へ縮小・形容詞修飾を blacklistADR-0094 Phase A テスト中に発覚した cross-section false positive 修正
ADR-0157 採択価値スクリーン (ABC) を導入A (不可逆 harm) をコスト未記載でも先に進めたい要件が浮上
ADR-0158 採択triage から独立ノード化し abc_screen の後段へ配置A 免除 (A_AWARE_PASS) を追加し #safe 穴を塞ぐ。triage の挙動は逐語維持

14. 関連リンク

  • 前ノード: 13_abc_screen.md
  • 次ノード: 12_problem_space_pregate.md
  • 関連 ADR:
    • ADR-0088 — コスト試算の義務化
    • ADR-0157 — ABC 守りの価値スクリーン
    • ADR-0158 — triage から独立ノードへ分離
  • マイグレーション: drp/migrate-v13-cost-gate.sql
  • 設定: wrangler.toml [vars] COST_GATE_ENABLED (緊急停止)