Skip to content

1日サマリ機能 #607

@Kewton

Description

@Kewton

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/beforeDate型で受け取り、内部で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バージョン依存がない(注: AbortControllersignalexecFileの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要約の同時実行制御

  • globalThisisGeneratingDailySummary: 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要約のプロンプト注入リスク軽減策

ユーザーメッセージをプロンプトに埋め込むため、以下の軽減策を実装する:

  1. 構造化プロンプト設計: システムプロンプト(要約指示)とユーザーデータ(メッセージ履歴)を明確に分離する。ユーザーデータは<user_data>...</user_data>タグで囲み、AIにデータとして扱うよう指示する
  2. 入力サニタイズ: メッセージの制御文字(\x00-\x1f)を除去してからプロンプトに埋め込む
  3. 生成結果バリデーション: 想定外の長さ(例: MAX_MESSAGE_LENGTHの2倍以上)の出力を検出した場合はエラーとする

実装タスク

  • 全worktree横断用の新関数getMessagesByDateRange(db, { after, before, includeArchived })新設(chat-db.ts、after/beforeはDate型、内部でgetTime()変換)
  • 全worktree横断用インデックス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追加
  • 全worktree横断の日別サマリ取得API新設(/api/daily-summary)、同時実行制御(globalThisフラグ + declare global型宣言 + 429応答)を含む
  • AI要約レポート生成機能実装(対応ツール: claude, codex, copilot。UIで選択制限)
  • AI要約のタイムアウト制御実装(setTimeout + child.kill()方式、SUMMARY_GENERATION_TIMEOUT_MS。getActiveProcesses() Map整合性のテスト含む)
  • AI要約のプロンプト注入リスク軽減策実装(構造化プロンプト、入力サニタイズ、出力バリデーション)
  • Review画面にページレベルタブ(review | report)追加
  • Reportタブ内の日付ピッカー + 前後ボタンUI実装
  • レポートDB保存・編集機能実装
  • 既存テスト修正(ReviewPage-stalled.test.tsx等、ページレベルタブ追加に伴うDOM構造変更への対応)
  • ユニットテスト追加

受入条件

基本機能

  • Review画面にページレベルタブ(Review / Report)が表示される
  • Reviewタブ内には既存の3フィルタ(in_review / approval / stalled)がそのまま動作する
  • 日付指定(カレンダー + 前後ボタン)で作業ログを確認できる
  • AI要約レポートが生成される(claude, codex, copilotから選択可能)
  • gemini, vibe-local, opencodeはAI要約ツールとして選択不可である
  • レポートの編集・保存ができる(daily_reportsテーブルに永続化)
  • copilot指定時にモデル選択ができる(CMATE.mdのcopilot --model構文と同様)

エッジケース

  • 指定日にメッセージが0件の場合、「該当日のメッセージはありません」と表示される
  • archivedメッセージは要約対象に含めない(ACTIVE_FILTERを適用)
  • 削除済みworktreeのメッセージも要約対象に含める(メッセージはDB上に残存するため)
  • AI要約生成中はローディング表示され、タイムアウト(SUMMARY_GENERATION_TIMEOUT_MS = 60秒、setTimeout + child.kill()で制御)時にエラーメッセージが表示される
  • レポート編集中に再生成ボタンを押した場合、上書き確認ダイアログが表示される
  • AI要約の同時実行リクエストには429 Too Many Requestsが返される

テスト

  • ユニットテストが追加されている
  • 既存テスト(ReviewPage-stalled.test.tsx含む)が全てパスする

影響範囲

変更対象ファイル

ファイル 変更内容
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): GetMessagesOptionsafterパラメータ追加方針を明記。全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()変換する既存パターンとの整合性、タイムゾーン(サーバーローカル)の扱いを追記

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions