# v3 アーキテクチャ根本監査 — なぜ修正が終わらないのか

- 作成日: 2026-04-17
- 作成者: Claude Code（自己監査）
- 目的: Codex 1〜4回目のレビュー指摘を俯瞰し、「個別バグ」ではなく「設計レベルの根本原因」を特定する
- ステータス: **Codex 5回目レビュー（アーキテクチャ反論）の入力ドキュメント**

---

## 0. 問題の核心

ユーザーからの指摘:
> 「修正すれば修正するほど、次から次へと修正が必要になるのはなぜでしょうか」

**CC の honest な回答: アーキテクチャ選択が 100店舗規模の商用SaaSに合っていない。**

個別バグを直しても、同じ根本原因から別のバグが生えてくる。これは **モグラ叩き** 状態。

---

## 1. Codex 1〜4回目レビューの俯瞰

### 各回の指摘を「根本原因」でグルーピング

| Codex指摘 | 回 | 根本原因 |
|---|---|---|
| scheduler claim UPDATE race | 1 | **KV/D1 multi-statement critical section は serialize されない** |
| scheduled_at 型比較不整合 | 1 | 周辺（KV/D1の制約対応） |
| 承認POST が非原子的 | 1 | **multi-statement critical section** |
| プロンプトversion 固定 | 1 | 周辺 |
| Cron 間隔 vs retry上限 | 1 | **毎分Cron + LIMIT 5 polling アンチパターン** |
| processing_started_at TEXT比較 | 2 | **D1 TEXT比較・時刻統一 の脆さ** |
| scheduled_at ISO vs datetime() | 2 | 周辺 |
| 承認 single-use 不足 | 2 | **bearer-URL + KV counter アンチパターン** |
| プロンプトversion クライアント申告 | 2 | 周辺 |
| Cron LIMIT 5 | 2 | **Cron polling アンチパターン** |
| handleFailure retry race | 3 | **multi-statement section** |
| confirm_token 発行 in GET | 3 | **bearer-URL がそもそも認証境界を持たない** |
| Upload 128MB isolate 超過 | 3 | **Worker isolate で画像バッファリング アンチパターン** |
| audit chain fork | 3 | **KV/D1 で atomic single writer を実現できない** |
| login rate limit race | 3 | **KV as atomic counter アンチパターン** |
| audit seq allocation race | 4 | **multi-statement section**（seq予約→SELECT→INSERT） |
| 0005 migration next_seq | 4 | 周辺バグ |
| login PBKDF2 fan-out | 4 | **KV counter ではatomic admission gate不可** |
| Upload 依然バッファ先行 | 4 | **formData() を streaming parser に置換できない限り解決しない** |

### 根本原因は4つに集約される

**R1. KV (eventually consistent) を atomic counter / single writer として使っている**
- login rate limit（race で PBKDF2 fan-out）
- approval rate limit（同様）
- 画像一時保存（eventually consistent で読み取り失敗の温床）

**R2. D1 で multi-statement critical section を serialize しようとしている**
- scheduler claim UPDATE（修正済み、しかし audit で再発）
- 承認POST（修正済み、しかし confirm_token の GET/POST で再発）
- audit seq → prev_hash SELECT → INSERT（4回目で再発）

**R3. Worker isolate 128MB 内で画像 base64 バッファリング**
- 1〜2回目指摘、20MB cap 入れても「streaming parser を使わない限り formData() が先に buffer する」と再指摘
- R2 streaming upload が正解

**R4. bearer-URL + token model**
- 承認フロー
- 根本的にセッションベース認証に持ち替えないと、confirm_token / rate limit / CSRF などを後付けで積み上げ続ける

### モグラ叩きのメカニズム

| フェーズ | CCの対応 | 次に出た問題 | 共通根本原因 |
|---|---|---|---|
| R1-A | processing_started_at 追加 | TEXT比較不安定 | D1 multi-statement |
| R1-B | datetime() 統一 | claim UPDATE 述語不足 | D1 multi-statement |
| R1-C | locked_by 追加 | handleFailure race | D1 multi-statement |
| R1-D | 単一UPDATE+RETURNING | audit seq で再発 | D1 multi-statement |
| R1-E | audit seq 導入 | multi-statement section で fork | D1 multi-statement |

4ラウンドかけて **scheduler は最終的にほぼ直った** が、同じパターンが **audit / rate limit / approval / upload** で繰り返し出現する。

---

## 2. なぜ当初から R1〜R4 に気づけなかったか（CC の反省）

### 2.1 v2 実装を「マルチテナント化のベース」として無批判に継承
- v2 は 10店舗の検証段階では動いていた
- v2 のアーキテクチャ（KV counter / D1 で全部 / base64 Worker内処理）は **その規模だから問題が顕在化していなかっただけ**
- 100店舗 SaaS の設計レビューで「このまま継承」を疑うべきだった

### 2.2 Codex レビューを「機能修正」に限定させた
- 毎回「git diff を adversarial review」と指示したため、Codex も code レベルで指摘を返した
- 最初から「アーキテクチャ選択の adversarial review」を依頼していれば R1〜R4 が1ラウンドで出た可能性が高い

### 2.3 段階的スケール前提（10→30→100店舗）を無視した
- v2 は 10店舗では動く。v4 級の再設計は 30店舗で問題が出てから始めても遅くない
- 「100店舗を現 v2 ベース最適化で目指す」は tech debt の先送りにしかならない

---

## 3. CC が考える正しいアーキテクチャ（100店舗規模）

### 3.1 コンピュートレイヤー

```
┌─ Cloudflare Pages (既存) ─ フロントエンド
│
├─ Cloudflare Workers (fetch) ─ 軽量ルート：auth / API / signed URL発行
│
├─ Durable Objects ─ 強整合性が必要な箇所
│   ├─ PublishOrchestratorDO (per-post)  ─ publish state machine, idempotent retry
│   ├─ RateLimitDO (per-ip or per-store)  ─ atomic counter, 本物のlogin/API gate
│   └─ AuditAppenderDO (singleton)        ─ single-writer audit chain
│
├─ Cloudflare Queues ─ 予約投稿 / retry / 非同期処理
│   └─ Scheduler → Queue → PublishOrchestratorDO
│
└─ Cron Trigger ─ Queues enqueue のみ（polling はしない）
```

### 3.2 データレイヤー

```
├─ D1 ─ トランザクショナルデータ（stores / posts / approval_requests）
│       ただし atomic counter や chain head は DO に任せる
│
├─ R2 ─ 画像ストレージ
│       Worker では signed URL を発行するだけ、base64 に変換しない
│
└─ KV ─ 本来用途のみ（小さな config cache / session blocklist）
         rate limit や counter には使わない
```

### 3.3 フロー別の責務分離

**即時投稿:**
```
Web → Worker(fetch) → R2 upload (signed URL) → PublishOrchestratorDO →
  IG Graph API → D1 status=published → AuditAppenderDO
```

**予約投稿:**
```
Web → Worker → D1 insert status=scheduled, Queue message →
  [5分後] Queue consumer → PublishOrchestratorDO →
  IG Graph API → D1 update → AuditAppenderDO
```

**承認フロー:**
```
承認者がログイン（別アカウントor通常ログイン）→
  Worker fetch (session cookie) → D1 claim status=pending→processing →
  CSRF token検証 → PublishOrchestratorDO → ...
```

### 3.4 これで R1〜R4 がどう解消されるか

| 根本原因 | 解消方法 |
|---|---|
| R1 KV atomic counter 誤用 | RateLimitDO で atomic、KVは使わない |
| R2 D1 multi-statement race | DO 内で single-writer 処理、batch transaction 可 |
| R3 Worker isolate 画像バッファ | R2 signed URL で Workerはbase64に触れない |
| R4 bearer-URL model | 承認者ログイン or SSO、confirm tokenは CSRF token に昇格 |

---

## 4. 商業的な判断軸

### 4.1 現状 v2 は何店舗まで耐えるか（CC推定）
- **10〜20店舗**: 問題なく稼働（既存実績）
- **30〜50店舗**: 監視と人手介入があれば動く。rate limit bypass や画像 memory 事故は稀に発生
- **50〜100店舗**: R1〜R4 に触れる事故が**週次で発生し得る**。SLA 担保不可
- **100店舗+**: No-ship、再設計必須

### 4.2 v3 (現状 HEAD=603a49f) は何店舗まで耐えるか
- 10〜30店舗は同水準で安定性向上
- 30〜50店舗は v2 より緩和するが根本解決していない
- **100店舗 SaaS としてはリリース不可**（Codex 4回目が明言）

### 4.3 3つの戦略的選択肢

**α. 現状 v3 でコミット、10〜30店舗運用で商用開始。100店舗は v4 で再設計**
- CC 追加工数: 0h（現状でコミット済み）
- 子安氏: 運用監視 8〜12h + 本番 migration 2〜4h = **10〜16h**
- リスク: 30店舗超で事故発生、信用毀損

**β. v3 をさらに固めて 50〜80店舗まで耐えさせ、100店舗は v4 で再設計**
- CC 追加工数: 3〜5h（4a, 4b 修正、5/3 markingの明示的子安氏スコープ化）
- 子安氏: R2/Queues/DO/WAF 等 80〜100h = **85〜105h**
- リスク: 中程度。移行前に 80店舗到達のリスク

**γ. 100店舗を真に目指すなら v4 を今から設計（DO + R2 + Queues 前提）**
- CC: v4 アーキテクチャ設計書作成 4〜8h
- 子安氏: v4 実装 200〜300h = **1.5〜3ヶ月**
- v3 は 30店舗まで商用、並行して v4 開発
- リスク: 投資先行、但し技術負債は最小化

### 4.4 CC の意見

**β または γ**。α は「今すぐ100店舗目指さない」契約前提なら可だが、この案件は100店舗を契約ターゲットにしている気配がある。

γ が理想だが投資額が大きい。**β で 50店舗までを v3 で、100店舗到達前に v4 移行** が現実的な落としどころ。

ただしこの判断は **経営判断の領域**。CCは技術情報を整理して渡すだけで、最終判断はユーザー（高木）と子安氏の協議。

---

## 5. Codex 5回目レビューへの質問（アーキテクチャ反論）

この監査ドキュメントを入力として、Codex に以下を厳しく反論してほしい:

### 5.1 根本原因の特定は正しいか
- R1〜R4 の抽象化は正しいか？ 過度な単純化 or 見落としはないか？
- 他に共通根本原因はないか（例: auth model, 監視不在, etc）

### 5.2 100店舗アーキテクチャの選択は適切か
- Durable Objects + Queues + R2 の構成は本当に必要か？ 過剰な複雑化ではないか？
- 代替案（例: Supabase + Vercel / AWS Lambda + DynamoDB / Fly.io + Postgres 等）との比較で Cloudflare が最適か？
- Workers Paid プラン + DO + Queues + R2 のコスト増分は100店舗 ¥2M/月の売上に対して妥当か？

### 5.3 段階的移行戦略は妥当か
- β（v3→v4 移行）は tech debt が溜まりすぎないか？
- γ（v4 一気実装）の200〜300h見積は現実的か？
- v3 のまま運用する場合、何店舗までなら商用 SLA を名乗れるか？

### 5.4 そもそも v3 を継続する価値があるか
- HEAD=603a49f までの修正投資を捨てて、v2（本番稼働中）+ v4（100店舗対応）の2系統運用が合理的か？
- v3 は「30店舗までのつなぎ」に役目がないか？

### 5.5 CCの役割の境界
- CC が追加でコード修正するのは「設計が正しい前提でのbug fix」に意味があるが、設計自体が間違っているならCC修正は無意味
- 子安氏に渡すのは「アーキテクチャを再決定してから」であるべきではないか？
