プロジェクト

全般

プロフィール

機能 #10306

未完了

PathCollector MVP実装:Web行動記録基盤(PC・iPhone・Android対応/MCP連携)

Redmine Admin さんが18日前に追加. 18日前に更新.

ステータス:
実行中
優先度:
通常
担当者:
-
開始日:
2026-07-21
期日:
進捗率:

0%

予定工数:
from_agent:
to_agent:
lock_required:
context_json:
session_id:
priority_custom:
start_time:
end_time:
execution_time:
acceptance_criteria:
assigned_role:
human_gate:
gate_type:
chain_root_id:
wave_id:

説明

目的

PathCollectorを、対象Webサイト上の行動を端末横断で時系列記録し、個人情報を意味付きマスクしたうえで保存し、MCP経由で検索・取得・解析できる画面なしのサービスとして実装する。

最上位ゴールは「Web行動記録」であり、GA4/GSCの代替ではなく、DOM状態・DOM変化・ユーザー操作・遷移・エラーを後から解析可能な形で保持すること。

担当ワーカー

Claude Code(主担当)
推奨モデル: Sonnet

MVPスコープ

1. Collector SDK

  • TypeScript + rrweb
  • PC / macOS / iPhone / iPad / Android対応
  • Chrome / Edge / Firefox / Safari / iOS Safari / iOS Chrome / Android Chrome
  • 初期DOMスナップショット
  • DOM追加・削除・属性・テキスト変更
  • click / tap / input / change / submit / scroll / navigation / focus / blur
  • visibilitychange / pagehide / orientationchange / online / offline
  • JavaScript error / unhandled rejection
  • SPA遷移対応
  • touchend・pointerup・clickの重複排除、モバイル操作をtapへ正規化
  • 5秒、50イベント、256KB、visibilitychange、pagehideでflush
  • 終了時はsendBeacon優先、失敗時fetch keepalive

2. プライバシー

  • クライアント側とサーバー側の二段階マスキング
  • [MASKED_NAME] / [MASKED_EMAIL] / [MASKED_PHONE] / [MASKED_ADDRESS] / [MASKED_CARD_NUMBER] / [MASKED_PASSWORD] / [MASKED_TOKEN] 等
  • input type、name、id、autocomplete、aria-label、周辺ラベル、CSS selector、正規表現による検出
  • hidden input value、password、script/style本文、認証情報を保存しない
  • data-pc-mask、data-pc-ignore対応
  • URL query/fragment内の機微情報除去
  • 生のPIIをログへ出力しない
  • ブラウザフィンガープリント禁止
  • IPを行動分析DBへ保存しない

3. 同意・オプトアウト

  • ConsentState: unknown / granted / denied / essential_only
  • consentMode: explicit / implicit
  • optOut / optIn / isOptedOut
  • オプトアウト時は録画停止、未送信バッファ破棄、visitor/session ID削除
  • pc_opt_outをファーストパーティCookieまたはlocalStorageへ保存

4. API

Collector API:

  • POST /v1/collect
  • POST /v1/consent
  • POST /v1/opt-out
  • GET /v1/sdk/config/{write_key}

Analysis API:

  • GET /v1/projects/{project_id}/sessions
  • GET /v1/projects/{project_id}/sessions/{session_id}
  • GET /v1/projects/{project_id}/sessions/{session_id}/timeline
  • POST /v1/projects/{project_id}/analysis/pages
  • POST /v1/projects/{project_id}/analysis/events
  • POST /v1/projects/{project_id}/analysis/paths
  • POST /v1/projects/{project_id}/analysis/funnel
  • POST /v1/projects/{project_id}/analysis/dropoffs
  • POST /v1/projects/{project_id}/analysis/scroll-depth

Privacy API:

  • DELETE /v1/projects/{project_id}/sessions/{session_id}
  • DELETE /v1/projects/{project_id}/visitors/{visitor_id}

System API:

  • GET /health
  • GET /ready
  • GET /metrics

5. MCP Server

画面なし。MCPを主要操作面とする。
実装ツール:

  • pathcollector_search_sessions
  • pathcollector_get_session
  • pathcollector_get_web_behavior
  • pathcollector_get_timeline
  • pathcollector_summarize_web_behavior
  • pathcollector_get_page_metrics
  • pathcollector_analyze_paths
  • pathcollector_analyze_funnel
  • pathcollector_analyze_dropoffs
  • pathcollector_get_errors
  • pathcollector_delete_session
  • pathcollector_delete_visitor
  • pathcollector_get_schema
  • pathcollector_health

通常はrrweb生イベントではなく正規化済みuser_actionsを返す。生DOM取得はraw_events:read scope保有時のみ許可する。

6. DB

PostgreSQL 16以上。
最低限のテーブル:

  • organizations
  • projects
  • project_domains
  • api_credentials
  • privacy_rules
  • anonymous_visitors
  • sessions
  • page_views
  • event_batches
  • raw_events
  • user_actions
  • action_effects
  • privacy_findings
  • daily_metrics
  • deletion_requests
  • audit_logs

主識別単位はsession_id、補助識別単位はvisitor_id。複数端末をまたぐ人物統合は行わない。

7. 操作と画面変化の関連付け

ユーザー操作後2秒以内に発生したDOM変更をaction_effect候補として関連付ける。
記録項目:

  • added_node_count
  • removed_node_count
  • changed_attribute_count
  • changed_text_count
  • navigation_occurred
  • validation_error_occurred
  • javascript_error_occurred
  • affected_selectors

技術スタック

  • pnpm workspace
  • Node.js + TypeScript
  • Fastify
  • rrweb
  • PostgreSQL 16
  • Drizzle ORM推奨
  • Zod
  • MCP TypeScript SDK
  • Vitest
  • Playwright
  • Docker Compose
  • Nginx
  • JSON構造化ログ

リポジトリ構成

pathcollector/
├─ apps/api
├─ apps/mcp
├─ apps/worker
├─ apps/collector-sdk
├─ packages/database
├─ packages/domain
├─ packages/privacy
├─ packages/schemas
├─ packages/logger
├─ packages/config
├─ migrations
├─ tests/integration
├─ tests/e2e
├─ tests/privacy
├─ docs/api
├─ docs/mcp
└─ docker-compose.yml

実装順序

Phase 1:

  1. リポジトリ初期化
  2. PostgreSQLスキーマとmigration
  3. プロジェクト/APIキー登録CLI
  4. Collector SDK
  5. rrweb取得
  6. クライアントマスキング
  7. /v1/collect
  8. サーバー再マスキング
  9. raw_events保存

Phase 2:

  1. visitor/session/page_view生成
  2. click/tap/scroll/navigation/error正規化
  3. user_actions保存
  4. action_effects生成
  5. TTL削除

Phase 3:

  1. MCP認証
  2. セッション検索
  3. Web行動タイムライン
  4. ページ指標
  5. パス/ファネル/離脱分析
  6. 削除ツール

Phase 4:

  1. E2E
  2. PII漏えいテスト
  3. レート制限
  4. metrics
  5. バックアップ・復元手順
  6. API/MCPドキュメント

必須受入条件

  • Docker Composeで起動できる
  • /health と /ready が正常
  • テストサイトへ1タグで設置可能
  • Windows Chrome、macOS Safari、iPhone Safari、iPhone Chrome、Android Chromeで計測可能
  • タップが重複せず1操作として保存される
  • DOMスナップショット・DOM変更・スクロール・遷移・エラーを記録できる
  • SPA遷移を記録できる
  • 入力値を保存せず、入力行為とフィールド種別のみ記録できる
  • email、電話番号、氏名、password、tokenが保存前にマスクされる
  • data-pc-ignore配下が保存されない
  • 同意拒否・オプトアウト時に送信しない
  • MCPからWeb行動を時系列取得できる
  • MCPから「入口、閲覧順、スクロール、操作、DOM変化、離脱点、エラー直前行動」を取得できる
  • 権限外project_idを参照できない
  • TTL削除が稼働する
  • 削除操作が監査ログへ記録される
  • READMEに導入、起動、設定、テスト、MCP接続方法を記載

完了定義

対象サイトへJavaScriptタグを設置することで、PC・iPhone・Android・タブレット上のWeb行動を取得し、個人情報をマスクした状態で時系列保存し、MCP経由で検索・取得・解析できること。

開発上の制約

  • 管理画面は作らない
  • セッションリプレイUIは作らない
  • ヒートマップUIは作らない
  • 生PIIを一切永続化しない
  • 問題発生時は原因、影響、暫定対応

Redmine Admin さんが18日前に更新

  • ステータス新規 から 実行中 に変更

Claude Code(Sonnet)ワーカー agent-pool-1 に実装を委譲し、Phase 1を開始しました。ロック: pathcollector-implementation。初回成果は、リポジトリ初期化、DB migration、health/ready、最小Collector SDK、POST /v1/collect、マスキングテスト、Docker Compose起動、コミットまでです。

Redmine Admin さんが18日前に更新

【Phase1 状況確認】着手前に既存資産を調査した結果、/root/projects/pathcollector に既存リポジトリを発見(新規作成はしません)。Epic #10236 配下の旧サブタスク(#10237 PC1-01 DBスキーマ, #10238 PC1-02 プロジェクト/APIキーCLI, PC1-03 Collector SDK, PC1-04 rrweb統合, PC1-06 /v1/collect)で既に相当量が実装済みでした。

内訳:

  • master: リポジトリ骨格、DBスキーマ+migration(Drizzle, packages/database)、プロジェクト/APIキー登録CLI(apps/api/src/cli.ts) — マージ済み
  • ブランチ pc-track-b-sdk (worktree, 未マージ): Collector SDK基本実装+rrweb統合+クライアント側マスキング(apps/collector-sdk)
  • ブランチ pc-track-a-backend (worktree, 未マージ): POST /v1/collect実装(write_key検証・Origin検証・レート制限・gzip・HMAC・Zod検証・raw保存)

未実装/未存在: /health, /ready エンドポイント、docker-compose.yml(root)、apps/api用Dockerfile、packages/privacy(サーバー側再マスキングパッケージ、現状はcollector-sdk内のクライアント側マスキングのみ)。

競合サービス確認: 同VPS上に track-gufu (Redmine #9882, track.gufu.jp) という類似のFastify+PostgreSQLトラッキング基盤が別プロジェクトとして稼働中ですが、PathCollectorとは別プロジェクト・別ドメイン・別DBであり競合や統合の必要はないと判断しました(参考としてDocker構成パターンのみ踏襲)。

方針: 新規リポジトリは作らず、既存2ブランチをmasterへマージした上で不足分(/health, /ready, docker-compose, サーバー再マスキング接続)を実装し、Phase1初回成果として仕上げます。

Redmine Admin さんが18日前に更新

【Phase1 初回成果 完了】/root/projects/pathcollector master へコミット済み(HEAD: e70d5c5)。

実施内容:

  1. 既存の未マージブランチ pc-track-b-sdk(Collector SDK基本実装+rrweb統合)と pc-track-a-backend(POST /v1/collect)を master へマージ(競合なし)
  2. apps/api/src/routes/health.ts を新規追加: GET /health(liveness、DB非依存)と GET /ready(readiness、SELECT 1でDB到達性確認、失敗時503)
  3. docker-compose.yml + apps/api/Dockerfile + docker/api-entrypoint.sh を新規追加(PostgreSQL+APIの最小構成、起動時にmigration自動適用)
  4. docs/adr/0001-0005 を追加(曖昧点の安全側決定を記録)

テスト結果:

  • apps/collector-sdk: vitest 72 tests pass (masking.test.ts 16件含むマスキング単体テスト)
  • apps/api: typecheck pass、test:unit 74 tests pass、test:integration(ephemeral Docker Postgres) 33 tests pass(新規 health.test.ts 3件含む)
  • docker compose up -d --build で db healthyヺapi起動を確認。curlで /health ・ /ready が実際に200を返すことを確認
  • コンテナ内CLIでregister-projectを実行しwrite_keyを発行、そのwrite_keyでHMAC署名付きPOST /v1/collectを実行し202を確認、event_batchesに1件保存されることをDB直接確認
  • docker compose restart api 後もmigration再適用がエラーなく完了(冪等性確認)。検証後 docker compose down で後片付け済み

主な設計判断(詳細はdocs/adr):

  • ADR-0001: 新規リポジトリは作らず既存を継続利用
  • ADR-0002: 未マージブランチをマージ(再実装すると手戻りのため)
  • ADR-0003: Dockerの既定アドレスプールが枚枚の既存サービスで枯渇していたため 10.77.0.0/24 を明示指定。DBはホスト非公開、APIは127.0.0.1:3210のみ(ポート・サブネット競合確認済み)。公開ドメイン未指定のためproxy-network未参加
  • ADR-0004: /healthはliveness(DB不問)、/readyはreadiness(DB確認)で明確に分離
  • ADR-0005: packages/databaseがworkspace未使用+TS直接参照のため、Docker内もtsx直接実行を踏襲(ビルド手順を増やさない安全側選択)

競合サービス: 同VPS上のtrack-gufu(#9882)は別ドメイン・別DBの別プロジェクトであり競合なしと判断(ADR-0001参照)。

残作業(Phase1完了に向けて): チケット本文Phase1項目8「サーバー再マスキング」はapps/api側に未実装(現状はクライアントマスキング済みのペイロードをそのままevent_batchesに保存)。項目9「raw_events保存」はバッチ単位(event_batches)までで、raw_eventsテーブルへのイベント単位展開は未着手(既存実装のコミットメッセージでPC1-08相当として明記済み)。障害はなし。次ステップとしてこの2項目を実装しPhase1を完了させたい。

なお、旧worktree(/root/.worktrees/pc-track-a-backend, pc-track-b-sdk)はマージ済みのため不要ですが、他作業との競合を避けるため今回は削除せず残してあります。

他の形式にエクスポート: Atom PDF