ADR-0037: Decision Pipeline に server-side draft staging + lifecycle 管理を追加 — 投函・列挙・自動削除
- Status: Accepted
- Mode: Standard
- Kruchten Type: Existence/Property
- Scope: platform
- Implementation Status: Not Started
- 起案者: [email protected]
- 起案日時 (JST): 2026-05-14 02:17
- 承認日時 (JST): 2026-05-14 02:25
- Deciders: [email protected] (単独)
1. コンテキスト
1.1 背景 (Background)
ADR-0035 で drp/public/chat.html にローカル draft JSON ロードボタンを実装したが、ロード対象はあくまで起案者ローカルのファイル選択方式である。本日 (2026-05-14) の ADR-0036 投入時、ファイル選択ダイアログ (macOS Finder) が /tmp/ を非表示にしており、起案者は cp /tmp/adr0036_draft.json ~/Downloads/ で手動コピーを実施した。
また現在の /tmp/adrNNNN_draft.json 運用には以下の構造的弱点がある:
- 端末横断不可: PC で
python3等で draft を生成しても、tablet / phone から投入できない (ローカルファイルのため) - PR 作成後の残骸: Pipeline /draft 投入で PR が作成されても
/tmp/adrNNNN_draft.jsonは残存、次回起案時に古い draft と新規 draft の混同リスク - 再投入時の整合性管理: /draft が中途失敗した時、起案者は同じ /tmp 上のファイルを再読み込みするが「最新版か旧版か」をファイル内部の context 文字数で判断する手作業発生
- 複数端末での協調不能: 複数の draft を準備して「他端末で確認しながら投入」のワークフローが現状ない
1.2 現状 (Current State / As-Is)
- ADR-0035 (basic + R2/R3/R5) 本日 2026-05-14 01:43 JST に main マージ + 本番デプロイ済
- 本日 2026-05-14 の ADR-0035/0036 投入: ローカルパス起因の手作業 1 件発生 (Downloads コピー)
/tmp/に残存している draft JSON: 2 件 (adr0035_draft.json / adr0036_draft.json、本日の作業で生成)- 同名 draft の再生成: ADR-0035 で 1 回発生 (起案当初のスコープが GitHub 読み込みボタンだったため書き直し)
- ADR-0035 hidden field
loaded-from-file利用率: 未測定 (Pipeline backend が payload に含めるか別作業) - Cloudflare KV の既存利用状況:
drpでSTATE(Socratic session) /RATE_LIMITなどで利用中、新規 namespace 追加に技術ブロックなし
1.3 課題 (Problem)
- ファイル picker の探索コスト: 起案者がファイル選択ダイアログで /tmp 等の非表示パスを毎回探す。本日のように Finder 制約に当たると手動コピーが必要、起案フローが分断される
- 端末横断不可: PC で起案準備 (CLI で /tmp に書き出し) → phone で投入確認 のフローが現状不可能、起案者の作業端末固定化
- PR 作成後の残骸: 投入完了済 draft が /tmp に残り、3 ヶ月後に新規起案で「あれ、これ古いやつ?」と混同。月次クリーンアップ運用も負担
- 失敗時の再投入難: /draft 失敗 (本日 ADR-0035 投入 1 回目の JSON escape error) で起案者は手元の /tmp を再読み込みするが、「Pipeline で何が起きて再投入が必要か」のコンテキストがファイル内部に残らない
1.4 制約・要件 (Constraints & Requirements)
- 既存 ADR-0035 (ローカル draft JSON ロードボタン) を Supersede しない: 補完関係として共存、ローカルファイル投入も継続可能
- 既存 Basic 認証 (chat UI 全体に適用済) で保護、追加認証不要
- Cloudflare KV を新規 binding として追加 (既存 KV namespace と分離して
DRAFTS_KVを作成) - KV 利用料: 100k reads / day 無料枠内、月 10-30 件起案では問題なし
- failed /draft 時の draft は KV に keep (削除しない、起案者が再試行できる状態を維持)
- 実装規模 6-7 時間 (Backend 3 endpoint + frontend プルダウン UI + auto-delete hook)
- draft の機微情報リスク: 既存ローカル draft と同等 (取引先名 / 金額等は事前マスク前提)、KV 側に PII フィルタは別 ADR で検討
1.5 目標 (Goals / To-Be)
- 起案者が draft をアップロード → chat UI のプルダウンに列挙 → 選択 → 4 フィールド一括 populate のフロー確立
- /draft 投入で PR 作成成功時に該当 draft を Cloudflare KV から自動 DELETE (auto-cleanup)
- /draft 失敗時 (Pipeline エラー / PR 作成失敗) は KV に残し、起案者が再試行可能
- 端末横断: PC でアップロード、tablet / phone から投入 (Basic Auth は両方で同じ)
- 残骸 draft 件数: 目標 1 ヶ月平均 ≤ 1 件 (auto-delete が機能)
2. 決定
Cloudflare KV ベースの draft staging を Decision Pipeline に導入する。新規 KV namespace DRAFTS_KV を drp/wrangler.toml に binding 追加し、POST /drafts / GET /drafts / DELETE /drafts/:id の 3 endpoint を src/index.ts に実装する (既存 Basic 認証 middleware を継承)。chat.html に「サーバー draft (KV staging)」プルダウン + アップロード / 読み込み / 削除の 3 ボタンを追加 (ADR-0035 のローカル投入ボタン直下に配置、共存)。/draft 内部に auto-delete hook を追加し、PR 作成成功時のみ payload の draft_id で env.DRAFTS_KV.delete(draft_id) を実行する (failed 時は KV に残し再試行可能)。
2.1 KV binding 追加
drp/wrangler.toml に新規 KV namespace DRAFTS_KV を binding:
[[kv_namespaces]]
binding = "DRAFTS_KV"
id = "<wrangler kv:namespace create で発行>"
2.2 新規 endpoint (drp/src/index.ts)
3 つの endpoint を追加 (いずれも既存 Basic 認証 middleware を継承):
POST /drafts: body ={id, author, title, context, options}の JSON。KVDRAFTS_KV.put(id, JSON.stringify(payload), { metadata: { mtime: Date.now(), title } })。サイズ上限 1 MB、4 キー検証で fail なら 400 返却GET /drafts: KVDRAFTS_KV.list()で全件取得、metadata から[{id, title, mtime}]配列を返却 (mtime 降順)DELETE /drafts/:id: KVDRAFTS_KV.delete(id)を実行、204 返却
2.3 chat UI 拡張 (drp/public/chat.html)
ADR-0035 で追加した「📂 ドラフト JSON を読み込む」ボタンの直下に以下を追加:
<div class="field">
<label>サーバー draft (KV staging):</label>
<select id="draft-select"><option value="">-- 選択 --</option></select>
<button type="button" id="load-server-draft-btn">📥 サーバーから読み込み</button>
<button type="button" id="upload-draft-btn">⬆️ ローカル JSON をアップロード</button>
<button type="button" id="delete-server-draft-btn">🗑️ 削除</button>
</div>
- ページロード時に
GET /drafts→<select>を populate (id + title + mtime 表示) - 「読み込み」: 選択 id を
GET /drafts/:id(実装方法 1 案) またはGET /draftsのキャッシュから取り出し、4 フィールドに自動投入 + hidden fieldloaded-from-file=server:<id>set - 「アップロード」: ローカルファイル選択 → ADR-0035 既存 FileReader で読み →
POST /draftsで KV 投函 → プルダウン再構築 - 「削除」: 確認ダイアログ +
DELETE /drafts/:id→ プルダウン再構築
2.4 /draft 内部の auto-delete hook
/draft の Pipeline 処理完了 + PR 作成成功時に、payload 内の draft_id (request body から取得、optional) が存在すれば await env.DRAFTS_KV.delete(draft_id) を実行。PR 作成失敗・Pipeline エラー時は削除しない。
2.5 判断基準 (重み付き)
- 端末横断 + 自動削除の構造化 (最重要) — 残骸 draft / 古い draft 混同を構造的に防止
- ADR-0035 との共存 (高) — ローカルファイル投入も継続可、Supersede 不要
- 既存認証 + KV インフラの最大活用 (高) — Cloudflare KV / Basic Auth / wrangler の既存パターン踏襲、新規認証層なし
- failed 時の冪等性 (中) — 失敗時は KV に残し、再試行可能
3. 検討した代替案 (Alternatives Considered)
案 A (採用): Cloudflare KV ベースの draft staging (POST/GET/DELETE /drafts) + /draft auto-delete hook + chat.html プルダウン UI
- Good: 端末横断 + 自動削除を構造的に実現、ADR-0035 と共存
- Good: 既存 Cloudflare KV / Basic Auth / wrangler パターン踏襲、新規認証層なし
- Good: failed /draft 時に KV に残し再試行可能、冪等性を維持
- Bad: KV の eventual consistency (最大 60 秒) で端末横断時に列挙遅延の可能性
- Bad: draft が server 経由で外部送信、ローカル保持より機微情報リスクがやや上がる
案 B: 現状維持 (ADR-0035 のローカル draft 投入のみ) — 不採用理由: 実装コストゼロだが、端末横断不可、PR 後の残骸が累積、ファイル picker 探索コストが継続。本 ADR の主目的を全く解決しない。
案 C: drag-and-drop UI のみ追加 (ADR-0035 拡張、server 不要) — 不採用理由: Finder からの直接ドラッグで探索コスト削減できるが frontend 完結のため端末横断不可、auto-delete 不可、本 ADR の主目的 (lifecycle 管理) を満たさない。
案 D: File System Access API (
showDirectoryPicker) で起案者がフォルダを指定 + 自動スキャン — 不採用理由: frontend 完結で KV 不要だが、Chrome 専用 (Safari/Firefox 未対応)、端末横断不可、auto-delete も実現困難。案 E: GitHub Gist にアップロード → Gist URL を chat UI に貼り付け — 不採用理由: GitHub 既存インフラ活用で端末横断可能だが、private Gist でも GitHub への外部送信、PII リスクが KV より高い。GitHub API レート制限の管理も別途必要。
4. コスト試算
| 項目 | 工数 | 金額 |
|---|---|---|
| KV binding + wrangler.toml 更新 + namespace 作成 | 0.5 h | $0 |
| 3 endpoint (POST/GET/DELETE /drafts) 実装 | 1.5 h | $0 |
| /draft auto-delete hook 追加 | 0.5 h | $0 |
| chat.html UI (プルダウン + 3 ボタン + JS) | 1.5 h | $0 |
| ローカル wrangler dev + 本番デプロイ + 動作確認 | 1.5 h | $0 |
| ADR-0037 起案 + Accepted + マージ | 1 h | $3 |
| 合計 | 約 6.5 時間 | 約 $3 (≈ [MASKED:AMOUNT]) |
機会費用換算: 6.5 h × Jr 想定時給 [MASKED:AMOUNT]/h = [MASKED:AMOUNT] + LLM [MASKED:AMOUNT] ≈ [MASKED:AMOUNT] 投資
年間削減効果:
- ファイル picker 探索コスト削減: 月 5-10 件 × 15-30 秒 = 月 1.25-5 分 × [MASKED:AMOUNT]/h × 12 ヶ月 = 年 [MASKED:AMOUNT]-4,000
- 残骸 draft 混同回避 (月次クリーンアップ廃止): 月 5 分 × 12 ヶ月 = 年 1 時間 = 年 [MASKED:AMOUNT]
- 端末横断 (PC 作成 → 移動先で確認 + 投入): 月 1-2 件 × 30 分 × [MASKED:AMOUNT]/h × 12 ヶ月 = 年 [MASKED:AMOUNT]-48,000
- 失敗時再投入の簡略化: 年 5-10 件 × 1-2 分 = 年 5-20 分 = 年 [MASKED:AMOUNT]-1,330
- 合計: 年 [MASKED:AMOUNT]-57,000 削減見込み (投資回収 5-11 ヶ月)
5. 影響 (Consequences)
5.1 正の影響 (Good)
- 端末横断起案が可能になる: PC でアップロード → tablet / phone から Basic Auth で投入確認、起案者の作業端末固定が解消
- PR 作成成功時の auto-delete により残骸 draft が構造的に削減、目標月平均 ≤ 1 件
- ADR-0035 ローカル投入と共存 (Supersede 不要)、起案者がワークフローに応じて選択可能
- 既存 Cloudflare KV / Basic Auth / wrangler パターンを踏襲、新規認証層・新規インフラ不要
- failed /draft 時は KV に draft が残り、再試行が冪等に可能
- Jr エンジニア (2026-10 入社予定) の起案フロー統一感向上
5.2 負の影響 (Bad)
- KV のレート制限 / eventual consistency: Cloudflare Workers KV は最大 60 秒の伝播遅延あり、1 端末でアップロード直後に他端末でリストすると遅延の可能性 (起案フローでは実用上問題なしと判断するが、UX としては気付き要因)
- draft 機密情報の server 経由送信: ローカル draft より外部送信リスクが高まる (既存 Pipeline で /draft body は LiteLLM Gateway 経由で OpenAI/Anthropic に送信済のため追加リスクは限定的だが、PII フィルタ層は本 ADR 範囲外で別 ADR 検討)
- 実装工数 6.5 時間 + 約 [MASKED:AMOUNT] 投資: 短期的に他作業を圧迫
- draft ID 命名規約の未 enforce: ADR 番号 (
adr0037等) を ID にする規約は推奨どまり、衝突リスクや Pipeline slug 採番との不整合可能性が残る (別 ADR で enforcement 検討)
Status / Mode / Scope は 2026-06-11 に遡及追加 (ADR-0031 corrigendum パターン)。出典: Status = 旧形式「## ステータス」節の機械転記 / Mode = 旧 README §既存 ADR 一覧の推定値 (git 履歴) / Scope = ADR-0049 4 層分類の遡及付与 (PR レビューで確定)。
5.3 中立・トレードオフ (Neutral / Trade-offs)
- KV 容量肥大化リスク: failed draft の累積で KV が肥大化する可能性、月次クリーンアップ or TTL 設定で対応 (別 ADR で起案可能性)
- draft schema 拡張 (4 キー以上、例: ADR-0036 で Confirmation セクション追加時): version 管理を別 ADR で起案
- multi-user 化 (Jr 入社後): owner フィールド追加 + UI フィルタを別 ADR で対応
- Cloudflare 課金: KV reads/writes 無料枠 (100k reads/day) 内で月 10-30 件起案は問題なし、影響軽微
5.4 影響範囲 (変更ファイル)
drp/src/index.ts: 3 endpoint 追加 + /draft 内部 auto-delete hook (+80 行)drp/wrangler.toml: KV binding 追加 (+5 行)drp/public/chat.html: プルダウン UI + 3 ボタン + GET/POST/DELETE 呼出 JS (+60 行)drp/.dev.vars(任意): ローカル開発用 KV (wrangler kv:namespace create DRAFTS_KV --preview)
5.5 不変対象
- ADR-0035 ローカル draft JSON ロードボタン (継続稼働、補完関係)
- 既存 /draft / /chat/* エンドポイント (auto-delete hook 追加のみ、既存挙動不変)
- 既存 KV namespace (
STATE/RATE_LIMIT等、新規DRAFTS_KVを分離) - Basic 認証 middleware (既存パターンを継承)
6. 完了条件 (定量メトリクス)
- KV binding 追加済:
grep -c "DRAFTS_KV" drp/wrangler.toml→ >= 1 - 3 endpoint 実装済:
grep -cE "app\.(post|get|delete).*'/drafts" drp/src/index.ts→ 3 - chat.html にプルダウン UI 実装済:
grep -cE 'id="draft-select"|id="upload-draft-btn"|id="delete-server-draft-btn"' drp/public/chat.html→ 3 - /draft auto-delete hook 実装済:
grep -c "DRAFTS_KV.delete" drp/src/index.ts→ >= 1 - ローカル wrangler dev で動作確認: アップロード → リスト → 読み込み → /draft 投入 → PR 作成成功時に DELETE 自動実行を curl + ブラウザで目視
- 本番デプロイ後の動作確認: 実 draft で 1 回投入成功 + auto-delete 確認
- CI markdown-link-check PASS / TypeScript build PASS
7. 撤退条件 (Rollback Plan)
| 判定指標 | 閾値 | 判定タイミング | 対応 |
|---|---|---|---|
| プルダウン UI が使われない | 3 ヶ月後の Pipeline ログ集計でアップロード経由起案が起案総数の 20% 未満 | 3 ヶ月後 Review After | UI 縮退 (削除 or feature flag で hide)、ADR-0035 ローカル投入のみ運用に戻す |
| KV 障害で起案フロー停止 | 月 1 件超 KV エラーで /drafts 失敗 | 月次振り返り | フォールバック (ローカル投入のみ案内) を UI に追加 |
| auto-delete の暴発 | failed /draft なのに削除される事象 | 障害時 | hook を一時無効化、削除条件 (PR 作成成功 + 200 応答) を再点検 |
| KV 容量上限 | 月 100 件超の draft 累積 (auto-delete 不全) | 月次容量監視 | 古い draft の TTL 設定 (例: 30 日) を追加 ADR で起案 |
8. 長期影響
6 ヶ月後の負債化リスク:
- KV 容量肥大化: failed draft の累積で KV が肥大化 → 月次クリーンアップ or TTL 設定で対応
- draft schema 拡張: 4 キー以上のフィールド (例: ADR-0036 で Confirmation セクション追加時) → version 管理を別 ADR で起案
- multi-user 化: Jr 入社後、複数起案者で draft が混在 → owner フィールド追加 + UI フィルタを別 ADR で対応
- PII リスク: KV に取引先名等が混入する可能性 → POST /drafts に PII フィルタ層追加を別 ADR で検討
観測指標 (月次):
- アップロード経由起案率: ADR 起案数のうち KV アップロード経由の割合 (目標 80% 以上)
- auto-delete 成功率: PR 作成成功時の delete 実行率 (目標 100%)
- 残骸 draft 件数 (KV 内): 目標 月次平均 ≤ 1 件 (failed draft のみ)
- 端末横断起案: PC アップロード → 別端末で投入の発生件数
Review After:
- 1 ヶ月後 (2026-06-14): UI 利用率測定、auto-delete 成功率確認
- 3 ヶ月後 (2026-08-14): 撤退条件 (利用率 < 20%) 判定、KV 容量チェック
- 6 ヶ月後 (2026-11-14): Jr 入社後の運用感ヒアリング、multi-user 化要否判定
Confirmation (準拠確認 / Fitness Function)
本セクションは ADR-0036 (Accepted 2026-05-14) で遡及追加された。ADR-0031 パターン (業界標準準拠メタデータ後付け = 誤字修正範疇) に準拠する遡及で本文の意思決定内容は不変。
- 検証手段: 月次レビューで KV draft 利用率 + auto-delete 成功率測定 + 手動 QA
- 実行頻度: 月次
- 違反時の対応: 月次レビューで集計
参照 (References)
- 関連 ADR: ADR-0035 (ローカル draft JSON ロードボタン、本 ADR と共存・補完関係)、ADR-0036 (本日 2026-05-14 投入時に Finder 制約事象が発生)
- 関連 PR/Issue: (要追記)
- 外部資料: Cloudflare Workers KV ドキュメント (eventual consistency 最大 60 秒)、
wrangler kv:namespace createCLI