xdeal-lp / spikes-lp / ivs-cms の3アプリを、挙動を変えずに @event/* へ寄せる。一気に移さず、機能的に等価なコードから置換するのが原則(当初「バイト同一」と想定したが、実測では表層差分が多く diff 行数は当てにならない — export シグネチャとロジックの照合で判定する)(docs/ARCHITECTURE.md §8.2 の 5 フェーズ)。実行手順はレシピ converge-existing-app に Phase A の具体コード付きである。
フェーズ全体像(ARCHITECTURE.md §8.2)
| Phase | 対象 | 狙い / 受け入れ条件 |
|---|---|---|
| A | fours-sdk / core / schema | 抽出 & 並走。バイト同一ファイルから置換(挙動変更ゼロ) |
| B | session-card / asset-kit | 4コピー balance engine + 約10の toPng を統合。最大の LOC 削減 |
| C | cms / shell | contentStore + 認証を統一。id/_id 統一・lazy DB client |
| D | feature modules | feedback/spatial(ivs)・booking/mailer(xdeal)・networking(spikes) |
| E | examples 化 | 3アプリを薄い re-skin にし、starter が唯一の fork 元になる |
Phase A — 抽出 & 並走(挙動変更なし)
各アプリはパッケージを deps に足しつつ自前コピーも残す。バイト同一のものから移す(差し替えても挙動が変わらない):
- 4S セッションハンドオフ —
src/lib/4s-session/*は xdeal↔ivs で byte-identical(origin 文字列と quote style だけが違う)。consumeHandoff/tokenStore/use4SSession→@event/fours-sdk/client、verify4SUser(3アプリで同じ約30行)→@event/fours-sdk、createLoginProxyHandler/createCognitoRefreshHandler→ route を factory に - 死コード削除 — 分析で確認済み: xdeal の 1,457行
ScheduleMap.tsx(未使用)、four-s.tsの OAuth stub(FOUR_S_OAUTH_*未設定) - contentStore / auth —
@event/coreのcreateContentStore(ivs canonical + sha リトライ統合版)とcreateSharedPasswordAuth(constant-time 統一 — spikes の平文===を修正)へ - 検証 — 各置換ごとに型チェック + 該当フローを1つ手で叩いて挙動一致を確認してから次へ
収束の3段構え — バイト同一 → 採取元 JSDoc → 段階収束
パッケージ化の作法は「バイト同一のものを最初に、採取元を JSDoc に記録し、段階的に寄せる」の3段:
- バイト同一ファイル置換 — Phase A の
4s-session/*のように、3アプリで既に一致しているコードは差し替えてもリスクゼロ。ここから着手する - 採取元 JSDoc(
@harvested-from) — パッケージ側は必ず採取元を記録する。例:@harvested-from ivs-cms src/lib/titleBreak.ts。どのアプリのどのファイルを起源に一般化したかが追える(packages/*/src/index.ts冒頭に集約) - 段階収束 — Phase B 以降は「最も重複が多く drift しやすいコードほど先」。session-card の4コピー balance engine(最多重複)→ 約10の toPng フロー → id 統一、と1論理単位=1コミットで進める
Phase B 以降のポイント
- B: 4コピー balance engine →
@event/session-card/engine(各アプリは theme token だけ残す)。約10の ad-hoctoPng→@event/asset-kit。最も drift しやすく payoff が最大 - C: 3アプリを
@event/cmscontentStore(ivs の楽観ロック版)+@event/shellへ。requireRealmガードが ivs(再検査0)/ spikes(1)のギャップを塞ぐ - D: 相互受粉 — xdeal は per-item カード調整 + 楽観 PATCH を、spikes は i18n + SEO + セキュリティヘッダを、ivs は CSP + Sentry を得る
- E: 収束後、3アプリは契約を証明する薄い re-skin(
examples/)になる。今後の新イベントは常にstarter/から fork する
統治ルール
イベントアプリにエンジンコードを残さない。 新イベントがロジックを要するなら、fork ではなく config knob 付きでパッケージに落とす。「エンジンはパッケージへ、アプリには config だけ」が収束後も維持すべき唯一の線である。