重複の実態 — 数字で見る
xdeal-lp / spikes-lp / ivs-cms の3つの IVS 2026 アプリは、同一スタック(Next.js 15 App Router + React 19 + Drizzle/Neon + Vercel Blob + 4S API + yarn + Vercel)の上に独立に建てられた。13ドメイン監査の結果、同じサブシステムがそれぞれ 2〜4 回ずつ再実装されていることが確認された。
| 重複アーティファクト | コピー数 | 備考 |
|---|---|---|
session-card balance engine(computeBalance / computeGridBalance / overlayExtraH / autoFitFontSize / visualWidth) | 4 | ivs 原典 ~1922 行 + xdeal 複製 + spikes DOM 版 + spikes satori 再移植 |
contentStore.ts(fs-dev / Octokit-prod + 楽観ロック) | 3 | ほぼ同一。default branch / committer / If-Match 拡張のみ drift |
content/lib.ts(readContent + Resource enum + sample merge) | 3 | inline / 抽出済 / 欠落の3態 |
| 共有パスワード auth | 3 | spikes は平文 ===、ivs は constantTimeEqual、xdeal は timingSafeEqual + jitter |
| 4S session-handoff モジュール | 2 verbatim + 1 half | xdeal↔ivs はバイト近似同一 |
verify4SUser(GET /users/me) | 3 | 同じ ~30 行の関数が3つの異なる戻り型を持つ |
| 組織名正規化 | 6 | spikes 内だけで4つ + xdeal inline 2つ |
| クライアント capture→PNG | 3 rasterizer | modern-screenshot / html2canvas-pro / html-to-image |
| bulk-ZIP worker-pool ループ | ~7 | DownloadsModal / SurveysClient / StagePosters 他 |
downloadBlob / sanitize / uniqPath | ~8+ | ~40 ファイルにコピペ |
| RFC-4180 CSV パーサ | 2+ | |
| 4S event/profile クライアント | 3 |
概算で 同じ ~15 サブシステムが 30〜40 の重複モジュールと数百のコピペ helper に相当する。各コピーは別の時点で、別のフォントスタック・レンダラ・イベント向けにパッチされ、確実にドリフトしてきた。
統合テーゼ
3つのアプリは3つのプロダクトではない。「存在する前に3回フォークされた、1つのイベントプラットフォームテンプレート」である。13ドメイン分析はすべて独立に同じ結論に到達した — パラメータ化されたモジュールを1つ抽出し、イベント固有の data adapter と design-token ファイルを注入すれば、新イベントは「デザインレベルの指示だけ」で出荷できる。
event-manual は 実行可能な monorepo テンプレート(@event/* パッケージ群)+ 同じ真実源から生成されるマニュアルサイト として構築する。パッケージがエンジン / transport / 永続化 / 認証の全ロジック(generic)を所有し、各イベントは3つの薄い面だけを書く:
- 4S binding への data adapter
- design-token / theme ファイル
- content JSON + manifest
抽出のレバレッジ順は以下の通り(docs/ARCHITECTURE.md §1):
@event/fours-sdk— 6+ の 4S 重複コピーを消し、認証を標準化。他の全てがここを通して読む@event/session-card— 最多重複(4コピー)の balance engine を消す@event/core— contentStore + auth + env + KV + next.config。3アプリが既に 90% 合意している背骨@event/asset-kit— capture 1系統 + bulkZip 1系統 + asset store 1系統で ~10 のアドホック export を退役
統治ルール
今後の不変条件は1行に尽きる:
> イベントアプリにエンジンコードを書かない。 新イベントがロジックを必要としたら、それはパッケージ側に config knob 付きで実装する。フォークしない。
これを支える規約:
- 4S への fetch は必ず
@event/fours-sdkを通す。アプリから直接fetchを書かない - 正規ドメイン型は
@event/schema。ID は全域id(_id禁止) - LOCKED フィールド(4S 所有:
title/startsAt/speakerIds等)に CMS から書き込まない - 既存3アプリから採取したコードは JSDoc に
@harvested-fromで出典を記録する
既存3アプリは Phase A〜E の段階移行(第43章)を経て、最終的には examples/ の薄い re-skin となる。将来のイベントは常に apps/starter からフォークし、特定イベントからはフォークしない。