開発プラン
実査機能 新基盤(OpenTok → LiveKit 移行)の開発計画書。レーンA(新基盤)・minedia-www レーンB・レーンC(インフラ構築)の3レーン並走を Phase 0〜6 で構成し、全ユースケースのトレーサビリティと工数見積を示す。インプットは実査機能ユースケース統合版 PDF(全41ページ)・To-Be シーケンス図 v2・本設計リファレンス(API / 認可 / Webhook / エンティティ / minedia-www 変更点 / インフラ / オブザーバビリティ)。
1. 全体像
1.1 To-Be アーキテクチャ(3層分離)
- 調査ドメイン(minedia-www 残置): Project / Slot / Answer / Interview、パネル紐付け、リマインド(Mail/SMS/Push)、出欠・評価、謝礼(
Minedia::Point)、要約依頼(StatementSummaryRequest) - 基盤コア(新規開発): Room / Session / Participant / EntryHistory / Recording / Transcription / ConnectionTest / Handout / Chat履歴 / RoomEvent の REST API、入室ゲート(JWT検証 → RoomPolicy → LiveKit grants)、LiveKit webhook 受信、調査側への W1〜W7 配信
- インタビュールーム SPA(新規開発): Vite + React SPA + Workers Assets 配信(確定)。全ロール共通のルーム UI、チャット(サーバー権威型・LiveKit データチャネル再利用)、接続テスト自前計測画面。インフラ配置はインフラ構成(Cloudflare 案)を参照
- 管理画面(新規開発・開発者向け): Refine SPA を Workers Assets で配信し Cloudflare Access でゲート。コアへは Service Bindings の内部プロキシ
/internal/admin経由(ApiKey 非露出)。詳細は §1.3 - LiveKit Cloud: SFU+Egress(録画)。Ship プラン・録画は video composite(確定)
1.2 実査ライフサイクル(To-Be シーケンス v2 の5フェーズ)
| # | フェーズ | 主な処理 |
|---|---|---|
| ① | 作成 | Project/Slot 作成(調査) → POST /rooms(kind=interview, nested session, 2-phase commit) → connect_url 保存 → POST /sessions/{id}/handouts |
| ② | 配布 | 安定URL(connect_url)の共有・リマインド。JWT/Participant はこの時点で作らない |
| ③ | 準備 | POST /connection-tests → 基盤の自前計測画面で双方向計測 → W5 → 調査側で合否判定(User.webrtc_test_status) |
| ④ | 参加 | 入室時はテナントが自己署名した参加 JWT を SPA が提示(基盤への発行往復なし・Participant は初回検証時に JIT 生成) → 二重トークンゲート(JWT検証 → grants 導出 → LiveKit トークン) → メディア接続 → EntryHistory / W1 / BAN+revocation |
| ⑤ | 振り返り | Egress 完了 → Recording → 自動文字起こし → W3/W6 → 出欠評価(entry-histories)→ 謝礼 → 要約(Transcription.uuid 参照)→ 録画閲覧(署名URL) |
1.3 管理画面(開発者向け内部ツール)
| 項目 | 要件 |
|---|---|
| 目的・対象 | 開発チーム専用の内部管理画面。Room(URL発行)/ Session / Handout の作成・参照、Participant / EntryHistory の確認、ApiKey の発行・失効、テナント署名鍵の生成・登録・ローテーション・失効を行う。Operator 向け・minedia-www 側の管理画面(Phase 5)とは別物 |
| フロントエンド | Refine(React)SPA。Workers Assets(静的配信)でホスティング(ルーム SPA・基盤コアと Workers 系ツールチェーンに統一。Pages は不使用) |
| 認証 | Cloudflare Access(Zero Trust)でメールアドレスベースにゲート |
| コアとの接続 | Service Bindings で内部接続(公開ホップなし・CORS 不要。インフラ構成(Cloudflare 案)の Worker 構成参照) |
| 内部プロキシ | /internal/admin/api/v1/*(基盤コア Worker 内)。Cloudflare Access の JWT(Cf-Access-Jwt-Assertion)を CF の公開鍵で検証してアクター(メールアドレス)を特定 → サーバー側保持のテナント ApiKey を付与して公開 API へ転送。テナント ApiKey の実値はブラウザに渡さない |
| 監査 | 書き込み操作は検証済みメールアドレスを X-Admin-User-Id として転送し、将来の監査ログ(RoomEvent payload / audit_logs)に接続できるようにする。ApiKey の発行・失効・変更は専用の監査ログ api_key_events に記録が確定(アクター=CF Access メール。エンティティ一覧参照) |
| 実装フェーズ | Room / Session / Participant / EntryHistory・ApiKey 管理・署名鍵管理画面=Phase 1、Handout 画面=Handout API 実装と同時に Phase 2 で追加 |
2. 開発フェーズ計画
G1 OpenAPI 契約凍結ゲート
これ以降レーンBはモック(契約テスト)で先行着手できる。レーンCの完了は不要(インフラ非依存)。
G2 コア・ステージング稼働ゲート
レーンBの結合テスト開始条件。
Phase 0: 環境整備・契約凍結
ゴール:OpenAPI 契約が凍結されて(G1)、レーンBが並走開始できる(レーンCは Phase 0 開始と同時に着手済み)。実インフラの構築はレーンC(並走)で進めるため、Phase 0 自体はコード・契約整備に専念する。
0-1. 基盤コア リポジトリのスケルトン
| 作業項目 | 内容 |
|---|---|
| 雛形 | pnpm + TypeScript + Hono + @hono/zod-openapi |
| エントリポイント4種 | fetch(API)/ queue(配信 consumer)/ Workflows / scheduled(Cron)の骨格。1 Worker 同居 or 分割の初期判断込み |
| wrangler.jsonc | compatibility_date(開始日固定)、nodejs_compat、observability(traces + head_sampling_rate)、bindings(Hyperdrive / Queues / Analytics Engine / Rate Limiting)の定義のみ先行。実リソースへの接続はレーンC完了後(それまではローカル/モックで進行)。Env 型は wrangler types で生成(手書き禁止) |
| Prisma 7 初期化 | @prisma/adapter-pg、schema.prisma の空枠、prisma migrate を CI から TCP 実行する経路確立(エンティティ定義は P1。実 Aurora への疎通はレーンC完了後) |
| テスト基盤 | vitest + @cloudflare/vitest-pool-workers |
| convention test 初期セット | truffle-survey-v2 から移植・翻案: ①エラーエンベロープ形式 ②全ルートが認証ミドルウェア経由 ③zod-openapi ルート定義とハンドラの網羅 ④no-floating-promises(ESLint) |
| CI/CD | GitHub Actions: typecheck / lint / test / migrate / wrangler deploy(dev→stg。stg デプロイの実行はレーンCの Workers 環境分離完了後) |
| シークレット運用 | wrangler secret put の手順化(IAM キー・LiveKit キー・webhook 署名鍵) |
0-2. 横断規約の実装
| 作業項目 | 内容 |
|---|---|
| ApiKey 認証ミドルウェア | ApiKey <key_id>.<secret>、bcrypt ハッシュ照合 + timingSafeEqual、L0/L1 テナント分離を PoC 段階から実装。キーのスコープ分離はここで仮決め |
| Idempotency-Key | ミドルウェア実装(Postgres ユニーク制約 + ON CONFLICT) |
| エラーエンベロープ | success/data|error + Hono onError での一元変換 |
| カーソルページネーション | 共通ヘルパー |
| 標準エンドポイント | /health・/health/ready・/.well-known/jwks.json(jose・EdDSA 鍵の生成・保管・ローテーション方針を含む) |
0-3. OpenAPI 契約の整備・凍結(G1)
| 作業項目 | 内容 |
|---|---|
| 全エンドポイントの契約化 | interview-api-docs 準拠の全エンドポイントを @hono/zod-openapi のルート定義(スタブ実装)として起こし、openapi.yaml をコードから生成(定義=契約=実行時検証の単一ソース)。本計画で追加が確定した契約(チャット定型スタンプ、翻訳 kind、加工動画 API、Room/Session 作成時の設定封入〈録画・文字起こし・入室画面〉等・P2/P3/P5 参照)も api-design へ反映した上で凍結する。Phase 0 の最重量作業。凍結対象は公開 API(ApiKey 面)+Webhook のみ——インタビュールーム(参加 JWT 面)の SEND/RECEIVE はインタビュールーム設計が正典で G1 凍結対象外(基盤とルーム SPA が同一システム内のクローズド契約。実装は同じ zod-openapi でルート定義し実行時検証は行う) |
| lint・ドキュメント配信 | redocly lint を CI に、UI は Scalar(@scalar/hono-api-reference・確定)をコア Worker の /docs で配信。interview-api-docs(本サイト)との整合手順を決める |
| レーンB 先行の足場 | Prism モックサーバー起動手順 + minedia-www 側 committee 契約テスト雛形 |
| G1 判定 | 契約凍結の宣言、凍結後の変更管理ルール(versioning 手順) |
0-4. LiveKit Cloud セットアップ + 前倒しスパイク
| 作業項目 | 内容 |
|---|---|
| プロジェクト作成 | dev / stg、リージョン確認 |
| Workers 実機動作確認 | livekit-server-sdk(AccessToken 生成 / CreateRoom / WebhookReceiver)を Workers 上で実証。依存構成上は動く見込みだが未実証のため、「薄い E2E スパイク」の前半を P0 に前倒し(dev 環境の Workers で検証可能な範囲。レーンCの環境分離完了前でも着手できる) |
| webhook 疎通 | 署名検証・raw body・失敗 401 |
0-5. ルーム SPA スケルトン
| 作業項目 | 内容 |
|---|---|
| 完了 Vite + React SPA + Workers Assets で確定(SSR 不要のため OpenNext / Next.js は不採用。OpenNext 動作検証タスクは消滅) | |
| 雛形・デプロイ | Vite + React + livekit-client + @livekit/components-react 導入、Workers Assets で dev/stg デプロイ |
| チャットの枠 | サーバー権威型(コアの HTTP ingest → sendData 配信)準拠。SPA 側は DataReceived 購読・履歴 API 取得の枠のみ用意(実装は P2) |
0-6. オブザーバビリティ基盤
| 作業項目 | 内容 |
|---|---|
| メトリクス | Analytics Engine binding + writeDataPoint ヘルパー、標準属性契約(tenant.id/room.uuid)のコード化(メトリクスは OTLP と別経路) |
| エラートラッキング | @sentry/cloudflare(tracesSampleRate: 0・Team プラン確定) |
| 構造化ログ | JSON・PII 排除の規約をログヘルパー + convention test で強制 |
レビュー稼働 4 人日幅 3〜5 人日 / 想定カレンダー 6〜9 営業日
レーンC: インフラ構築(AWS / Cloudflare・Phase 0 と並走)
ゴール:Phase 1 でステージング環境に実接続する作業(Hyperdrive 経由の Aurora 接続、Queues 経由の Webhook 配信等)までに、AWS/Cloudflare の実インフラが整っている。
C-1. AWS 側基盤
| 作業項目 | 内容 |
|---|---|
| アカウント・環境分離 | dev / stg / prd 方針(S3 は既存アカウント流用か要判断) |
| Aurora Serverless v2 作成 | PostgreSQL・東京、min 0.5 ACU 常時(auto-pause 不使用)、public access 有効、rds.force_ssl=1 |
| セキュリティグループ | Hyperdrive の公式公開 IP レンジのみ許可(0.0.0.0/0 にしない) |
| DB ユーザー設計 | master 不使用。アプリ用・マイグレーション用を分離 |
| S3 バケット | 録画 / handout +保持ポリシー(録画180日相当)。文字起こし本文は S3 に置かず Aurora 行内(result_json・As-Is 踏襲) |
| IAM | Worker 用最小権限キー(S3 presign / put)、LiveKit Egress 書き込み用 |
C-2. Cloudflare 側基盤
| 作業項目 | 内容 |
|---|---|
| Workers Paid・環境分離 | wrangler environments で dev / stg / prd |
| Hyperdrive 設定 | 環境ごとに Aurora 接続文字列を登録、TLS verify-full |
| Queues | webhook-deliveries + DLQ(W1〜W7 配信用) |
| Workflows / Cron Triggers | 有効化確認(録画→文字起こしパイプライン・リコンサイルバッチの実装は P3/P5) |
| Smart Placement | 有効化(Worker を東京 Aurora 近傍で実行) |
| Rate Limiting binding | トークン発行エンドポイント用 |
レビュー稼働 1 人日幅 1〜2 人日 / 想定カレンダー 3〜5 営業日(Phase 0 と並走・クリティカルパス外)
Phase 1: コアAPI — ルーム・認可・入退室
ゴール:ルームの作成〜入室〜退出〜強制退出が API+入室ゲートで一気通貫に動く(実査の背骨)。
| 作業項目 | 内容 |
|---|---|
| Rooms | POST /rooms(kind=interview は nested session{}、empty_timeout=枠長+30m、2-phase commit、RTC_PROVISION_FAILED)、GET /rooms/{id}、PATCH /rooms/{id}(文字起こし構成の更新)、POST /rooms/{id}/close(トークン全失効) |
| Sessions | GET /sessions(external_type/external_id 逆引き)、GET/PATCH/DELETE /sessions/{id}(キャンセルは Room close へカスケード) |
| 入室ゲート | JWT 検証(署名/exp・nbf/aud/jti失効/Participant.status)→ RoomPolicy から LiveKit grants 導出(observer は canPublish:false を物理強制)→ LiveKit アクセストークン発行。違反時は発行拒否(認証設計) |
| 失効ストア(jti) | 失効済み jti を TTL 付きで記録する格納先を確定(Cloudflare KV か Aurora か・未決)。入室ゲートが検証のたびに参照する |
| RoomPolicy | 単一 can(ctx, capability, resource?) に認可を集約(HTTP・チャット・メディアトークン発行の全実行点が同一関数を呼ぶ) |
| Participants / BAN | GET /rooms/{id}/participants、POST /rooms/{id}/participants/{participant_id}/ban(status=banned 更新・理由必須・冪等200・接続中なら LiveKit RemoveParticipant)。再入室阻止は入室ゲートでの status 参照により実効化 |
| LiveKit webhook 受信 | 署名検証(raw body・失敗 401)、イベント id dedup、participant_joined/left → EntryHistory 作成・更新(exited_at / duration_sec)、createRoom(metadata: {room_id}) による同定 |
| EntryHistory | GET /rooms/{id}/entry-histories(出欠判定材料) |
| 調査側 Webhook 配信基盤 | 署名付き配信・リトライ・配信ログ(WebhookDelivery)。W1 participant.entered を実装(退室は GET entry-histories の pull で読む) |
| 管理画面(Refine) | 開発者向け内部ツール(§1.3)。Room/Session/Participant/EntryHistory の一覧・作成・参照画面(Workers Assets 配信) + Cloudflare Access(Zero Trust)でのアクセスゲート + 内部プロキシ(/internal/admin/api/v1/*・CF Access JWT検証。コアへは Service Bindings 内部接続) |
| ApiKey 管理 | 発行(secret は生成時に一度だけ表示・以後は bcrypt ハッシュのみ保持)・一覧(key_id / 作成日 / 最終使用日)・失効。管理画面(CF Access 認証・/internal/admin)から操作し、公開 OpenAPI 契約(G1 凍結対象)には含めない。ローテーションは複数キー並行有効で実現(新キー発行 → minedia-www 切替 → 旧キー失効)。監査: 発行 / 失効 / 変更を api_key_events(エンティティ一覧)に記録(状態変更と同一トランザクション・構造化ログへ複写・アクター=CF Access メール)+ 照合 Cron(1 時間毎・イベント突合で不正挿入 / 改ざん / 不正再有効化を検知 → 運用チャンネルへ直接通知) |
| 署名鍵管理 | テナント署名鍵(Ed25519 公開鍵レジストリ jwks_keys・エンティティ一覧)のライフサイクル一式。鍵ペアは管理画面のブラウザ内(WebCrypto)で生成し、秘密鍵はその場で 1 回だけダウンロード・公開鍵 JWK のみを登録(基盤サーバーは秘密鍵に一切触れない・登録口は d 混入を拒否)。kid は RFC 7638 サムプリントをサーバーが確定。自前生成テナント向けに公開鍵(JWK / SPKI PEM)の貼り付け登録を併設。ローテーションは複数鍵並行有効(active → retiring → revoked の一方向遷移・失効伝播は鍵解決キャッシュ TTL 60 秒が上限)。基盤鍵(issuer=interview-platform)は読み取り専用表示のみ。ApiKey 管理と同じく公開 OpenAPI 契約には含めない。監査: 登録 / retire / 失効を jwks_key_events に記録(状態変更と同一トランザクション・アクター=CF Access メール)+ 全変更の即時通知 + 照合 Cron(1 時間毎・不正挿入 / 改ざん / 不正再有効化を検知)。詳細は 認証設計 §署名方式 |
レビュー稼働 8 人日幅 6〜10 人日 / 想定カレンダー 8〜13 営業日(署名鍵管理 +2 人日・参加トークン発行 API 廃止 −1 人日を含む)
Phase 2: インタビュールーム実装(SPA+チャット)
ゴール:全ロール(パネリスト / モデレーター / オブザーバー / Operator 観察 / Extemporary 匿名)が同一ルーム UI でインタビューを実施できる。
| 作業項目 | 内容 |
|---|---|
| 入室フロー | connect_url → 参加 JWT 提示 → 入室ゲート → LiveKit 接続。無効/期限外トークンの拒絶 UI、ブラウザ非対応・デバイスアクセス拒否のエラー画面 |
| 入室前画面 | デバイス選択(カメラ/マイク)+映像プレビュー+マイク音量確認+名前入力(モデレーター/パネリスト)、名前入力のみ画面(observer 系)、パネリスト待機画面(司会者未着時のチェックリスト・開始時刻表示)、注意文言表示 |
| メディア基本機能 | publish/subscribe(role別 grants)、マイク/カメラ ON/OFF、入室後のデバイス切替(マイク/カメラ/スピーカー・テスト音再生)、受信音量調整、画面共有、参加者タイル・レイアウト(拡大/縮小・全画面)、iOS の音声再生有効化(autoplay 制約対応)、退出(確認ダイアログ+遷移先) |
| チャット | public / private(モデ↔オブザーバー)/ observer_only の 3 visibility(チャットは chat_messages 単一テーブル・チャネルエンティティなし)、定型スタンプ(visibility×ロール別セット・初期 13 種)、ファイル添付(現行どおりモデレーターのみ)、履歴の永続化(コアへ)と入室時取得。現行の observer 系チャットの Slack 転送の扱いは未定 |
| チャット送信・添付 API(コア側・インタビュールーム) | POST /rooms/{uuid}/chat_messages(参加 JWT・RoomPolicy 認可・クライアント採番 uuid の冪等送信〈リトライ再送は既存を 200 返却〉・保存→sendData 配信)。添付は署名 PUT → 送信確定の 2 段:POST /rooms/{uuid}/chat_attachments(moderator のみ・MIME/サイズ検証・レコード作成なし)→ S3 直接 PUT → 送信時 attachment_key で確定(S3 キーのみ永続化・URL は都度署名。未確定オブジェクトは S3 ライフサイクル/定期清掃で回収)。スタンプは GET /rooms/{id}/chat-stamps(定義は基盤設定ファイル)+送信時 INVALID_STAMP 検証。契約正典はインタビュールーム設計(参加 JWT 面=G1 凍結対象外) |
| チャット履歴 API(コア側) | GET /rooms/{id}/chat-messages(全 visibility・添付は取得都度の短期署名 GET URL)、DELETE(運用 purge・添付 S3 オブジェクトは非同期削除) |
| 提示物(Handouts) | コア側: POST/GET /sessions/{id}/handouts(multipart)、PATCH/DELETE /handouts/{id}。SPA側: モデレーターの配布操作 → handout.show RoomEvent → 全員の画面に表示。途中入室時の表示状態復元(room-events から最新 show/hide を再生。現行 playback_events 相当) |
| 動画同期再生 | play / pause / mute の同期イベント(モデレーターのみ操作可) |
| BAN UI | モデレーター/Operator 向け確認モーダル → ban API 呼び出し。BAN された側の切断・再入室拒否表示 |
| オブザーバー一覧 | 見学者(observer / Operator 観察)の一覧・人数カウンタ表示(モデレーター / observer 系向け) |
| ルームイベント送信 API(コア側・インタビュールーム) | POST /rooms/{uuid}/room_events(参加 JWT)。handout.show/hide は can?(:present)=moderator のみをサーバー側で強制し、永続化→sendData 配信。動画同期の play/pause/mute イベントも本エンドポイント経由(現行 ActionCable speak にはサーバー側認可が無く、moderator 制約が UI のみだった穴を構造的に是正) |
| RoomEvent 記録 | GET /rooms/{id}/room-events(handout.show 等の時系列取得) |
| 録画状態表示 | 録画中/停止のルーム内表示(Phase 3 のイベントを受けるUI枠を先行実装)、録画失敗のルーム内警告(参加者がいるのに録画イベントが来ない場合の警告・現行踏襲) |
| 再接続 UX | LiveKit 自動再接続中の状態表示・復帰、切断時の再読み込み誘導・再読み込みボタン |
| ルーム内ネットワーク品質表示 | 受信品質(パケットロス・ビットレート等)の表示(現行 Help 相当。Phase 4 の「事前」接続テストとは別) |
| observer 付帯機能 | Document Picture-in-Picture(PiP 中はメイン映像停止で負荷軽減)、無人時の自動リロード相当の整理、オブザーバー招待リンクの表示/コピー |
| 管理画面 Handout 追加 | 管理画面(§1.3)に Handout の一覧・アップロード・更新・削除画面を追加(Handout API と同時実装) |
レビュー稼働 13 人日幅 11〜15 人日 / 想定カレンダー 13〜19 営業日
Phase 3: 録画・文字起こしパイプライン
ゴール:録画が自動で回り、文字起こしまで基盤内で完結、調査側へ W3/W6 が届く(G2: ステージング一気通貫)。
| 作業項目 | 内容 |
|---|---|
| Egress 制御 | always=room 開始時 / manual=初 publish 時の録画開始、解像度・composite レイアウトの指定、egress_started/updated/ended webhook の分岐処理(COMPLETE → storage_key/size/duration 保存、FAILED → error 保存) |
| Recordings API | GET /rooms/{id}/recordings、GET /recordings/{id}、GET /recordings/{id}/download-url(署名URL)、DELETE(個人情報消去) |
| ストレージ | AWS S3 東京(確定済み・バケットはレーンC(C-1)で作成)、保持ポリシー |
| 自動文字起こし | 録画完了を契機に基盤内で自動起動(現行 minedia-www の起動4条件ロジックを基盤へ移管・再定義)。AI 処理(Azure / OpenAI Whisper / ElevenLabs)連携、VTT 出力。話者分離付き VTT(<v 話者> タグ)を出力仕様とする(現行 Azure の話者欠落バグは移管時に解消) |
| Transcriptions API | GET /recordings/{id}/transcriptions、GET /transcriptions/{id}、POST /recordings/{id}/transcriptions(別言語・再実行に加え、kind=translation で日→英翻訳を統合。現行 Whisper translation・local/translated 相当の移管)、GET /transcriptions/{id}/result(VTT 本文取得・result_url は TTL 3600 秒) |
| 加工動画(ProcessedRecording) | Recording と 1:1 シングルトン。POST /recordings/{id}/processed-recording(source=trim=トリム指示 → Workflows で非同期処理/source=upload=署名 PUT 払い出し)+POST /recordings/{id}/processed-recording/complete(S3 HEAD 検証で available 化)+GET/GET .../download-url/DELETE。ブラー/音声変換の自動化は対象外(現行も未自動化・将来拡張)。保護加工の依頼管理・録画の外部共有は www 残置(Phase 5) |
| W3 / W6 / W7 配信 | recording.status_changed(available/failed)、transcription.completed(翻訳完了を含む・kind で区別)、processed_recording.status_changed(加工動画 available/failed・W7) |
| 録画状態のルーム内通知 | recording_status topic(LiveKit データチャネル)でのライブ表示に加え、ChatMessage(content_type=archive_status) を基盤ランタイムが自動生成(egress webhook 契機・observer_only へ永続化・語彙は recording_started/recording_stopped の 2 値)。振り返り画面の履歴表示は履歴 GET で現行同等(Egress に一時停止は無いため現行の paused 通知は廃止) |
| アラート | 録画失敗・provision 失敗 → P1 即時オンコール(observability 設計準拠) |
レビュー稼働 8 人日幅 6〜10 人日 / 想定カレンダー 9〜13 営業日
Phase 4: 接続テスト
ゴール:パネリストが事前に自前計測画面で接続テストでき、結果が調査側の合否判定(User.webrtc_test_status)に届く。
| 作業項目 | 内容 |
|---|---|
| ConnectionTests API | POST /connection-tests(+Room kind=connection_test、test_url 払い出し)、GET /connection-tests(external_type/external_id 必須)、GET /connection-tests/{id}(summary/baseline/load) |
| 自前計測画面 | signaling / webrtc / turn / reconnect / publish / protocol / region の双方向計測。短命トークン(当該テストにスコープ) |
| 計測結果保存 | ConnectionTest(checks/summary) 更新+ConnectionTestEvent 記録 |
| W5 配信 | connection_test.completed |
レビュー稼働 4 人日幅 3〜5 人日 / 想定カレンダー 5〜7 営業日
Phase 5: minedia-www 連携改修(レーンB・G1 後にモック先行)
ゴール:調査ドメインが新基盤を呼び出し、W1〜W7 を受信して既存業務(出欠・謝礼・要約・録画管理)が回る。
| 作業項目 | 内容 |
|---|---|
| Core::ApiClient | ApiKey 認証・Idempotency-Key・リトライを備えた API クライアント。契約テスト(OpenAPI モック)で G2 前に先行実装 |
| スキーマ変更 | interview_room_refs(薄い参照モデル: slot_id NOT NULL の Project 系専用・uuid群・first_entered_at・teardown_state・同期鮮度)、extemporary_sessions(即席の薄い実体: ExtemporarySlot 廃止の後継・フォルダ紐付け+コア Session 直参照+ライトスルーキャッシュ)、recording_refs(録画1行=1レコードの行キャッシュ: status/duration/recorded_at・W3 で room 単位 full sync・フォルダ集計と月次統計の材料)、interview_transcripts(文字起こし本文ローカルコピー+status・fulltext 検索)、recording_processing_requests(保護加工の依頼管理)、projects.webrtc_engine / extemporary_folders.webrtc_engine(即席のエンジン分岐はフォルダ単位)、事後処理モデルへ recording_uuid / transcription_uuid / 自前 slot_id/project_id 追加。いずれも core 非依存のため G1/G2 を待たず先行実装可 |
| アダプタ層 | OpentokRoom(既存の薄いラッパ)/ LivekitRoom(コアAPI連携)+ Slot#interview_room 統一IF。エンジン切替をフラグで制御 |
| ① 作成連携 | Slot 確定時に POST /rooms(nested session)→ connect_url/Session.uuid 保存、割付解決済みリストで POST /sessions/{id}/handouts、キャンセル時 DELETE /sessions/{id}。project 設定(録画モード/解像度・文字起こし言語/エンジン・入室画面の注意文言/氏名欄)を Room/Session 作成パラメータに封入して引き渡す(G1 凍結前に契約へ反映) |
| ② 配布 | 招待メール・SMS・Push・リマインドを connect_url(安定URL)ベースへ差し替え(配信基盤は既存流用)。Operator の強制入室URL 再生成・再送フローの置き換え |
| ④ 入室導線 | 各ロールの入室起点(User: 自社認証 force_access_token 維持 / Employee: Devise / Operator: 観察 / Extemporary: 匿名・役割別URL)から tenant 0 の秘密鍵で参加 JWT を自己署名(基盤への発行往復なし)し SPA へ引き渡す。JWT 発行 BFF の認可必須化: 発行前に room_access_token 照合または認証済みセッション+Ability 検証を必須とし、role はクライアント申告でなく入室経路から BFF が導出(現行 /api/v1/employees/rooms/:id の実質無認証の構造的是正) |
| Webhook 受信(W1/W3/W5/W6/W7) | W1 → Interview.attend_at(panelist 初回のみ・冪等)+ ref/即席実体の first_entered_at 記録、W3 → payload 直書きせず core GET /rooms/{id}/recordings を read-back して recording_refs を room 単位 full sync(冪等 upsert・削除突合)+録画失敗アラート、W5 → 合否判定 User.webrtc_test_status、W6 → interview_transcripts 保存(VTT Pull・error 行も保存・result_url TTL 失効時は再取得からリトライ)→ 要約起動、W7 → recording_processing_requests の completed 遷移/failed→オペ通知。署名検証・dedup + 日次リコンサイル(webhook 欠落補足・last_synced_at 鮮度監視。退室時刻・在室時間は評価時に GET entry-histories) |
| ⑤ 振り返り | 事後評価(GET /rooms/{id}/entry-histories)→ Interview(is_attend/is_reward/score)、謝礼(Minedia::Point.add + DepositHistory)、要約(StatementSummaryRequest が Transcription.uuid を参照)、録画閲覧を download-url 経由に置換 |
| Operator 管理画面 | 録画アーカイブ管理を Recordings API 経由へ、録画失敗表示を W3 起点へ、スロット詳細の Room 情報表示(Session・inspector_url 等)をコア参照へ |
| チャット履歴の運用画面 | スロット詳細の visibility 別チャット履歴の閲覧・CSV 出力・全削除を、コアの chat-messages API 経由へ置換(チャット正本はコアに移るため)。CSV は www 側 BFF が履歴 API を全ページ吸い上げて生成(基盤に CSV/エクスポート API は追加しない)。添付は attachment_name のみ出力(現行の署名 URL 焼き込み=3h 失効リンクの欠陥を解消)、スタンプ・system メッセージの日本語化は www 側 I18n 辞書(現行 operator 側の translation missing バグの解消込み) |
| 文字起こし・翻訳・発言録の運用画面 | 録画ごとの文字起こし/翻訳の実行(言語・エンジン選択)を Transcriptions API(POST・kind)経由へ、字幕の閲覧・書き出し(VTT/docx/text/csv)を interview_transcripts(W6 で取得した VTT ローカルコピー)ベースへ再実装。発言録・レポートサマリー生成は www 残置のまま入力を同テーブルに差し替え |
| ユーザ詳細の Web RTC 表示 | ブラウザテスト結果の表示を ConnectionTests API 経由へ置換(録画動画列は廃止=P4 の「計測値のみ・録画しない」挙動変更に追随)、「インタビュー動画(全て)」リンクを recording_uuid 参照へ |
| 使用時間集計・ダッシュボード | ダッシュボード「即席インタビュー使用時間(月別)」・即席調査一覧「録画時間」列の集計ソースを OtArchive → recording_refs(W3 同期のローカル行キャッシュ)へ置換、「直近アウトプット」欄は interview_transcripts/recording_processing_requests 材料に縮退(完了/失敗のみ)、即席フォルダ一覧の Room 数バッジは extemporary_sessions.first_entered_at IS NOT NULL カウントへ(W1 契機)、「OpenTok Server status」ウィジェットを LiveKit ステータスへ差し替え。移行期は新旧合算: 月次統計は OtArchive 由来と recording_refs 由来を月バケットで合算、フォルダ単位の数字は folder を webrtc_engine で二分しレガシー/新クエリをマージ |
| Extemporary(即席調査) | フォルダ管理は残置。ExtemporarySlot は廃止し、即席枠は薄い実体 extemporary_sessions(コア Session 直参照)に置換。保存時に Room 作成+役割別トークンURL発行(URL の role パラメータ廃止・トークン封入で詐称防止)、招待URL CSV 出力の形式変更、フォルダ検索(SearchCop)に extemporary_sessions.name/memo/access_token を併記追加(移行期は新旧両属性でヒット) |
| 読み取り経路の改変 | ダッシュボード・一覧・集計 → ローカル参照/行キャッシュ(recording_refs・interview_transcripts・recording_processing_requests・first_entered_at)で即返し、詳細・振り返り → コア API on demand(障害時デグレード) |
| 録画の外部共有 | OtArchiveAccess(URLキー+アクセスキー+期限の公開共有)は www 残置のまま、参照先を recording_uuid/コア管理の加工動画へ対応(現行のポリモーフィック参照の置換。DEC-08 の責務分界どおり閲覧可否はテナント側) |
| 保護加工の依頼管理 | recording_processing_requests のフロー実装: オペの「個人情報保護加工が必要」選択で作成(requested)→ Slack 依頼(現行踏襲)→ 人手加工後にコアへ手動アップロード(署名 PUT + complete)→ W7(available) 受信で completed 化・processed_recording_uuid 記録。トリムのみの加工は行を作らずコア直行 |
| 文字起こし横断検索(interview_transcripts) | 依存機能(Employee ジョブ横断検索・captions 書き出し・cue 表示・要約/AI チャットボット入力)を interview_transcripts 読みに差し替え(1 文字起こし=1 行・現行の 64KB チャンク分割は踏襲しない・検索は status: done 条件)。二重運用期は旧 transcription_request_analyses とアプリ層 2 系統検索で research_project id を union(dual-write なし・1 ジョブ=1 エンジンのためマージがクリーンに成立) |
| レガシー整理 | belongs_to :ot_archive 廃止 → recording_uuid 参照、旧 Room/OtSession/OtArchive の read-only 化。応募フロー内回線テスト(AnswerConnectivity・「事前質問の録画」)は未使用のため廃止(P6 の OpenTok 解約のブロッカー解消)。projects_controller.rb:18 の includes(room: :ot_archives) prefetch は dead code につき削除(view の room = slot.room は未使用) |
レビュー稼働 10 人日幅 9〜12 人日 / 想定カレンダー 11〜15 営業日
Phase 6: 並行稼働・移行・カットオーバー
ゴール:案件単位で新旧を切り替えながら安全に全面移行し、OpenTok を廃止する。
| 作業項目 | 内容 |
|---|---|
| E2E リハーサル | 全ロール同席の模擬インタビュー(作成→配布→準備→参加→振り返りの一気通貫)を stg で実施 |
| パイロット | webrtc_engine フラグで社内テスト案件 → 限定本番案件の順に適用(切替単位: Project 系=project 単位・即席=フォルダ単位〈extemporary_folders.webrtc_engine・枠単位の混在不可〉)。どの案件/フォルダをどちらのエンジンで回すかの運用ルールを策定し、安定後に新規案件・新規フォルダの default engine を livekit へ切替。ロールバック手順を用意 |
| 二重運用監視 | 新旧の品質・障害・コスト比較、W1〜W7 配信成功率・録画成功率の監視 |
| 移行 | 新規案件から新基盤へ。過去データ(旧録画・チャット・入退室)の core 移送 or read-only 残置は未決定・別途確定(即席は既存データ移行なしで確定済み)。全案件切替後に OpenTok 廃止・解約、legacy Room/OtSession/OtArchive は read-only 化 → アーカイブ。完全移行後に文字起こし検索を一本化: transcription_request_analyses → interview_transcripts へバックフィル(チャンク行を連結して 1 行化)し 2 系統検索コードを退役(legacy 参照キー設計はデータ移行範囲の決定と連動) |
| レガシー退役(OpenTok 廃止時) | OpentokRoom アダプタ・webrtc_engine 分岐・レガシー読み取り経路の除去。即席は extemporary_slots/extemporary_project_handouts を read-only 化 → 削除、www 即席入室ページ(10桁トークン)の廃止、フォルダ検索(SearchCop)の旧属性(extemporary_slots.*)削除 |
| 運用整備 | ランブック、オンコール体制、アラート閾値の本番調整、テナント/APIキーの運用手順 |
レビュー稼働 5 人日幅 4〜6 人日 / 想定カレンダー 12〜24 営業日
3. 工数サマリー
| フェーズ | 内容 | レビュー稼働(人日) | 幅 | 想定カレンダー(営業日) |
|---|---|---|---|---|
| Phase 0 | 環境整備・契約凍結(0-1〜0-6) | 4 | 3〜5 | 6〜9 |
| レーンC | インフラ構築(AWS/Cloudflare 基盤・Phase 0 と並走) | 1 | 1〜2 | 3〜5(並走) |
| Phase 1 | コアAPI(ルーム・認可・入退室)+ 管理画面(§1.3)基盤(署名鍵管理含む・参加トークン発行 API 廃止) | 8 | 6〜10 | 8〜13 |
| Phase 2 | インタビュールーム実装(SPA+チャット・現行踏襲UI含む)+ 管理画面 Handout 追加 | 13 | 11〜15 | 13〜19 |
| Phase 3 | 録画・文字起こしパイプライン(翻訳・話者分離・加工動画含む) | 8 | 6〜10 | 9〜13 |
| Phase 4 | 接続テスト | 4 | 3〜5 | 5〜7 |
| Phase 5 | minedia-www 連携改修(運用画面の API 経由化含む) | 10 | 9〜12 | 11〜15 |
| Phase 6 | 並行稼働・移行・カットオーバー | 5 | 4〜6 | 12〜24 |
| 小計 | 53 | 43〜65 | — | |
| バッファ(15%) | 未決事項の確定・仕様手戻り・LiveKit 検証 | 8 | — | — |
| 合計 | 61 人日 | — | — |
本ページは実査機能 新基盤(OpenTok → LiveKit 移行)の開発計画書です。3レーン並走の Phase 0〜6 と工数サマリーを整理しています。