# チャット型ファネルビルダー 実装計画書（確定版・MECE精査済み）— 2026-07-06

**位置づけ**: GitHub Issue #1（12分類の論点）とそのコメント（実装案）を土台に、**実コードの裏取り**（file:line）と**7テンプレート設計ドキュメントの全ステップ消化**を行い、精査・ブラッシュアップした確定版の実装計画。
**最上位原則**: **現状の実装に矛盾・おかしな変更を一切加えない**。チャット中は本体DBに書き込まず、確定時のみ既存モデル・既存制約のまま下書き生成する。焦点は「チャット体験の使いやすさ」。
**関連文書**: テンプレート仕様の正本 = `docs/funnel-builder-template-specs-20260706.md`／論点 = Issue #1／既知バグ = `docs/audit-degradation-techdebt-20260629.md`・`docs/bug-hunt-core-flows-20260629.md`

---

# 第1部 現状把握（MECE・実コード裏取り済み）

現状を **A. 使える資産 / B. 守るべき制約 / C. 干渉する既知バグ / D. 存在しないもの** の4象限で整理する（漏れなく・重複なく）。

## A. 使える資産（再利用する・作り直さない）

| 資産 | 場所 | ビルダーでの使い方 |
|---|---|---|
| チャット作成の入口UI（2区分切替・チャットログ・吹き出しCSS） | `funnel/create.blade.php:11-73,129-166`（`準備中`バッジ:50） | 入口とチャットUIの見た目の土台。flag ON時にペイン内容をBuilderへ差し替え |
| チャット作成のサーバ側分岐 | `FunnelController@store:33-65`・`resolveChatFunnelName:221` | **温存**（flag OFF時の既存挙動）。Builderは別Controller |
| ページJSON構造・エディタ・プレビュー | `funnel_pages.page_content`・`editor.blade.php`・`preview.blade.php` | 生成ページは既存JSONで出力→既存エディタでそのまま編集可能 |
| ページ内連携要素の走査系 | `form_register`/`line_btn`/`line_qr`（`FunnelPageScenarioUsageService`・`FunnelPageLineBlockService`）、`form_payment`/`buy_button`（`FunnelPageProductCheckoutService`） | 生成側はこの既存要素形式に合わせるだけで公開表示・連携チェックに乗る |
| シナリオ/ステップ配信エンジン | `Scenario`・`StepMessage`（`schedule_mode`=immediate/plus_hours/plus_days 実装済）・`StepMailDispatchService` | 生成するステップは既存カラムに値を入れるだけ。配信は既存エンジン |
| ステップの下書き状態 | `step_messages.status` 既定`'draft'`（baseline）。**配信対象は`'active'`のみ**（`StepMailDispatchService:39,87`・`MailDeliveryStateService:33,407`） | **生成時`draft`にすれば確定前に1通も送られない**（新規機構不要） |
| イベント/予約/リマインダ | `Event`（type=seminar/consultation/other・`is_published`）・`EventScheduleSlotGenerator`・`scenario_reminders` | ①④⑥のイベント生成に既存モデルをそのまま利用 |
| 商品/決済 | `Product`/`ProductPlan`（`payment_gateway_account_id`・購入後導線）・`ProductPlanCheckoutEligibility` | ⑥⑤の決済導線。購入可能性判定も既存を利用 |
| テンプレサービス（ページ名生成） | `FunnelTemplateService`（`applyToFunnel`・private生成） | **変更しない**。Builder用テンプレ定義は別レイヤー |
| 認可基盤 | `auth`+`operator.module`（`Kernel.php:94`・`web.php:50`）・`OperatorPermissionCatalog`（perm_funnel） | Builderルートも同グループ・同権限に載せる |
| 参考実装（チャット保存の先例） | `DashboardChatController`+`dashboard_chat_messages` | 「保存＋固定返答」の既存パターン。ただしビルダー専用テーブルに分離（Issue #1どおり） |

## B. 守るべき制約（実コードで確定・違反すると矛盾が生じる）

| # | 制約 | 根拠（file:line） | ビルダー側の対応 |
|---|---|---|---|
| B1 | `page_type` は11種のenumのみ（lp/registration/line_registration/sales/payment/upsell/downsell/thanks/webinar/event_booking/member_invitation） | `FunnelPageController:60,345` | 新page_typeを追加しない。7テンプレ全てを既存値にマッピング（仕様書§比較表） |
| B2 | シナリオは**メールまたはLINEアカウント必須**。かつ `scenarios.account_id` は **NOT NULL**＋`unique(account_id, uid)` | `ScenarioController:143`・baseline migration | 生成時: アカウント未設定→シナリオを作らず警告。作成時は `account_id`（互換主キー）と `mail_delivery_account_id` の両方を設定し、uidはアカウント内一意で採番 |
| B3 | ステップ配信は `status='active'` のみ送信・予約同期対象 | `StepMailDispatchService:39,87`・`MailDeliveryStateService:33,407` | **生成ステップは必ず `status='draft'`**。既存UI（`StepMessageController@store:151`）が`active`で作る実装を**真似しない** |
| B4 | ステップは現状**メールのみ**（`type=email`固定・編集もメール以外拒否） | `StepMessageController:127,231` | **LINEステップの自動生成は行わない**（blueprint内「作成予定メモ」として保持。将来Issueに分離） |
| B5 | 公開ページのフォーム表示には条件がある（シナリオのメールアカウント設定・フォーム公開・有効項目） | `FunnelPageScenarioFormService`（仕様§7） | Applierはシナリオ生成時に**読者項目の既定投入と公開フォーム設定**まで行う（要実装時確認: `ScenarioReaderFormDefaults`の適用条件） |
| B6 | ファネル/ページ生成は private が既定（テンプレ作成も private） | `FunnelTemplateService`・`FunnelPage.isPubliclyAccessible()` | 生成物は全て private / `is_published=false` / `status='draft'` |
| B7 | `funnels` のfillableは最終生成物用の列のみ（builder途中状態の置き場がない） | `Funnel.php:7-10` | 途中状態は新設 `funnel_builder_sessions` にのみ保存。`funnels`には要約のみ（`state_transition_notes`） |
| B8 | イベントの `type` は seminar/consultation/other。リマインドは配信アカウント条件つき | `Event` モデル・`EventController` | ①=consultation、④=seminar、⑥=other/seminar。条件未達なら `reminder_enabled=false`＋警告 |
| B9 | feature flag 基盤は存在しない（`config/features.php`なし） | `config/` 一覧 | 新設（既存configに手を入れない） |
| B10 | ルートは `Route::resource('funnel', ...)` が既存 | `routes/web.php` | `/funnel-builder` prefixで衝突回避（Issue #1どおり） |

## C. ビルダーに干渉する既知バグ（2026-06-29監査より・対応方針つき）

| # | 既知バグ | ビルダーへの影響 | 方針 |
|---|---|---|---|
| C1 | **公開ページがセクションmargin未読込**（audit H1: `preview.blade.php:268-275`はpadding系のみ） | Builderが生成したLPを公開すると余白が崩れる恐れ | **Phase 4より前にH1を修正する**（推奨）。暫定回避するなら生成セクションの余白は「要素側のmargin/padding」だけで表現しsectionのmarginに依存しない |
| C2 | **配信の重複リスク**（bug-hunt P3: `scenario_mail_deliveries`ユニーク欠如・リマインダreserved未除外） | 生成シナリオを`active`化して運用に入った後に顕在化 | Builder自体は`draft`生成なので無関係。ただし**「生成→有効化」ガイドに有効化前の注意として明記**。恒久対応は別Issue |
| C3 | **テストDB未分離**（audit H6: `phpunit.xml:24-26`） | Builderは多数のFeatureテストを追加する。現状のまま書くと本番DB事故リスク | **PR 1 に phpunit の sqlite/:memory: 分離を含める**（Builder開発の前提整備） |
| C4 | 会員slug非一意・決済webhook判定等（P1/P2/P4） | Builderと直接関係なし | 本計画のスコープ外（既存ハンドオフ文書で対応中） |

## D. 存在しないもの（新規に作る・ゼロからの範囲）

- Builderセッション/メッセージのテーブル・モデル・Controller・ルート（`/funnel-builder`）
- テンプレート状態機械（質問フロー定義）・Blueprint・Validator・Applier・MapBuilder
- 右側ファネルマップUI・公開前チェックUI・本番同様テストUI
- feature flag（`config/features.php`）
- AI連携（初期はAdapter interfaceのみ。**AIなしのルールベースで完走可能にする**）

---

# 第2部 Issue #1 計画の精査結果（承認＋修正・具体化）

Issue #1本文とコメントの実装案は方向性として**全面的に妥当**。実コード裏取りにより以下を修正・具体化する。

## 2-1. そのまま承認する点（再掲のみ・変更なし）

- チャット中は本体DB不変、`blueprint_json`が唯一の正、apply時のみトランザクション反映、二重apply防止（`lockForUpdate`）
- `FunnelBuilderController`への分離・`/funnel-builder` prefix・`auth`+`operator.module`
- 新テーブル2つ（sessions/messages）のみ・既存テーブル変更なし
- MVP=②メール取得のみ、page_type/element/status既存準拠、AIはAdapter化しNull実装から
- PR分割の基本構成（PR1〜PR7+）

## 2-2. 修正・具体化する点（本計画の付加価値）

| # | Issue #1の記述 | 精査結果 → 確定仕様 |
|---|---|---|
| K1 | 「StepMessage `status=inactive` を推奨。既存仕様上activeで予約されるなら…」 | **確定: `status='draft'`**。baselineの既定値であり、配信・予約同期とも`active`のみ対象（B3）。`inactive`という値は既存UIの更新時enum（active/inactive）にあるが、**未確定の下書きは`draft`が正**。「配信を有効にする」操作＝既存のステップ編集画面で`active`化（Builderは有効化までやらない） |
| K2 | シナリオ生成「name / mail_delivery_account_id / business_setting_id / uid」 | **追加: `account_id`（NOT NULL・互換主キー）を必ず設定**し、uidは`unique(account_id, uid)`でアカウント内一意に採番（B2）。`business_setting_id`は既定会社をフォールバック（既存チャット作成と同じ`FunnelController:56`の解決ロジックを流用） |
| K3 | 「form_registerにscenario_idを埋めれば公開表示に乗る」 | **不足を補完**: 公開フォームが実際に描画されるには**シナリオ側の読者項目＋公開フォーム設定が必要**（B5）。Applierに「読者項目の既定投入（`ScenarioReaderFormDefaults`相当）＋フォーム公開設定」を含める。ここを漏らすと「生成したのにフォームが出ない」というUX事故になる |
| K4 | 生成ページのsections設計（hero/offer/form…） | **追加の注意**: 既知バグC1により、**セクションmarginに頼らないブロック構成**にする（または先にH1修正）。また要素typeは`headline/subhead/text/image/button/form_register/divider/spacer/footer`等の既存27種のみ使用 |
| K5 | Phase 0のfeature flag | **具体化**: `config/features.php` 新設＋`FEATURE_FUNNEL_BUILDER=false`。**入口の差し替え方法**: `funnel/create.blade.php`のチャットペインは温存し、flag ON時のみペイン内に「新しいチャット作成（ベータ）を開く」ボタン→`/funnel-builder`へ遷移（既存フォームのhidden `creation_mode=chat`経路はflag OFF時の互換として残す）。既存Bladeへの差分を最小化 |
| K6 | テスト計画 | **前提追加**: PR 1でphpunitのテストDB分離（C3）。これをやらずにBuilderのFeatureテストを増やすのは危険 |
| K7 | PR 7以降の順序（LINE→相談→Zoom→法務→決済→ウェビナー） | **修正: ⑦法務ページは独立して早められる**。⑦は静的2ページ＋リンク設定のみで配信・イベント・決済の生成依存がなく、リスク最小。一方⑥⑤が必要とする「決済ページからの法務リンク」の前提にもなる。→ **確定順: ②→③→①→④→⑦(並行可)→⑥→⑤**（各テンプレのSTEP数・依存は仕様書の比較表参照） |
| K8 | UIは「PC左チャット・右マップ」 | **具体化（7ドキュメント共通UX原則を正本化）**: 仕様書§0の10原則（1問1答＋選択肢チップ＋おすすめ／戻る・あとで設定・仮で進める／時間基準の常時明示／不要ノードは「不要＋理由」／内部用語の言い換え表／要確認バッジ／公開前チェック→本番同様テストの2段締め 等）をUI実装の受け入れ条件にする |
| K9 | applyの生成範囲 | **明確化: Builderは「下書き生成」まで**。公開（ページpublic化・イベントpublish・ステップactive化・配信開始）は**すべて既存画面で人間が行う**。「本番同様テスト」ノードは初期実装では**チェックリスト表示＋既存プレビューへのリンク**であり、自動E2Eではない（誤解防止として仕様書にも明記） |
| K10 | セッションstatus | **追加: `applying`で中断したセッションの復旧**。`apply`冒頭で`status='applying'かつ更新から10分超`は`failed`へ落とし再apply可能にする（トランザクション失敗時はロールバック＋`failed`記録）。※`failed`時に`created_funnel_id`が残らないことをテストで保証 |

## 2-3. UX充実のための追加提案（Issue #1に無い・7ドキュメント由来）

| # | 提案 | 根拠 |
|---|---|---|
| U1 | **右マップの「基準」バッジを一級市民に**: 各配信ノードに `基準：登録日` / `基準：開催日時` / `基準：決済完了` を常時表示するUIコンポーネントを共通化 | 全7ドキュメントが「基準の混同が最頻トラブル」と繰り返し強調 |
| U2 | **choice chips＋「おすすめで進める」を全質問の標準に**: おすすめ選択時は標準構成を自動採用し、右マップに反映後「次はいつでも変更できます」と一言 | 全テンプレの質問に「おすすめ」選択肢が定義済み |
| U3 | **「仮で進める」＝要確認バッジ**: 仮リンク・仮日時・仮価格は進行を止めず、公開前チェックに集約 | ②特典URL仮・④Zoom URL・⑥価格仮・⑦住所仮の各仕様 |
| U4 | **不要ノードの扱い**: 「送らない」「提供しない」選択時はノードを消すのではなく「状態：不要＋理由」表示（例: カード決済のみ→未決済フォロー不要） | ④⑤⑥仕様の明文 |
| U5 | **テンプレ切替提案**: 会話中に有料→⑥、無料→④、LINEだけ→③ 等の切替を「選択肢」として自然に出す（強制遷移しない） | 各テンプレSTEP 0〜1の分岐仕様 |
| U6 | **内部用語言い換え表を`config/funnel_builder.php`に定数化**: 画面文言のブレを防ぎ、レビュー可能にする | 仕様書§0-7の言い換え表 |
| U7 | **セッション再開**: 一覧（`GET /funnel-builder`）に下書きセッションを表示し「続きから再開」。放置セッションは自動削除しない（明示キャンセルのみ） | ユーザーの離脱・再開が想定される会話長（19〜29問） |

---

# 第3部 実装計画（確定版）

## 3-0. ゴールと非ゴール

- **ゴール**: チャットで質問に答えると右側に「作成予定のファネル構成」が組み上がり、確定ボタンで**既存モデルの private 下書き一式**（ファネル・ページ・シナリオ・draftステップ…）が生成され、以後は**既存のエディタ・既存の設定画面**で仕上げ・公開できる。
- **非ゴール（初期リリースでやらない）**: 公開・配信開始の自動化／LINEステップ自動生成（B4）／決済・イベントの自動公開／AI必須化（Null Adapterで完走）／既存チャット分岐・既存テンプレ作成の変更。

## 3-1. アーキテクチャ（確定）

```
[funnel/create.blade.php]  flag ON時のみ「ベータを開く」→
GET /funnel-builder ──┬── FunnelBuilderController（薄い: validate・認可・Engine呼出のみ）
                      │
        FunnelBuilderEngine（状態機械の進行・answers/blueprint/map/warnings更新）
                      │
        FunnelBuilderTemplateRegistry ── Templates/EmailLeadTemplate ほか7種
                      │                    （質問定義・分岐・buildBlueprint・buildMap）
        FunnelBlueprintValidator（schema/page_type/element/アカウント条件/決済条件）
                      │
   （確定時のみ）FunnelBlueprintApplier ── DB::transaction {
        Funnel(private) → FunnelPage(private, 既存page_content) →
        Scenario(account_id+mail_delivery_account_id, 読者項目既定+公開フォーム設定) →
        StepMessage(type=email, status=draft) → [Phase後半: Event(is_published=false), 法務Page] →
        form_register等へid埋め込み → session.applied + created_funnel_id }
```

- **DB**: `funnel_builder_sessions` / `funnel_builder_messages`（Issue #1コメントのマイグレーション案どおり。statusは drafting/ready/applying/applied/cancelled/failed）
- **API**: `GET /funnel-builder`・`POST /session`・`GET /{session}`・`POST /{session}/message`・`POST /{session}/back`・`POST /{session}/apply`（throttle: message 60/min・apply 6/min 目安）
- **認可**: `auth`+`operator.module`。セッションは`user_id`スコープ（他ユーザーは404）
- **blueprint schema**: Issue #1コメント案 v1 を採用（schema_version/funnel/pages/scenarios/step_messages/events/products/legal_pages/map/warnings/checklist）

## 3-2. フェーズ＆PR分割（確定版・Issue #1案に修正を反映）

| PR | 内容 | Issue #1からの変更点 |
|---|---|---|
| **PR 1** | feature flag（`config/features.php`）＋Builder入口＋**phpunitテストDB分離（C3）** | phpunit分離を追加（K6） |
| **PR 2** | sessions/messages マイグレーション＋Model＋Controller＋ルート＋UI骨格（PC2カラム/SPタブ・チャットログは既存CSS流用）＋セッション再開一覧（U7） | 再開一覧を追加 |
| **PR 3** | Engine＋Registry＋`EmailLeadTemplate`（②の19問を仕様書どおり実装）＋Validator＋MapBuilder＋右マップ描画（基準バッジU1・不要ノードU4・要確認バッジU3） | UX原則を受け入れ条件化（K8） |
| **PR 4** | `FunnelBlueprintApplier`: Funnel＋FunnelPage生成（private・既存page_content・**セクションmargin非依存の構成K4**）＋二重apply防止＋applying復旧（K10） | H1回避方針・復旧を追加 |
| **PR 5** | シナリオ＋draftステップ生成（**account_id必須K2・status=draft K1・読者項目/公開フォーム設定K3**）＋`form_register`へのscenario_id埋込＋アカウント未設定時の警告 | K1-K3を反映 |
| **PR 6** | 公開前チェック＋本番同様テスト（チェックリスト＋既存プレビューへのリンクK9）＋disabled制御＋マップ詳細ドロワー＋SP仕上げ | 「テスト＝自動E2Eではない」を明記 |
| **PR 7** | ③LINE取得（LP/サンクス/line_btn/line_qr生成まで。LINE配信文面はblueprint内メモB4） | — |
| **PR 8** | ①個別相談（Event type=consultation・予約枠・リマインダ条件付き生成B8） | — |
| **PR 9** | ④Zoomセミナー（Event type=seminar・参加/未参加フォローは設計メモから段階実装） | — |
| **PR 10** | ⑦法務ページ（静的2ページ＋各ファネルへのリンク設定。**PR 7以降と並行可K7**。「最終判断は事業者/専門家」を常時表示） | 順序の柔軟化 |
| **PR 11** | ⑥単発イベント決済（決済GW設定済みの場合のみProduct/Plan生成・申込確定条件・未決済フォロー） | — |
| **PR 12** | ⑤自動ウェビナー（CTA時間制御・リプレイ期限は**新規ページ機能の実装が必要**→事前に別設計メモ起票） | 視聴ページのCTA遅延表示・期限制御は既存に無い機能である旨を明記 |

> **PR 12の注意**: ⑤の「N分後にCTA表示」「リプレイ視聴期限」は既存 `page_content` 要素に該当機能がなく、公開ページ側のJS/表示制御の**新規実装**を伴う。ここだけはBuilder外の機能開発が発生するため、着手前に別設計メモ（対象: 視聴ページ要素の仕様）を作ってから進める。

## 3-3. テスト計画

- **前提**: PR 1でphpunitをsqlite/:memory:に分離（本番DB事故の構造的防止）。
- **回帰（既存を壊さない保証）**: テンプレ作成17種・`creation_mode=chat`のflag OFF挙動・`FunnelPage` CRUD/preview が変わらないこと。
- **Builder Unit**: 各Templateの質問順・分岐・answers→blueprint→map生成・Validator（page_type/アカウント条件/決済条件の拒否）。
- **Builder Feature**: session開始/message/back/**apply前はfunnels不変**/apply後の生成物/他ユーザー404/二重apply1件/apply失敗ロールバック（failed時にcreated_funnel_id無し）。
- **Integration**: 生成`page_content`を既存サービスが検出（form_register→scenario_id）／生成ページがエディタで開ける・プレビュー表示できる／生成ステップが`draft`で配信されない（`StepMailDispatchService`を1回実行し送信0件）。

## 3-4. リリース手順・運用

1. flag OFFでmerge → 2. stagingでON（②のみ）→ 3. 一部ユーザーON → 4. 全体ON → 5. テンプレ追加は各PRごとに同手順。
- ロールバック: flag OFF一発で既存挙動（現行の`chat_brief`保存フロー）に完全復帰。
- 監視: apply失敗ログ・`status=failed`セッション一覧・生成funnel_idの追跡。
- **有効化ガイド**（生成後にユーザー/運用が行うこと）: ステップの内容確認→`active`化、ページ公開、イベントpublish。※有効化前の注意としてC2（配信重複の既知バグ）を記載。

## 3-5. 先決事項（実装開始前に決める）

| # | 事項 | 影響 |
|---|---|---|
| S1 | **main/wipどちらを正に集約するか**（既報: mainに`.github`なし・Jun18-19作業はwipのみ） | Builderブランチの分岐元。`feature/funnel-chat-builder`をどこから切るか |
| S2 | 既知バグ**H1（公開セクションmargin）を先に直すか**、Builder側で回避するか | PR 4のページ生成設計（K4） |
| S3 | AI接続の時期とプロバイダ（初期はNull Adapterで進める前提の確認） | PR 3以降の会話の自由入力の扱い（ルールベース分類で開始） |
| S4 | ⑤の視聴ページ新機能（CTA遅延・視聴期限）の要否とスコープ | PR 12の前提設計メモ |

## 3-6. Definition of Done（最終・Issue #1版に追記）

Issue #1コメントのDoD 16項目に加えて:
- [ ] 生成ステップは`status='draft'`で、`putage:step-mail-dispatch`実行でも送信されない（テストで保証）
- [ ] 生成シナリオは`account_id`＋`mail_delivery_account_id`が設定され、読者項目・公開フォーム設定まで揃い、公開LPでフォームが実際に描画される
- [ ] 右マップの全配信ノードに時間基準バッジが表示される
- [ ] 「仮で進める」で作った項目が公開前チェックに要確認として全件列挙される
- [ ] flag OFFで`creation_mode=chat`の現行挙動（chat_brief保存→blankファネル）が変わらない

---

*本計画は実コード裏取り（第1部B・C）に基づく。実装時に想定と異なる挙動を発見した場合は、本書と Issue #1 を同じ差分で更新すること（リポジトリ運用ルール §12 準拠）。*
