01. Triage Node (Gate 0)
TL;DR(このノードは何をする・専門語ゼロ): 提案された技術判断を受け取り、まず「これはわざわざ記録に残す価値があるか」を最初にふるい分ける入口の関所。残す価値がなければ「普通の変更説明だけでよい」と差し戻す。残す価値があれば、その判断の重みを軽い・普通・重大の 3 段階に仕分けし、後続のチェックがどれだけ厳しく見るかを決める。
実装:
drp/src/nodes/triage.tsプロンプト設計ドキュメント:prompts/01_triage.mdプロンプト SSoT (KV デプロイ):drp/prompts/production/gate0-triage/prompt.md(ADR-0042 Type 1) テスト:drp/test-tc.mjs(TC-01〜05) 基盤 ADR: ADR-0019 / ADR-0020 (判定基準根拠) / ADR-0033 (モデル選定)
1. 役割と位置づけ
パイプライン最上流の「そもそも ADR〔Architecture Decision Record=技術判断の記録〕を書くべきか?」を判定する関所。書くべき場合は Light / Standard / Critical〔モード=判断の重みを軽い・普通・重大の 3 段階に仕分けする区分〕のモードを決め、下流ノードのスコア閾値・並列レビュー数を制御する。
- 解決する課題: 軽微な変更 (typo・色調整・自明なバグ修正) に対して ADR ライフサイクル一式を回すと起案コスト・LLM コストが膨らみ、起案者が「ADR を書きたくない」心理になる。Triage〔トリアージ=ADR にする価値があるかの振り分け〕で対象外を弾くことでパイプラインの摩擦を最小化する。
- 設計思想: 差戻し型ゲート〔Gate=通過条件を満たすか判定する関門〕。
is_adr_worthy=falseで END に飛ばし、起案者に「通常の PR 概要欄に書いてください」と返却する。判断に迷ったら Standard を既定値とする保守的設計 (ADR-0020)。 - 学術的根拠: Nygard 5 領域 / Chen et al. ASR / Zimmermann 5+2 / Bezos Type 1/Type 2 (詳細は ADR-0020)。
2. フロー図
flowchart LR
START([POST /draft or /chat/start]) --> T[triage]
T -->|needsAdr=true| S[socratic]
T -->|needsAdr=false
rejected=true| END([END
「ADR 対象外」を起案者に返却])
具体例で見る処理の流れ
判定ルールの詳細は §5 にありますが、ここでは「実際に提案を出すとどう処理されるか」を 3 つの身近な例で示します。
例 1:誤字の修正 → その場で「対象外」
- 出した提案: 「README の誤字を 1 か所直したい」(短い 1 文で、コード名や URL を含まない)
- どう動くか: 記録に残すような判断要素がなく、文章も短いので、AI を呼ぶ前に入口で弾かれます。
- 結果: 「ADR 対象外です。通常の PR 概要欄に書いてください」と差し戻し。審査はここで終わります。
例 2:データの保存先を変える(試算あり)→ 通過
- 出した提案: 「請求データの保存先を新しいデータベースに変えたい。理由は〇〇。月◯円のコストがかかる見込み」
- どう動くか: 将来に影響する判断なので「記録に残すべき(ADR 対象)」と判定。重みは「普通(Standard)」。コストの見込みも書かれているので入口の条件も満たします。
- 結果: 次の段階(見落としチェック)へ進みます。
例 3:例 2 と同じ提案だが試算を書き忘れ → いったん差し戻し
- 出した提案: 例 2 と同じ。ただしコストの見込みを書いていない。
- どう動くか: 「記録に残すべき・重みは普通」までは例 2 と同じ。ただし「普通」以上はコストの見込みが必須なので、入口の条件チェックで止まります。
- 結果: 「コストの見込みを書き足してください」と差し戻し。追記して出し直せば、例 2 と同じように通過します。
ポイント: 止まり方には 2 種類あります。例 1 は「そもそも記録に残す話ではない」、例 3 は「記録に残す話だが、出し方が条件を満たしていない」。差し戻しのメッセージもこの 2 つで変わります。
3. トリガー条件
| エントリポイント | 起動グラフ | 説明 |
|---|---|---|
POST /draft (フォーム再送信型) | buildGraphWithWebhook | 常に最初に実行 (START → triage) |
POST /chat/start (チャット型) | buildPreGraph | triage + socratic のみの軽量グラフ |
POST /chat/run (チャット型・full) | buildGraph | webhook を除く full pipeline |
スキップ条件: なし。Triage は全エントリポイントで必ず実行される。
4. 入力 (State)
| State フィールド | 型 | 必須 | 用途 |
|---|---|---|---|
title | string | 任意 | 起案タイトル。未入力なら suggested_title で補完 |
context | string | 必須 | 背景・目的の自由記述 |
options | string | 任意 | 検討した代替案 |
プロンプトに渡す形式:
タイトル: {title} ← title が空なら省略
背景・目的:
{context}
検討した代替案: ← options が空なら省略
{options}
5. 処理ロジック
0. 冪等性: state.needsAdr が既に決定済なら何もしない (return {})
1. ADR-0095 Phase A short-circuit: LLM 呼出前に typo / 軽微変更系の短文かつ ADR 識別子なしを検知したら
即 needsAdr=false で差戻し (LLM を呼ばない。triageDecisionSource='short_circuit',
triageShortCircuitReason=理由, triageRejectKind='not-adr-worthy')
- **閾値**: `context` の長さが `TRIAGE_SHORT_CIRCUIT_MAX_CHARS` (既定 80) 以下のときのみ対象。`"0"` で機能無効化 (緊急ロールバック)。
- **長さは Grapheme クラスタで計数** (`String.length` でなく `Intl.Segmenter('ja')`)。日本語・絵文字混在草案の視覚長乖離を防ぐ (§盲点 #1)。
- **ADR 識別子があれば短文でも素通し** (LLM へ): `## H2` 見出し / URL / `ENV_VAR` (UPPER_SNAKE) / `` `code span` `` / `func(` 呼出 / `module.func` / camelCase / snake_case のいずれか。
- short-circuit hit は `event: 'triage_short_circuit'` ログ (reason + 先頭 50 字 sample) で false positive を週次監視 (§盲点 #6)。
2. loadLlmParams(env, 'gate0-triage', { temperature: 0.2, seed: 42 }) → createLlm(..., maxRetries=3)
3. loadPrompt(env, 'gate0-triage', FALLBACK_PROMPT) で system prompt を取得し、user message に起案入力を埋めて invoke
4. safeParseLlmJson<TriageResult>() で JSON 抽出 (parse 失敗時はフォールバック値)
5. consistency override (P1 fix): is_adr_worthy と mode の自己矛盾 (mode 付きなのに対象外等) を検知して整合化
6. is_adr_worthy=false → needsAdr=false, rejected=true, triageRejectKind='not-adr-worthy' で END
7. is_adr_worthy=true → 起案前ゲートを順に評価 (Light は免除):
① ADR-0088 コスト試算ゲート: Standard 以上で context にコスト試算が無ければ (!hasCostEstimate)
rejected=true, rejectionReasonCode=COST_MISSING, triageRejectKind='pre-gate-block' で差戻し
② ADR-0091 3 ルールゲート: Standard 以上で no-placeholder-marker 等の 3 ルール違反なら
最初の 1 件で rejected=true, rejectionReasonCode=..., triageRejectKind='pre-gate-block' で差戻し
8. 全ゲート通過 → needsAdr=true, triageMode/triageReason をセット、title 未入力なら suggested_title で補完
起案前ゲート (①②) は「ADR 対象だが品質要件未達」での差戻しで、
triageRejectKind='pre-gate-block'。 一方 short-circuit / is_adr_worthy=false は「そもそも ADR 対象外」でtriageRejectKind='not-adr-worthy'(ADR-0094 Phase C で意味論分離)。
5.1 処理フローチャート
triageNode() 内部の分岐 (src/nodes/triage.ts)。緑=needsAdr=true で socratic へ、赤=rejected=true で END。差戻しは どの triageRejectKind か で起案者への意味が変わる。
各ボックスにマウスを乗せると処理の説明が表示されます。図の元ソース: 01_triage_flow.d2(d2 --layout=elk で SVG を再生成)。
処理説明(番号は図のボックスに対応)
| 番号 | 処理名 | 処理内容 | 分岐・次の処理 |
|---|---|---|---|
| 1 | 冪等判定 | state.needsAdr が既に決定済みかを確認 | 済 → 何もしない(no-op で終了)/ 未 → 2 |
| 2 | 早期判定(short-circuit) | 短文(≤ MAX_CHARS)かつ ADR 識別子なしを検知し、LLM を呼ばずに弾く | 該当 → 差し戻し「ADR 対象外」(not-adr-worthy)/ 非該当 → 3 |
| 3 | AI 判定 | gemini-flash にプロンプトを渡し、is_adr_worthy / mode / reason を JSON で取得 | → 4 |
| 4 | 自己矛盾チェック(consistency override) | 「対象外なのに mode あり」等の矛盾を検知し、ADR 対象に補正 | → 5 |
| 5 | ADR 要否判定 | is_adr_worthy で記録に残すべきかを確定 | 否 → 差し戻し「ADR 対象外」/ 要 → 6 |
| 6 | 重みを決定 | mode を確定(未指定は Standard を既定値) | → 7 |
| 7 | Light 判定 | Light なら起案前ゲートを免除 | Light → 通過(socratic へ)/ Standard・Critical → 8 |
| 8 | コスト試算ゲート(ADR-0088) | context にコスト試算が書かれているか | なし → 差し戻し「品質未達」(COST_MISSING)/ あり → 9 |
| 9 | 3 ルールゲート(ADR-0091) | no-placeholder 等の必須 3 ルールを満たすか | 違反(先頭 1 件)→ 差し戻し「品質未達」/ 満たす → 通過 |
終端(結果): 通過 =
needsAdr=trueで socratic へ。差し戻しは 2 種類 — 処理 1〜5 由来の「ADR 対象外」(not-adr-worthy)と、処理 8〜9 由来の「品質未達」(pre-gate-block)。両者で起案者へのメッセージが変わる。テストとの対応 (§12): TC-01/02 → 「ADR 対象外」(処理 2 または 5)/ TC-03 → Light 通過(処理 7)/ TC-04(Standard)・TC-05(Critical)→ 処理 8〜9 を通過。
読み方: 上から下へ評価し、最初に該当した分岐で終端する。LLM 呼出は short-circuit を抜けた草案のみ (コスト最適化)。起案前ゲート (ADR-0088 / 0091) は
is_adr_worthy=true確定後・Light 免除で順に評価し、最初の 1 件で差し戻す (起案者の段階的修正を促す)。
判定 7 ルール (システムプロンプト gate0-triage より要約):
- ADR (Architecture Decision Record) として記録すべき決定かを判定する。
- ADR 対象は「将来の自分・他人がその決定を覆す/参照する可能性のある、判断要素を含むもの」に限る。
- 以下は ADR 対象外: UI 文言・色・余白の微調整 / typo・コメント・外部挙動不変のリファクタ / 既存パターン完全準拠の CRUD 追加 / 一時デバッグログ・運用回避策 / 修正方針が自明なバグ修正。
- ⚠️ 「実装作業でなく政策/プロセス/運用ルールの確立だから PR で十分」を理由に対象外にしてはならない (2026-06 lint-governance false-negative 修正)。lint ルール / CI ガバナンス / メタデータ規約 / ドキュメント運用ポリシー (鮮度 SLA・verified-as-of/frontmatter 規約・staleness 検知) の確立は ADR 対象 (Standard、precedent: ADR-0051/0053/0057/0058/0088)。対象外は「外部挙動が変わらない些末作業」に限る。
- ADR 対象の場合のモード:
- Light = 内部実装の選択 (ライブラリ採用・命名規則等) で外部 I/F に影響しない。メタ ADR 典型例: テンプレ 1-2 行追加、命名規則微調整、README ガイド節追記、訂正注記 (corrigendum) 追加、ADR テンプレへの節・メタデータ項目追加 (Kruchten Type / Implementation Status / Confirmation 節等)。ただし CI 強制・既存成果物の遡及改訂・運用ポリシー化を伴うものは Light でなく Standard。
- Standard = データモデル変更 / 新規モジュール追加 / 業務フロー変更。メタ ADR 典型例: Pipeline ノードのモデル swap・プロンプト改修、サブワークフロー定義追加 (B1-B6 / C1-C6 等)、lint ルール / CI ガバナンス追加、ドキュメント運用ポリシー確立。
- Critical = 認証・課金・会計仕訳ロジック / 請求・決済・税の検証/計算 / 外部 API 契約 / セキュリティ / マイグレーション。メタ ADR では原則 Critical にしない (例外: ADR 全体構造の根本変更・連鎖 Supersede・業務フロー全体の段構成確定)。
- 【排他的除外リスト — 外見が小さくても Light にしてはならない】 (ADR-0102 で追加)。以下は修正量が小さく見えても 必ず Standard (該当すれば Critical):
- (汎用) データスキーマ/データモデル変更 (列追加・型変更・マスタ定義) / 外部 API・外部サービス連携の追加変更 (契約・認証フロー含む) / 認証・認可の変更 / 課金・会計仕訳ロジックの変更
- (本プロジェクト固有の高ステークス領域) Decision Pipeline のノード/グラフ/ゲート挙動の変更 / triage 判定基準・閾値の変更 / telemetry・監査スキーマ (D1 カラム等) の変更
- うち会計仕訳・認証・課金・外部 API 契約・マイグレーションは Critical を検討。
- 判断に迷ったら Standard を既定値 (Light は影響が真に限定的かつ排他的除外リスト非該当の場合のみ。Critical は明示的該当根拠がある場合のみ)。
- 整合性 (必須):
is_adr_worthy=falseならmodeは必ず null。逆に mode を Light/Standard/Critical に決めたらis_adr_worthyは必ず true (モードが決まる=ADR 対象)。両者の不一致を出力してはならない (P1 fix を prompt 側でも担保。コード側 consistency override は §5-5 / §10)。
5.2 シーケンス図(1 起案の時系列)
ノード内部の分岐 (§5.1) を、起案者・処理・AI のやり取りの時系列で見ると次のとおり。差し戻しは 2 種類 (対象外 = not-adr-worthy / 品質未達 = pre-gate-block)。
図の元ソース: 01_triage_sequence.d2(d2 --layout=elk で SVG を再生成)。
6. LLM 設定
| 項目 | 値 | 根拠 |
|---|---|---|
| モデル | gemini-flash (MODELS.triage) | 分類タスクのみで deep think 不要・最安・最速 (ADR-0033) |
| temperature / seed | KV gate0-triage params (fallback 0.2 / 42) | 判定の一貫性・再現性。実効値は KV 優先 (loadLlmParams) |
| thinking budget | 適用外 (gemini-flash) | thinkingBudget state は gemini-pro 系のみ |
| 期待出力 | JSON object (4 フィールド) | system prompt で形式強制 |
| コスト目安 | 1 起案あたり < 0.01 USD (入力 1〜2KB) | gemini-flash は他モデルの 1/10〜1/20 |
| レイテンシ | 1〜3 秒 | パイプライン全体 (30〜60 秒) に対し無視できる |
7. 副作用
なし。LiteLLM Gateway 経由の LLM 呼出のみ。GitHub API / KV / Webhook には触れない。
8. 出力 (State)
ADR 対象 (is_adr_worthy=true)
{
needsAdr: true,
triageMode: 'Light' | 'Standard' | 'Critical',
triageReason: string,
title?: string, // suggested_title (元の title が空の場合のみ補完)
}
ADR 対象外 (is_adr_worthy=false / short-circuit)
{
needsAdr: false,
triageMode: null,
triageReason: string,
rejected: true,
rejectionMsg: 'ADR 対象外です。通常の PR 概要欄に書いてください。\n理由: {reason}',
triageRejectKind: 'not-adr-worthy', // ADR-0094 Phase C
triageDecisionSource?: 'short_circuit', // short-circuit 経路のみ
triageShortCircuitReason?: string, // short-circuit 経路のみ
}
起案前ゲート差戻し (ADR 対象だが品質未達: ADR-0088 / ADR-0091)
{
needsAdr: false,
triageReason: string,
rejected: true,
rejectionMsg: string, // コスト試算欠落 / 3 ルール違反の指示
rejectionReasonCode: 'COST_MISSING' | ..., // REJECTION_REASON_CODES
triageRejectKind: 'pre-gate-block', // ADR-0094 Phase C
}
LLM 生 JSON (内部 TriageResult)
{
"is_adr_worthy": true,
"mode": "Standard",
"reason": "新規データストア採用は将来の運用コスト・移行性に長期的な影響を与えるため ADR が必要。",
"suggested_title": "ADR ストアの永続化先として DynamoDB を採用"
}
9. 分岐 (次ノード)
graph.ts の conditional edge:
.addConditionalEdges('triage', (s) => s?.needsAdr ? 'socratic' : END)
needsAdr | 次ノード | 起案者への表示 |
|---|---|---|
true | socratic | Gate 1 の Socratic〔ソクラテス式問答=前提・代替案を問い直して盲点を洗い出す手法〕問診フェーズへ |
false | END | rejectionMsg を返却してパイプライン終了 |
10. エラー時の挙動
ADR-0081 / PR #1087・#1090 で堅牢化済:
| 失敗パターン | 挙動 | 影響 |
|---|---|---|
| LLM タイムアウト / Gateway 不達 | createLlm の maxRetries=3 (exponential backoff) で transient error をリトライ。全失敗時のみ例外スロー | 起案者に 500 系エラー (稀) |
| LLM が JSON 以外を返す | safeParseLlmJson<TriageResult>() がフォールバック値を返し例外を投げない | structured error log に記録 |
is_adr_worthy 等のフィールド欠落 | フォールバック値で補完。consistency override (P1 fix) が mode/is_adr_worthy の自己矛盾も整合化 | 検出可能 (ログ出力) |
11. 既知の弱点・運用注意
- メタ ADR の Critical 化抑制 (2026-04 追加): ADR テンプレ改修・Pipeline 改修が安易に Critical 判定されてパイプラインの並列レビュー数が増えコストが膨らむ問題に対し、システムプロンプトで「メタ ADR は原則 Critical にしない」と明示。
- 判断迷い時のデフォルト変更 (2026-04 追加): 当初は「迷ったら厳しめ (上位モード)」だったが、Standard 想定の起案が次々 Critical に流れて並列レビューコストが膨らんだため、Standard を既定値に変更 (ADR-0020 §改訂)。
- タイトル補完の境界:
suggested_titleは 起案者が title を空で送った場合のみ state を上書きする。明示入力された title は尊重される。 - gemini-flash の JSON ブレ: ごく稀に
```jsonコードブロックで包んで返す → 実装側で正規表現除去で対処済。 - Light の境界判定: 「内部実装の選択で外部 I/F に影響しない」の判定は LLM の解釈に依存。曖昧なケースは Standard に倒される傾向 (TC-03 で確認)。排他的除外リスト (§5 rule 5 / ADR-0102) で Decision Pipeline 変更・triage 基準変更・telemetry スキーマ変更等は LLM の解釈に関わらず強制 Standard に固定。
- lint/CI ガバナンス系の false-negative (2026-06 修正): 「lint ルール確立は実装でなくプロセスだから PR で十分」と LLM が誤判定し ADR 対象外にする事例 (draft
adr-doc-freshness-verified-as-of) が継続したため、(a) プロンプト rule 3 で「制度設計は ADR 対象 (Standard)」を明示、(b)is_adr_worthy=falseかつmode!=nullの自己矛盾をコードで決定的に override (consistency override / §10) の二段で担保。 - short-circuit の false positive 監視: LLM 非呼出のため retry ログが出ない。
event: 'triage_short_circuit'を専用監視し、誤って弾いた草案を早期検知する (閾値はTRIAGE_SHORT_CIRCUIT_MAX_CHARSで調整、"0"で無効化)。
12. テストケース
| ID | 種別 | 内容 | 期待 |
|---|---|---|---|
| TC-01 | triage | README typo 修正 | is_adr_worthy=false |
| TC-02 | triage | UI 背景色変更 (デザインシステム内) | is_adr_worthy=false |
| TC-03 | triage | GAS 日付ライブラリ Luxon 採用 | is_adr_worthy=true, mode=Light |
| TC-04 | triage | 11_mst_account に税効果科目フラグ列追加 | is_adr_worthy=true, mode=Standard |
| TC-05 | triage | Action B 決済日バリデーション緩和 | is_adr_worthy=true, mode=Critical |
実行: node drp/test-tc.mjs --triage-only
注意: テストスクリプトは OpenAI 直接呼出 (LiteLLM Gateway を経由しない)。本番は gemini-flash のため厳密一致ではないが、判定の質傾向は確認できる。
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-05-04 | v0.1 初版 (Phase 1 着手) | Dify Cloud 上で初期検証 |
| 2026-05-04 | v0.2 (reason 80 字 / title 30 字を強制) | TC-03/04/05 で出力長超過 → プロンプトで明示制約 |
| 2026-05-11 | LangGraph TS 移行 | Dify 退役 (ADR-0019)。プロンプト本文は v0.2 を踏襲 |
| 2026-05-12 | gemini-flash に確定 | ADR-0033 でノード別モデル選定。分類タスクは flash で十分と判定 |
| 2026-04 (概算) | メタ ADR の Critical 抑制ルール追加 | ADR 運用改修系が Critical に流れすぎる問題に対処 |
| 2026-04 (概算) | 判断迷い時デフォルトを「厳しめ」→「Standard」に変更 | 並列レビューコスト膨張への対処 |
| 2026-05-31 | ADR-0088 コスト検出の精度改善 | cross-section false positive 修正 (距離 40→15 char、形容詞的修飾を blacklist) |
| 2026-05 | ADR-0094 Phase C: triageRejectKind 意味分離 | 「ADR 対象外 (not-adr-worthy)」と「起案前ゲート差戻し (pre-gate-block)」を区別、UI 矛盾を解消 |
| 2026-05 | ADR-0095 Phase A: LLM 呼出前 short-circuit | typo 系短文の retry 多発 (Gemini Flash 23-30s) を回避。Grapheme 計数 + ADR 識別子ガード |
| 2026-06 | ADR-0102 フェーズ①: 排他的除外リスト追加 (§5 rule 5) | 局所的に見える高ステークス変更 (Pipeline/triage 基準/telemetry スキーマ等) の過小審査を防止 |
| 2026-06 | lint/CI ガバナンス false-negative 修正 | 「制度設計は PR で十分」誤判定を prompt rule 3 + code consistency override の二段で解消 |
14. 関連リンク
- 上位ドキュメント: decision_pipeline/README.md
- プロンプト設計: prompts/01_triage.md
- 次ノード: 02_socratic.md
- State 定義:
drp/src/state.ts - グラフ定義:
drp/src/graph.ts - LLM Gateway:
drp/src/llm/gateway.ts - 関連 ADR:
- ADR-0019 — Dify → LangGraph 移行
- ADR-0020 — Triage 判定基準の学術的根拠
- ADR-0033 — ノード別モデル選定
- ADR-0042 — プロンプト SSoT (KV) ライフサイクル
- ADR-0081 —
safeParseLlmJson<T>()で JSON parse を堅牢化 (PR #1087) - ADR-0085 —
loadPrompt(env, 'gate0-triage', FALLBACK_PROMPT)で KV 経由読込 (PR #1090) - ADR-0103 — 入口統一 (案 C′)・triage 派生フィールド整形を
src/triage/shared_triage.tsに集約
実装更新 (2026-05-28)
triage.ts:JSON.parseをsafeParseLlmJson<TriageResult>()に置換 (PR #1087)SYSTEM_PROMPTをFALLBACK_PROMPTにリネームしloadPrompt(env, 'gate0-triage', FALLBACK_PROMPT)に切替 (PR #1090)ChatOpenAIインスタンスにmaxRetries=3(exponential backoff) を付与
実装更新 (2026-06-03 / 本ドキュメントの実装追従)
- ADR-0095 Phase A: LLM 呼出前 short-circuit (
evaluateShortCircuit_/graphemeCount_/ADR_IDENTIFIER_PATTERNS/ envTRIAGE_SHORT_CIRCUIT_MAX_CHARS)。 - ADR-0094 Phase C:
triageRejectKind(not-adr-worthy/pre-gate-block) +triageDecisionSource/triageShortCircuitReason。 - ADR-0088 / ADR-0091: 起案前ゲート (コスト試算
hasCostEstimate+checkTriageGate3 ルール、rejectionReasonCode)。 - ADR-0102 フェーズ①:
FALLBACK_PROMPTに 排他的除外リスト (rule 5) を追加 — Pipeline ノード/ゲート変更・triage 基準変更・telemetry スキーマ変更等を強制 Standard。 - lint/CI ガバナンス false-negative 修正: prompt rule 3 の制度設計=ADR 対象 明示 +
is_adr_worthy=false×mode!=nullの code consistency override。 - ADR-0103 案 C′ (入口統一): triage 派生フィールドの整形 (chat 応答 / session cache / consumer DO result / telemetry) を
src/triage/shared_triage.tsの共通関数に集約 (handler / consumer が同一定義を呼ぶ・PR #1353)。入口は/chat/start(Web UI 同期 triage 維持) と/runs(CI) の 2 つのまま。triage 内部ロジック (§5 / §5.1) は不変。