ADR-0164: 起案者向け intake サイト (/intake) を chat UI から分離し、triage 同期実行 + 静的起案前 lint で起案者の責任境界を画定する
- Status: Proposed
- Mode: Standard
- Kruchten Type: Existence/Executive
- Scope: platform
- Implementation Status: Not Started
- 起案者: [email protected]
- 起案日時 (JST): 2026-06-22 17:14
- 承認日時 (JST): -
- Approver Role: platform
- Approver Who: [email protected]
- Driver: [email protected]
- Consulted: Decision Pipeline AI 審査 (Gate 0-4)
コンテキスト
§1.1 背景
DRP の起案体験は https://drp.bizlp.dev/chat (chat.html・3631 行) に集約されており、1 画面に「起案する人格」と「審査する人格」の機能が同居している。起案フォーム / Socratic / triage 結果 / Step indicator (Gate1-15) / partial-results-card / 3 モデルレビュー / PR 作成ボタン / /audit/* 系がすべて同じ SPA 内に存在する。ADR-0157 で C 萎縮 (起案中に verdict プレビューを見て自己検閲) が問題視されており、構造的な対処を求められている。
§1.2 現状 (As-Is)
- chat.html = 3631 行の単一 SPA に起案者向け要素と審査者向け要素が同居
- telemetry_records 直近 30 日 (n=212) の triage_ms 計測: p50 = 47ms / p95 = 431ms (LLM 経路 n=136) / p99 = 27,833ms / max = 40,679ms
- triage の LLM 経路は 95% が 0.5 秒以内、99% が 28 秒以内
scripts/lib/adr-lint-rules.mjs::checkTriageGate(text, mode)が純粋関数として既に存在 (placeholder 残存 / コスト試算 H2 / 撤退条件 H2 / Confirmation KPI の 4 ルール)- 現状 graph 構成は 4 種:
buildPreGraph/buildGraph/buildCheckpointedGraph/buildGraphWithWebhook
§1.3 課題
- C 萎縮 (ADR-0157): 起案中に ABC verdict プレビュー・補強策プレビューが画面に出ると、起案者は「これは C 判定になりそう」と推測して自己検閲し、本来書くべき問題提起を取り下げる。
- JTBD 境界の不明瞭: 起案者の functional ゴール (「ADR 起案として土俵に乗る形」になっているか確定する) と、審査者の functional ゴール (「価値ある起案か裁く」) が UI 上で区別されていない。代表取締役単独運用でも人格切替の認知コストが発生し、Jr 起案者導入時には Jr が審査画面の verdict / 補強策を見て萎縮するリスクが顕在化する。
§1.4 制約・要件
- Cloudflare Workers の wall-time 上限 30 秒以内に
/intake/commitが完了すること - 現状 4 graph (
buildPreGraph/buildGraph/buildCheckpointedGraph/buildGraphWithWebhook) の構成は変更しない (drift 防止) checkTriageGateが Worker ランタイムから呼出可能であること (Node.js 固有 API 非依存)- ADR-0142 の
problem_space_pregateLLM ゲートの挙動を変えない - ADR-0120 (Workflows 移行) の checkpointed graph に触らない
§1.5 目標 (To-Be)
/intake 起案者専用 SPA を新設し、起案者の責任境界 (「土俵に乗ったかの確定」) を UI 上で完結させる。verdict プレビュー・補強策プレビュー・3 モデルレビュー・PR 作成ボタンは intake.html から排除し、/chat は審査者ビューとして維持する。Non-Goals: hostname 分割 (draft.drp.bizlp.dev) / LLM 由来 reject の通知設計 / chat.html の起案者要素削除 (別 PR) / triage 非同期化。
決定
drp worker に /intake/* route (Hono sub-app) を追加し、起案者専用 SPA drp/public/intake.html を新設する。POST /intake/commit で (a) buildPreGraph().invoke() を同期実行、(b) scripts/lib/adr-lint-rules.mjs::checkTriageGate(text, 'Standard') を純粋関数として呼出、(c) 両方 pass で DRAFTS_KV.put(id, payload, overwrite=1) を実行する。graph 定義は一切変更せず、LLM 由来 reject (problem_space_pregate の suspect_multiple 判定) は審査側に残す。env flag INTAKE_SITE_ENABLED=false で即時無効化できる構造を入れる。
intake.html に含む画面: 起案フォーム (author/title/context/options) / Socratic 質問パネル (triage 通過に必要な盲点ループのみ) / triage 結果カード (mode/reason/起案前ゲート逐語引用) / KV draft 一覧 (100 件上限の見える化) / 保存完了画面 (draft_id + 「審査キューに入れる」CTA)。
intake.html に含めない画面: Step indicator (Gate1-15) / partial-results-card / 3 モデルレビュー結果 / PR 作成ボタン / cancel ボタン / /audit/* 系画面 / ABC verdict プレビュー / 補強策プレビュー。
route 構成:
GET /intake→drp/public/intake.htmlPOST /intake/triage→buildPreGraph().invoke()同期実行 (triageOnly 経路)POST /intake/drafts/GET /intake/drafts/DELETE /intake/drafts/:id→ 既存/drafts系を sub-app 再 exportPOST /intake/commit→ triage + 静的 lint pass を条件に KV put し{draft_id, triage_mode, review_url}を返す/chat,/runs,/audit/*,/debug/*→ 既存のまま審査者ビュー
判断基準 (Decision Drivers)
3.1 評価軸
| # | 軸 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|
| 1 | #safe (起案者入力テキスト保護) | Must (×2.0) | /intake/commit 中の wall-time 30s 超過率 = 0% (本番反映後 4 週で確認・triage p99 = 28s + 静的 lint + KV put が 30s 内に収まる前提) |
| 2 | #reliable (graph drift 防止) | High (×1.0) | graph 構成は本 ADR で変更しない (4 graph 維持・新ノード追加なし) |
| 3 | #maintainable (変更局在化) | High (×1.0) | 新コードは drp worker の /intake/* route + intake.html + checkTriageGate 呼出 1 行に閉じる / graph.ts / nodes/*.ts / mainGraphDefinition は無変更 |
| 4 | #safe (起案者 reject 体験の予測性) | Medium (×0.5) | 静的 lint 由来 reject (placeholder / コスト試算 H2 / 撤退条件 H2 / Confirmation KPI) を OK 押下時に即座に画面で返す / LLM 由来 reject 件数 ≤ 静的 lint 由来 reject 件数 を 4 週で確認 |
| 5 | #operable (将来移行容易性) | Medium (×0.5) | C 案 (triage 非同期化) への移行が /intake/commit handler 書換のみで完結する設計を維持 |
K.O. criterion: Must 軸 (#safe wall-time) score < 3 は不採用。
3.2 評価軸 × 案スコア表
| 軸 | 係数 | 採択案 (B + 静的 lint) | 案 A (両方同期) | 案 C (triage 非同期化) | 案 D (chat 内 mode 切替) |
|---|---|---|---|---|---|
| #safe wall-time | ×2.0 | 4 | 2 | 5 | 4 |
| #reliable graph drift | ×1.0 | 5 | 2 | 4 | 5 |
| #maintainable 変更局在化 | ×1.0 | 4 | 3 | 2 | 2 |
| #safe reject 予測性 | ×0.5 | 5 | 5 | 5 | 3 |
| #operable 移行容易性 | ×0.5 | 5 | 3 | 5 | 2 |
| 加重和 (正規化) | 0.880 | 0.520 | 0.840 | 0.660 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ | ✓ | ✓ |
案 A は wall-time Must 軸で K.O.。案 C は wall-time が最良だが状態管理複雑度で #maintainable が低い。採択案は加重和最大かつ K.O. 通過。
検討した代替案 (Alternatives Considered)
- 案 A (両方同期):
buildPreGraphにproblem_space_pregateを追加し、起案者の OK 押下時に triage + 起案前ゲートを LLM 2 回で同期実行 — 不採用。graph drift リスク (problem_space_pregateがbuildPreGraphとmainGraphDefinitionの 2 箇所に重複・edge 更新漏れ事故源) / wall-time 30s 超過確率上昇 (p99 = 28s + LLM 1 回追加) / 起案テキスト消失の不可逆事故源。 - 案 B 素 (triage のみ同期、静的 lint なし): 静的 lint プリチェックを入れない — 不採用。静的に検出可能な reject (placeholder / コスト試算 H2 / 撤退条件 H2 / Confirmation KPI) まで審査キュー投入後に流れて LLM コストの無駄。
checkTriageGateが既に純粋関数として存在するのに使わない理由がない。 - 案 C (triage 非同期化): OK 押下で即 KV 仮 put、triage はバックグラウンド起動、polling で結果待ち — 不採用。状態管理の複雑度が一気に上がる (KV status 列追加 / 仮 put → triage 起動 → 結果書戻し → 確定 put の 4 操作の race 整合 / polling endpoint / UI 状態遷移 / 仮 put cleanup)。triage p99 = 28s で wall-time 30s に収まっており現時点で過剰技術。将来 triage が重くなった時点で
/intake/commitの handler 書換で移行可能。 - 案 D (chat.html 内 mode 切替): route を分けず chat.html 内で
?mode=intakeのような query で起案者ビューと審査者ビューを切替 — 不採用。3631 行の単一 SPA が更に肥大化。「同居解消」という本 ADR の目的を達成できない。起案者と審査者の bundle が分離されないので Jr 起案者導入時のセキュリティ境界が引けない。
影響 (Consequences)
§5.1 正の影響 (Good)
- 起案者の C 萎縮 (ADR-0157) を構造的に塞ぐ (verdict プレビューが画面にない)
- JTBD 境界が UI に反映され、起案者の責任範囲 (= 土俵に乗ったかの確定) が一発で完了する体験になる
- 静的 lint 由来 reject が起案者画面で即時返答され、審査キューに流れる reject 件数が減る (LLM コスト削減・小)
- 将来の hostname 分割 (
draft.drp.bizlp.dev) への移行余地が Hono sub-app として残る - chat.html (3631 行) の段階的縮退の足掛かりになる
§5.2 負の影響 (Bad)
/intake/commitの handler に LLM 同期呼出 + 純粋関数呼出が増える (worker code 量増)- LLM 由来 reject (
problem_space_pregateの suspect_multiple) の通知設計が宙ぶらりんになる (本 ADR スコープ外で別タスク化) - 兼務人格 (起案者と審査者を同一人物が兼ねる) の場合、
/intakeと/chatの行き来が認知コストとして増える - 兼務人格が
/intake/commit後に/chatを開くと同一 draft_id に対して triage が 2 回実行され、コスト試算「LLM 増分 = 0」が崩れる可能性 → §採用したい方針で KVtriage_doneフラグを持たせる設計を採用 (PR 1 スコープ) - hostname 分割が完了するまでは Jr 起案者がブラウザ履歴・URL 共有で
/chatに到達して verdict プレビューを閲覧する経路が残る (Jr 導入時期までは代表取締役単独運用で許容)
§5.3 中立・トレードオフ (Neutral / Trade-offs)
- triage の LLM モデルが将来 thinking モード等で重くなると wall-time 30s に常時引っかかる可能性 → 撤退条件で監視 → C 案へ移行
- LLM 由来 reject の通知が漏れると起案者が「土俵に乗った」気で離脱した draft が放置される → 通知設計を本 ADR 受理後 4 週以内に別 ADR で確定 (PR 1 merge 条件)
- 暫定措置として
/intake/commitのレスポンスと保存完了画面に「LLM ゲートは審査時に再チェックされ差し戻しの可能性がある」旨の警告 banner を PR 1 スコープに含める - 「静的 lint が多数派 reject」仮説の事前検証 (telemetry_records からの実績比率集計) が PR 1 着手前に必要
撤退条件 (Rollback Plan)
- wall-time 超過: 本番反映後 4 週連続で
/intake/commitの wall-time 30s 超過率が 0.5% を超過 → C 案 (triage 非同期化) へ移行する別 ADR を起案 - LLM reject 多数派化: 本番反映後 4 週連続で
(LLM 由来 reject 件数 / 全 reject 件数) > 50%→ 静的 lint の網羅性が不十分 → 静的ルール拡張をscripts/lib/adr-lint-rules.mjsで実施 - 起案者離脱率上昇: 本番反映後 4 週で
/intake起案完了率 (=POST /intake/commit成功数 /GET /intakeセッション数) が pre-ADR 比で 20% 以上低下 → intake.html の UX 設計に問題あり → 別 PR で UX 修正 - KV 100 件上限到達: KV draft 一覧が 100 件上限に達して新規起案
DRAFTS_KV.putがエラーを返す → LLM 由来 reject 通知設計 ADR の緊急受理 + 放置 draft の cleanup batch を別 PR で投入 - 無効化手順: env
INTAKE_SITE_ENABLED=falseをwrangler secret putで即時設定 →/intake/*は 410 Gone を返し起案者は/chatへ誘導 (rollback コスト = secret 操作 1 つ)
コスト試算
- 実装工数: 1.5 人日
- PR 1 (intake 新設・本 ADR スコープ): 1 人日 (
/intake/*route +intake.html~1500 行 +POST /intake/commithandler + 静的 lint 呼出 + intake.html 用 mocked e2e +intake_commit_ms観測列追加 migrate-v18) - PR 2 (chat.html 縮退・本 ADR スコープ外・別 PR): 0.5 人日 (chat.html から起案フォーム・Socratic・draft セレクト削除 /
real-llm-e2eの admin merge 運用)
- PR 1 (intake 新設・本 ADR スコープ): 1 人日 (
- 運用コスト増分:
- LLM コール増分: 0 (triage と
problem_space_pregateの LLM 呼出は審査経路でも起案経路でも同じ回数 / 起案前 reject 件数だけ LLM コストが減る方向)。ただし兼務人格の二重 triage 防止のため KVtriage_doneフラグを PR 1 で導入する前提 - subrequest 増分: 0 (worker 内で完結・新規外部呼出なし)
- KV 書込増分: 0 (既存
DRAFTS_KV.putと同じ操作) - 観測 SQL 増分:
/intake/commitの reject 内訳 (静的 lint 由来 vs LLM 由来) を月次で観測する SQL を 1 本追加 (drp/queries/adr-E-intake-reject-breakdown.sql)
- LLM コール増分: 0 (triage と
- 撤退時コスト:
/intake/*route の handler 削除 + intake.html 削除 + chat.html の起案者要素は無変更 (PR 2 を merge していない前提) ので素直に rollback 可能。env flagINTAKE_SITE_ENABLED=falseで即時無効化 (secret 操作 1 回)
Confirmation
- 検証手段:
- wall-time 実測:
/intake/commitの wall-time 分布を week 単位で観測 (telemetry_records にintake_commit_ms列追加 /migrate-v18-intake-commit-timing.sql) し、p99 が 30s を下回ることを確認。受理前提: staging 環境でcheckTriageGate + KV put + Hono routing + JSON シリアライズの合算レイテンシを 1,000 回計測し、triage p99 (27,833ms) との合算が 29 秒以内に収まることを PR 1 merge 条件とする。29 秒を超える場合は C 案を初期設計として再採用するか ADR 採用方針を再起案する - 静的 lint 仮説検証: 既存 telemetry_records から
checkTriageGate4 ルール別 reject 件数とproblem_space_pregateLLM reject 件数の実績比率を PR 1 着手前に集計し、「静的 lint 由来が多数派 (≥ 50%)」仮説の事前検証結果を ADR Implementation Status に追記 - reject 内訳観測:
/intake/commitの reject 内訳 (reject_source ∈ {static_lint, triage_llm, problem_space_pregate_llm}) を月次で観測し、static_lintが多数派 (≥ 50%) であることを確認 - C 萎縮緩和観測: ADR-0157 の C 萎縮観測 SQL (
abc_verdict='C'件数 /abc_confidence=1率) と本 ADR の起案完了率の相関を観測。観測タイミングは hostname 分割 ADR 受理後 に変更 (現時点では「C 萎縮の構造的解消の第一歩」と位置付け、Jr 起案者の/chat到達経路が残る限り完全解消は計測不能) - chat.html 起案経路の縮退観測: chat.html の起案フォーム経由の draft 投入数が本 ADR 反映後に減少することを確認 (= 起案者が
/intakeを使っている証跡) - PR 1 merge 条件: (a) staging wall-time 1,000 回計測 ≤ 29 秒、(b)
adr-lint-rules.mjsの import ツリー静的解析で Node.js 固有 API (fs/path/process) 非依存を確認 (依存があれば Worker 向けサブモジュール分離)、(c) LLM 由来 reject 通知設計 ADR の起案完了、(d) 暫定 banner (「LLM ゲートは審査時に再チェックされ差し戻しの可能性がある」) を保存完了画面に実装、(e) KVtriage_doneフラグによる二重 triage 防止実装
- wall-time 実測:
- 実行頻度: 週次 (本番反映後 8 週まで) / それ以降は月次 telemetry レビューに同期
- 違反時対応: 撤退条件のいずれかに該当した時点で env flag
INTAKE_SITE_ENABLED=falseで即時無効化 → ADR 本文 Implementation Status に「撤退・原因」を追記 → 再評価後に C 案 (非同期化) や A 案 (両方同期) を再検討
参照 (References)
- 関連 ADR:
- ADR-0071 (socratic 盲点検出エンジン化 + wall-time 制約 graph 分割): 準拠。
buildPreGraph(triage のみ軽量 graph) を起案者サイトの境界として再利用。buildPreGraph構成は変更しない - ADR-0120 (decision-pipeline Cloudflare Workflows 移行 / durable execution): 準拠。
buildCheckpointedGraph(Workflows 専用) に触らない。intake サイトは worker 同期経路で完結し Workflows には乗らない - ADR-0142 (問題空間プリゲート受付): 準拠。
problem_space_pregateの LLM 部分は審査側 main graph に残し挙動不変。静的部分 (checkTriageGate) のみ起案者側で先行実行 - ADR-0157 (ABC スクリーン): 補強。C 萎縮の構造的解消の第一歩として intake.html に verdict プレビューを含めない設計が ADR-0157 §Confirmation の補強となる
- ADR-0158 (コストゲート独立ノード): 独立。cost_gate ノードに触らない。cost_gate は審査側で従来どおり動作 (A_AWARE_PASS / BLOCK)
- ADR-0159 (mas 専属クローン化・main orchestrator 化): 準拠。本 ADR は drp 専属クローンの実装スコープ。main 役割 (orchestrator) には ADR 受理と関連 docs/_internal 更新を委譲
- ADR-0163 (事業軸ドメイン分類基準 4 種): 準拠。本 ADR は ADR-0163 §2.1 定義表の DRP ドメインに該当 (Decision Pipeline の挙動を変える:
/intake/*route +buildPreGraph同期実行 +checkTriageGate起案者側先行実行)。frontmatterbusiness: drpを ADR-0163 §2.6 運用に従い明示 - ADR-D (緩やかコンプラ違反 cross-rule・data-waiting): 独立。abc_decide.ts の判定ロジック側に対し本 ADR は intake UI 側
- ADR-0071 (socratic 盲点検出エンジン化 + wall-time 制約 graph 分割): 準拠。
- 関連 PR/Issue: PR 1 (intake 新設) / PR 2 (chat.html 縮退・別 PR・admin merge 運用) / 別 ADR (LLM 由来 reject 通知設計・本 ADR 受理後 4 週以内・PR 1 merge ブロッカー)
- 外部資料: telemetry_records 直近 30 日 triage_ms 集計 (n=212, LLM 経路 n=136) / Cloudflare Workers wall-time 上限 30 秒仕様