Note: このIssueは 2026-04-02 にStage 7レビュー結果を反映して更新されました。
詳細: dev-reports/issue/607/issue-review/
概要
指定した日の作業ログをAIで要約しレポートを生成する機能。Review画面に「Report」タブを追加し、日別の作業サマリを確認・編集できるようにする。
主要機能:
- 全worktree横断での日別作業ログ取得
- AIによるメッセージ履歴の要約レポート生成(対応ツール: claude, codex, copilot。copilotはモデル指定対応)
- レポートのDB保存・編集機能
- カレンダー(日付ピッカー + 前後ボタン)で日付指定
背景・課題
- 1日の終わりに何をやったか確認・報告したいが、各worktreeを個別に確認する必要がある
- チームメンバーと作業進捗を共有する際、サマリがないため手動でまとめる必要がある
提案する解決策
Review画面に「Report」タブを追加し、日別作業サマリ機能を提供する。
タブ構造の設計
Review画面にページレベルのタブ('review' | 'report')を導入する:
- reviewタブ: 既存のReviewFilter型(
'in_review' | 'approval' | 'stalled')による3つのサブフィルタをそのまま配置
- reportタブ: 日別サマリ生成・閲覧画面
既存のReviewFilter型は変更せず、ページレベルタブとReviewFilterを分離した設計とする。これにより既存のReview機能への影響を最小化する。
Reportタブのdeep link設計: ページレベルタブの切り替えはReactのstate管理のみで行い、URLパラメータ(?tab=report等)は使用しない。そのため、Header.tsx / GlobalMobileNav.tsxのisActive: p.startsWith('/review')判定への影響はない。
主要な変更点
- Review画面にページレベルタブ(review | report)を新設
- 日付指定 UI(カレンダー + 前後ボタン)
- 全worktree横断用の新関数
getMessagesByDateRange()を新設し、日付範囲クエリを実現(既存のgetMessages()シグネチャは変更しない)
- 全worktree横断の日別サマリAPI(
/api/daily-summary?date=YYYY-MM-DD)を新設
- AI要約レポート生成(対応ツール: claude, codex, copilot。copilotはCMATE.mdと同様に --model でモデル指定対応)
- gemini, vibe-local, opencodeは非対応(UIで選択不可とする)
- レポートのDB保存・編集機能(
daily_reportsテーブル新設)
日付範囲クエリの設計
- 既存の
getMessages(worktreeId, options)のシグネチャは変更しない(worktreeIdは必須引数のまま維持)
- 全worktree横断用に新関数
getMessagesByDateRange(db, { after, before, includeArchived })を新設する
after/beforeはDate型で受け取り、内部でgetTime()によりUnix epoch ms(INTEGER)に変換してSQLiteクエリに渡す(既存のgetMessages()のbefore?.getTime()と同パターン)
- タイムゾーンの扱い:
startOfDay(date)/endOfDay(date)の算出はサーバーのローカルタイムゾーンで行う(既存のchat-db.tsのDate.now()によるtimestamp記録と整合させる)
- この関数はworktree_idでWHERE句を絞り込まず、
idx_messages_timestampインデックスを活用する
- 既存の
getMessages()の呼び出し元7箇所(messages/route.ts, send/route.ts, worktree-status-helper.ts等)への影響を回避する
- 日別取得:
after = startOfDay(date), before = endOfDay(date) の組み合わせで1日分を取得
- 全worktree横断クエリのため、
idx_messages_timestamp(timestampのみ)インデックスを新設する
- 既存の
idx_messages_worktree_timeは(worktree_id, timestamp DESC)であり、全worktree横断クエリには不向き
レポート保存テーブル設計
CREATE TABLE daily_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL, -- YYYY-MM-DD形式
content TEXT NOT NULL, -- レポート本文(Markdown)
generated_by_tool TEXT NOT NULL, -- 生成に使用したCLIツール(claude, codex, copilot)
model TEXT, -- copilot使用時のモデル名(nullable)
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE UNIQUE INDEX idx_daily_reports_date ON daily_reports(date);
UNIQUE制約の仕様: idx_daily_reports_dateはdate単体のUNIQUE制約とし、1日1レポートの仕様とする(意図的設計)。異なるツールで再生成した場合は上書き(UPSERT)される。将来的にリポジトリ別レポートが必要になった場合は、UNIQUE制約を(date, repository_id)に拡張する設計オプションがあるが、現時点ではdate単体とする。
AI要約の仕様
- 対応CLIツール: claude, codex, copilot のみ
- gemini, vibe-local, opencodeは要約タスクの品質・信頼性が未検証のため、初期リリースでは対応スコープ外とする(注:
executeClaudeCommand()のexecモード自体はbuildCliArgs()が全6ツールに対応しているが、要約用途での動作検証が済んでいないため限定する)
- UIではこれら3ツールのみ選択可能とする
- cwd:
process.cwd()(CommandMateサーバーのプロジェクトルート)を使用する。理由: AI要約タスクではメッセージ履歴をプロンプトに埋め込んで実行するため、cwdからのファイルアクセスは発生しない。複数リポジトリが登録されている場合でも、特定リポジトリのパスを選択する必要がない
- 入力データ形式: 日付範囲で取得したメッセージをworktree別にグループ化し、以下のフォーマットでプロンプトに埋め込む
## Worktree: {branch_name}
### User: {message}
### Assistant: {response}
...
- MAX_MESSAGE_LENGTH超過時の対処: メッセージ量が
MAX_MESSAGE_LENGTHを超える場合は、古いメッセージから切り捨てて直近の内容を優先する。切り捨てが発生した場合はレポートに「一部メッセージが省略されています」旨を注記する
AI要約のタイムアウト制御
- タイムアウト値:
SUMMARY_GENERATION_TIMEOUT_MS = 60_000(60秒)。review-config.tsに定数を定義する
- 実装レイヤー:
/api/daily-summaryのAPIルートハンドラ内でタイムアウト制御を行い、executeClaudeCommand()のexecFileプロセスを60秒で強制終了する。executeClaudeCommand()自体のEXECUTION_TIMEOUT_MS(15分)とは独立した、API層でのタイムアウト制御とする
- 実装方針:
setTimeout + child.kill()パターンを採用する。executeClaudeCommand()が返すPromiseをラップし、60秒経過後にgetActiveProcesses()経由でchild processを取得してkillする。この方式は既存のgetActiveProcesses()パターンと整合し、Node.jsバージョン依存がない(注: AbortControllerのsignalをexecFileのoptionsに渡す方式はNode.js 15.4+が必要であり、プロジェクトのNode.jsバージョン要件が明示されていないため、setTimeout + child.kill()方式を優先する)。タイムアウト発生時はクライアントに504 Gateway Timeout相当のエラーレスポンスを返す
- プロセスMap整合性:
child.kill()によるプロセス終了時、child.on('exit')ハンドラが発火しgetActiveProcesses() Mapからの削除が行われる。kill()はSIGTERMを送信するためexitイベントは確実に発火する。この整合性は実装時にテストケースで検証する
AI要約の同時実行制御
globalThisにisGeneratingDailySummary: booleanフラグを持ち、生成中の重複リクエストは429 Too Many Requestsを返す
- TypeScript型宣言:
declare global { var __isGeneratingDailySummary: boolean | undefined; }をsrc/app/api/daily-summary/route.ts内に記載する(既存のclaude-executor.tsでのdeclare globalと同様のパターン)
- これにより、複数ユーザーの同時リクエストや日付変更時の連続リクエストによる複数CLIプロセス同時起動を防止する
- 既存のスケジュール実行(job-executor.ts)の
isExecutingフラグと同様のパターン
AI要約のプロンプト注入リスク軽減策
ユーザーメッセージをプロンプトに埋め込むため、以下の軽減策を実装する:
- 構造化プロンプト設計: システムプロンプト(要約指示)とユーザーデータ(メッセージ履歴)を明確に分離する。ユーザーデータは
<user_data>...</user_data>タグで囲み、AIにデータとして扱うよう指示する
- 入力サニタイズ: メッセージの制御文字(
\x00-\x1f)を除去してからプロンプトに埋め込む
- 生成結果バリデーション: 想定外の長さ(例: MAX_MESSAGE_LENGTHの2倍以上)の出力を検出した場合はエラーとする
実装タスク
受入条件
基本機能
エッジケース
テスト
影響範囲
変更対象ファイル
| ファイル |
変更内容 |
| src/app/review/page.tsx |
ページレベルタブ(review / report)追加 |
| src/lib/db/chat-db.ts |
getMessagesByDateRange()新関数の追加(after/beforeはDate型、内部でgetTime()変換。既存のgetMessages()シグネチャは変更しない) |
| src/lib/db/migrations/(新規ファイル) |
idx_messages_timestampインデックス追加、daily_reportsテーブルマイグレーション |
| src/lib/db/migrations/runner.ts |
CURRENT_SCHEMA_VERSIONの更新 |
| src/lib/db/migrations/index.ts |
新規マイグレーションファイルの登録 |
| src/lib/db/daily-report-db.ts |
新設: daily_reportsテーブルのCRUD操作 |
| src/lib/db/db.ts |
daily-report-db.tsのexport追加(バレルファイル) |
| src/app/api/daily-summary/route.ts |
新設: 全worktree横断の日別サマリAPI(同時実行制御・declare global型宣言・setTimeout+child.kill()タイムアウト含む) |
| src/components/review/ReportTab.tsx |
新設: Reportタブコンポーネント |
| src/components/review/ReportDatePicker.tsx |
新設: 日付ピッカーUI |
| src/config/review-config.ts |
Review画面設定定数にReportタブ関連定数追加(SUMMARY_GENERATION_TIMEOUT_MS等) |
| tests/unit/components/review/ReviewPage-stalled.test.tsx |
ページレベルタブ追加に伴うテスト修正 |
変更不要なファイル(確認済み)
| ファイル |
理由 |
| src/middleware.ts |
既存の認証matcher設定により/api/daily-summaryにも自動適用される。追加対応不要 |
| src/components/layout/AppShell.tsx |
Review画面のレイアウト設定は変更不要(タブはページ内部で完結) |
| src/hooks/useLayoutConfig.ts |
同上 |
| src/components/layout/Header.tsx |
Reportタブはstate管理のみ(URLパラメータ不使用)のためisActive判定への影響なし |
| src/components/mobile/GlobalMobileNav.tsx |
同上 |
| src/lib/db/db-migration-path.ts |
DBファイルの物理パス移行を担うモジュールであり、テーブルマイグレーションとは役割が異なる |
関連コンポーネント
src/lib/db/chat-db.ts - メッセージDB(新関数getMessagesByDateRange()、既存のidx_messages_worktree_timeインデックス)
src/lib/session/claude-executor.ts - AI要約実行(claude -p / codex exec / copilot 経由)。daily-summary APIからの実行時もgetActiveProcesses() Mapに登録されるため、既存のクリーンアップ機構(stopAllSchedules, resource-cleanup.ts)でカバーされる
src/lib/log-export-sanitizer.ts - レポートエクスポート時のサニタイズ
src/config/review-config.ts - Review画面設定定数
レビュー履歴
Stage 1 (2026-04-02)
- S1-001 (Must Fix):
GetMessagesOptionsにafterパラメータ追加方針を明記。全worktree横断用インデックス追加を設計に記載
- S1-002 (Must Fix):
daily_reportsテーブル設計を追加。DBマイグレーション関連ファイルを変更対象に追加
- S1-003 (Must Fix): ページレベルタブ(
'review' | 'report')導入方針を明記。既存ReviewFilter型との分離設計を記載
- S1-004 (Should Fix): APIを全worktree横断の
/api/daily-summary?date=YYYY-MM-DDに変更
- S1-005 (Should Fix): AI要約のcwd決定方法、入力データ形式、MAX_MESSAGE_LENGTH超過時の対処を追記
- S1-006 (Should Fix): AI要約対応ツールをclaude, codex, copilotの3つに限定。非対応ツールはUI選択不可と明記
- S1-007 (Should Fix):
src/lib/log-manager.tsの参照を関連コンポーネントから削除(日付範囲クエリとの関連が薄いため)
- S1-008 (Should Fix): エッジケースの受入条件を追加(0件メッセージ、archived除外、削除済みworktree、タイムアウト、上書き確認)
Stage 3 (2026-04-02) - 影響範囲レビュー
- S3-001 (Must Fix):
getMessages()シグネチャ変更から、全worktree横断用の新関数getMessagesByDateRange()新設方針に修正。既存呼び出し元への影響を回避
- S3-002 (Must Fix): マイグレーションファイル配置先を
db-migration-path.tsからsrc/lib/db/migrations/配下に修正。runner.tsのCURRENT_SCHEMA_VERSION更新を明記
- S3-003 (Must Fix):
src/lib/db/db.tsバレルファイルへのexport追加を変更対象に追加
- S3-004 (Should Fix): AI要約の同時実行制御(globalThisフラグ + 429 Too Many Requests)を設計に追加
- S3-005 (Should Fix): 既存テスト(ReviewPage-stalled.test.tsx)の修正を実装タスクに追加
- S3-006 (Should Fix): middleware.tsは変更不要(認証自動適用)であることを影響範囲に注記
- S3-007 (Should Fix): ReportタブのURL設計(state管理のみ、URLパラメータ不使用)を明記
- S3-008 (Should Fix): daily_reportsテーブルのUNIQUE制約(date)の仕様を明確化(1日1レポート、意図的設計)
- S3-009 (Should Fix): AI要約のプロンプト注入リスク軽減策(構造化プロンプト、入力サニタイズ、出力バリデーション)を追記
Stage 5 (2026-04-02) - 通常レビュー(2回目)
- S5-001 (Should Fix): AI要約非対応ツールの理由を修正。「execモードに対応していない」から「要約タスクの品質・信頼性が未検証のため初期リリースではスコープ外」に変更。buildCliArgs()が全6ツール対応済みである事実を正しく反映
- S5-002 (Should Fix): AI要約タイムアウト(60秒)の実装レイヤーを明確化。API層のAbortControllerでexecFileプロセスを強制終了する設計を追記。SUMMARY_GENERATION_TIMEOUT_MSをreview-config.tsに定義
- S5-003 (Should Fix): cwdの決定ロジックを
process.cwd()に明確化。AI要約タスクではファイルアクセスが不要なため、特定リポジトリのパス選択は不要である旨を明記
Stage 7 (2026-04-02) - 影響範囲レビュー(2回目)
- S7-001 (Should Fix): AbortControllerのsignalをexecFileに渡す方式からsetTimeout + child.kill()方式に変更。Node.js 15.4+依存を回避し、既存のgetActiveProcesses()パターンとの整合性を確保
- S7-002 (Should Fix): child.kill()後のgetActiveProcesses() Map整合性を明記。child.on('exit')ハンドラによるMap削除の確実性とテストケースでの検証を追記
- S7-003 (Should Fix): globalThis.__isGeneratingDailySummaryのdeclare global型宣言の配置先をroute.ts内に決定(claude-executor.tsと同パターン)
- S7-004 (Should Fix): getMessagesByDateRange()のafter/before引数をDate型と明記。内部でgetTime()変換する既存パターンとの整合性、タイムゾーン(サーバーローカル)の扱いを追記
概要
指定した日の作業ログをAIで要約しレポートを生成する機能。Review画面に「Report」タブを追加し、日別の作業サマリを確認・編集できるようにする。
主要機能:
背景・課題
提案する解決策
Review画面に「Report」タブを追加し、日別作業サマリ機能を提供する。
タブ構造の設計
Review画面にページレベルのタブ(
'review' | 'report')を導入する:'in_review' | 'approval' | 'stalled')による3つのサブフィルタをそのまま配置既存のReviewFilter型は変更せず、ページレベルタブとReviewFilterを分離した設計とする。これにより既存のReview機能への影響を最小化する。
Reportタブのdeep link設計: ページレベルタブの切り替えはReactのstate管理のみで行い、URLパラメータ(
?tab=report等)は使用しない。そのため、Header.tsx / GlobalMobileNav.tsxのisActive: p.startsWith('/review')判定への影響はない。主要な変更点
getMessagesByDateRange()を新設し、日付範囲クエリを実現(既存のgetMessages()シグネチャは変更しない)/api/daily-summary?date=YYYY-MM-DD)を新設daily_reportsテーブル新設)日付範囲クエリの設計
getMessages(worktreeId, options)のシグネチャは変更しない(worktreeIdは必須引数のまま維持)getMessagesByDateRange(db, { after, before, includeArchived })を新設するafter/beforeはDate型で受け取り、内部でgetTime()によりUnix epoch ms(INTEGER)に変換してSQLiteクエリに渡す(既存のgetMessages()のbefore?.getTime()と同パターン)startOfDay(date)/endOfDay(date)の算出はサーバーのローカルタイムゾーンで行う(既存のchat-db.tsのDate.now()によるtimestamp記録と整合させる)idx_messages_timestampインデックスを活用するgetMessages()の呼び出し元7箇所(messages/route.ts, send/route.ts, worktree-status-helper.ts等)への影響を回避するafter = startOfDay(date),before = endOfDay(date)の組み合わせで1日分を取得idx_messages_timestamp(timestampのみ)インデックスを新設するidx_messages_worktree_timeは(worktree_id, timestamp DESC)であり、全worktree横断クエリには不向きレポート保存テーブル設計
UNIQUE制約の仕様:
idx_daily_reports_dateはdate単体のUNIQUE制約とし、1日1レポートの仕様とする(意図的設計)。異なるツールで再生成した場合は上書き(UPSERT)される。将来的にリポジトリ別レポートが必要になった場合は、UNIQUE制約を(date, repository_id)に拡張する設計オプションがあるが、現時点ではdate単体とする。AI要約の仕様
executeClaudeCommand()のexecモード自体はbuildCliArgs()が全6ツールに対応しているが、要約用途での動作検証が済んでいないため限定する)process.cwd()(CommandMateサーバーのプロジェクトルート)を使用する。理由: AI要約タスクではメッセージ履歴をプロンプトに埋め込んで実行するため、cwdからのファイルアクセスは発生しない。複数リポジトリが登録されている場合でも、特定リポジトリのパスを選択する必要がないMAX_MESSAGE_LENGTHを超える場合は、古いメッセージから切り捨てて直近の内容を優先する。切り捨てが発生した場合はレポートに「一部メッセージが省略されています」旨を注記するAI要約のタイムアウト制御
SUMMARY_GENERATION_TIMEOUT_MS = 60_000(60秒)。review-config.tsに定数を定義する/api/daily-summaryのAPIルートハンドラ内でタイムアウト制御を行い、executeClaudeCommand()のexecFileプロセスを60秒で強制終了する。executeClaudeCommand()自体のEXECUTION_TIMEOUT_MS(15分)とは独立した、API層でのタイムアウト制御とするsetTimeout+child.kill()パターンを採用する。executeClaudeCommand()が返すPromiseをラップし、60秒経過後にgetActiveProcesses()経由でchild processを取得してkillする。この方式は既存のgetActiveProcesses()パターンと整合し、Node.jsバージョン依存がない(注:AbortControllerのsignalをexecFileのoptionsに渡す方式はNode.js 15.4+が必要であり、プロジェクトのNode.jsバージョン要件が明示されていないため、setTimeout+child.kill()方式を優先する)。タイムアウト発生時はクライアントに504 Gateway Timeout相当のエラーレスポンスを返すchild.kill()によるプロセス終了時、child.on('exit')ハンドラが発火しgetActiveProcesses()Mapからの削除が行われる。kill()はSIGTERMを送信するためexitイベントは確実に発火する。この整合性は実装時にテストケースで検証するAI要約の同時実行制御
globalThisにisGeneratingDailySummary: booleanフラグを持ち、生成中の重複リクエストは429 Too Many Requestsを返すdeclare global { var __isGeneratingDailySummary: boolean | undefined; }をsrc/app/api/daily-summary/route.ts内に記載する(既存のclaude-executor.tsでのdeclare globalと同様のパターン)isExecutingフラグと同様のパターンAI要約のプロンプト注入リスク軽減策
ユーザーメッセージをプロンプトに埋め込むため、以下の軽減策を実装する:
<user_data>...</user_data>タグで囲み、AIにデータとして扱うよう指示する\x00-\x1f)を除去してからプロンプトに埋め込む実装タスク
getMessagesByDateRange(db, { after, before, includeArchived })新設(chat-db.ts、after/beforeはDate型、内部でgetTime()変換)idx_messages_timestamp追加(migrations/配下に新規マイグレーションファイル作成)daily_reportsテーブル新設マイグレーション追加(migrations/配下、runner.tsのCURRENT_SCHEMA_VERSION更新)daily-report-db.ts新設(daily_reportsテーブルのCRUD操作)src/lib/db/db.tsバレルファイルにdaily-report-db.tsのexport追加/api/daily-summary)、同時実行制御(globalThisフラグ + declare global型宣言 + 429応答)を含む受入条件
基本機能
エッジケース
テスト
影響範囲
変更対象ファイル
getMessagesByDateRange()新関数の追加(after/beforeはDate型、内部でgetTime()変換。既存のgetMessages()シグネチャは変更しない)idx_messages_timestampインデックス追加、daily_reportsテーブルマイグレーション変更不要なファイル(確認済み)
/api/daily-summaryにも自動適用される。追加対応不要関連コンポーネント
src/lib/db/chat-db.ts- メッセージDB(新関数getMessagesByDateRange()、既存のidx_messages_worktree_timeインデックス)src/lib/session/claude-executor.ts- AI要約実行(claude -p/codex exec/copilot経由)。daily-summary APIからの実行時もgetActiveProcesses()Mapに登録されるため、既存のクリーンアップ機構(stopAllSchedules, resource-cleanup.ts)でカバーされるsrc/lib/log-export-sanitizer.ts- レポートエクスポート時のサニタイズsrc/config/review-config.ts- Review画面設定定数レビュー履歴
Stage 1 (2026-04-02)
GetMessagesOptionsにafterパラメータ追加方針を明記。全worktree横断用インデックス追加を設計に記載daily_reportsテーブル設計を追加。DBマイグレーション関連ファイルを変更対象に追加'review' | 'report')導入方針を明記。既存ReviewFilter型との分離設計を記載/api/daily-summary?date=YYYY-MM-DDに変更src/lib/log-manager.tsの参照を関連コンポーネントから削除(日付範囲クエリとの関連が薄いため)Stage 3 (2026-04-02) - 影響範囲レビュー
getMessages()シグネチャ変更から、全worktree横断用の新関数getMessagesByDateRange()新設方針に修正。既存呼び出し元への影響を回避db-migration-path.tsからsrc/lib/db/migrations/配下に修正。runner.tsのCURRENT_SCHEMA_VERSION更新を明記src/lib/db/db.tsバレルファイルへのexport追加を変更対象に追加Stage 5 (2026-04-02) - 通常レビュー(2回目)
process.cwd()に明確化。AI要約タスクではファイルアクセスが不要なため、特定リポジトリのパス選択は不要である旨を明記Stage 7 (2026-04-02) - 影響範囲レビュー(2回目)