feat(desktop): TODO Agent に cron 風スケジュール実行機能を追加#211
Conversation
TodoManager のサイドバーに「スケジュール」タブを追加し、 毎時 / 毎日 / 毎週 / 毎月 / cron 式 の UI ビルダーで作った定型 TODO を アプリ起動中に自動発火できるようにする。発火成功・スキップ・失敗は トーストで通知し、TodoManager が閉じていても届く。 - DB: todo_schedules テーブル + drizzle migration 0056 - main: scheduler (30秒間隔 tick) + schedule-store + trpc router - renderer: SchedulesSection (編集ダイアログ / 頻度ビルダー / 一覧) - 重複時は skip / queue をスケジュールごとに選択可能 - アプリ閉じている間の発火は v1 では対象外 (plan.md 参照)
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
📝 WalkthroughWalkthroughTODO エージェント向けのスケジュール実行機能を実装しました。定期的なタスク実行(時間ごと、日ごと、週ごと、月ごと、cron式)をスケジュール登録し、指定時刻に自動的にセッションを作成・実行する仕組みを追加しました。30秒ごとのポーリング、cron パース、スケジュール永続化、イベント通知を含みます。 Changes
Sequence Diagram(s)sequenceDiagram
participant Main as Main Process
participant Scheduler as TodoScheduler
participant Store as TodoScheduleStore
participant Sync as ScheduleSync
participant Supervisor as TodoSupervisor
participant Renderer as Renderer (Toast)
Main->>Scheduler: getTodoScheduler().start()
Scheduler->>Scheduler: setInterval (30s tick)
loop Every 30 seconds
Scheduler->>Store: listDue(now)
Store-->>Scheduler: due schedules[]
alt For each due schedule
Scheduler->>Scheduler: computeNextRunAt()
Scheduler->>Store: check lastRunSessionId (skip guard)
alt overlapMode === "skip" && session active
Scheduler->>Store: emitFire(skipped event)
else overlapMode === "queue" or no active session
opt autoSyncBeforeFire enabled
Scheduler->>Sync: autoSyncProjectMain(cwd)
Sync-->>Scheduler: ok | dirty | error
end
Scheduler->>Supervisor: createFromSchedule(template)
Supervisor->>Supervisor: start session async
Scheduler->>Store: recordRun(sessionId)
Scheduler->>Store: emitFire(triggered event)
Store->>Renderer: onFire subscription
Renderer->>Renderer: show success toast
end
end
end
Main->>Scheduler: before-quit → stop()
Scheduler-->>Main: cleanup complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
自己レビューで検出した 5 件の修正必須 + 2 件の修正推奨。 - listDue: composite index を実際に使う (enabled + lte(nextRunAt, now)) - stop() を before-quit で呼び、tick 内に isStopped ガードを追加。 closeLocalDb 後にセッション挿入する race を防ぐ - monthly の monthday=31 を月末クランプ (Feb 31 → Feb 28 等)。 以前は JS Date overflow で翌月にずれていた - supervisor.start 失敗時に failed event を再 emit。triggered のまま 残って UX 欺瞞になる問題を修正 - overlapMode='queue' は既存 TodoSupervisor の内部キューがそのまま 機能することをコード内コメントで明示 - cronstrue を `cronstrue/i18n` から `cronstrue` + ja ロケール単体に 変更し、renderer バンドルから他ロケールを除外 - ScheduleFireToasts の useSubscription に onError を追加
scheduler.fire() と trpc-router.create() の両方で TodoSession の約30 フィールドをコピペしていたのを、session-store の insertQueuedFromTemplate() に集約した。todo_sessions に列を足すときに 両方を書き換える必要が無くなる。
ユーザー要望により、スケジュールの一次キーを workspaceId から projectId に移行。ワークスペースは任意選択、省略時はプロジェクト本体 (main repo path) で実行する。 - todo_schedules: projectId を NOT NULL、workspaceId を NULL 許容に - UI: ScheduleEditorDialog に「プロジェクト」 dropdown を追加し 「実行対象」 dropdown に「プロジェクト本体 (main)」オプションを用意 - scheduler.fire(): workspaceId が null のときは ensureProjectBranchWorkspaceId() でプロジェクトの branch workspace を用意 (なければ lazy に作成) してセッションを attach - trpc schedule.list のキーを workspaceId → projectId に変更
自己レビューで指摘された副作用・整合性・読みやすさの改善。 - ensureProjectBranchWorkspaceId で branch workspace を materialize した後、既存 workspaces/procedures/create.ts と同じく兄弟の tabOrder を +1 で詰める。これまで tabOrder=0 の既存 worktree workspace と 衝突して サイドバー並び順が非決定的になっていた - ScheduleEditorDialog の "__main__" マジック文字列を MAIN_REPO_SENTINEL 定数に抽出 - schedule-store から未使用の listForWorkspace を削除 (現在は listForProject のみ使用) - schedule update input から projectId を omit。プロジェクト移動を ブロックして lastRunSessionId と projectId の不整合を予防
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac3946b7bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
3 つの追加改善。
1. スケジュールに 「実行前に main ブランチを最新化する」 opt-in を追加
- todo_schedules.autoSyncBeforeFire 列を追加 (新 migration)
- workspaceId が null (プロジェクト本体実行) のときのみ UI に表示
- 発火時に git fetch → checkout default → pull --ff-only
- 未コミット変更があるときはスキップ + 通知 (stash はしない)
2. Agent Manager 設定に セッション自動削除 (日) を追加
- todo-agent-settings.json に sessionRetentionDays (0-365, 0=無効) を追加
- 起動時に cleanupOldSessions() が終了済み (done/failed/aborted/escalated) かつ
N 日より古い todo_sessions 行 + artifact ディレクトリを削除
- 実行中・キュー中のセッションは絶対に対象外
3. スケジューラ起動失敗時のトースト通知 (提案 B 案)
- main/index.ts の起動 try/catch で失敗したら
schedule-store.emitFire({kind:"failed", scheduleId:"__scheduler_init__"}) を発行
- schedule-store に pendingInitFailure バッファを追加し、
renderer 購読が間に合わなくてもマウント直後に replay
- ScheduleEditorDialog 編集時にプロジェクト select を disabled。 ユーザーがプロジェクト変更を保存しても裏でサイレント無視される P1 issue を塞ぐ。update 送信時に projectId も payload から omit - scheduler.tick() が同じ firedAt を複数 fire に渡していたのを、 各 fire 直前に Date.now() を取り直すよう修正。遅い fire の後に 続く fire で nextRunAt が過去時刻にセットされ次 tick で重複発火 する P2 issue を塞ぐ
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0e76054dd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ベースに cleanupOldSessions が createdAt < cutoff で判定していたが、 ドキュメントでは「完了時刻 (completedAt) を優先、null のみ createdAt に フォールバック」と約束していた。数週間前に作成されたが今日完了した ばかりの長命セッションが起動時に消えてしまう P1 バグを修正。 Drizzle の sql テンプレートで COALESCE(completed_at, created_at) < cutoff とし、完了時刻ベースで判定するようにした。
概要
既存の TODO Agent にスケジュール機能を追加。毎日デプロイ / 毎時 lint / 毎週レポート生成のような定型タスクを UI ビルダーで登録でき、アプリ起動中に指定時刻が来ると TODO セッションが自動で作られて実行されます。
動作イメージ
主要な決定事項 (ユーザー確認済み)
cronstrueでヒューマン表示git fetch → checkout default → pull --ff-only、未コミット変更時はスキップ)アーキテクチャ
todo_schedulesテーブル新規 (migration0056_add_todo_schedules.sql)projectIdNOT NULL (cascade),workspaceIdnullable (set null)autoSyncBeforeFire,overlapMode (skip/queue)等TodoScheduler(setInterval 30s、before-quit で stop) が DB のnextRunAtを監視して発火 → 既存TodoSupervisorに委譲ensureProjectBranchWorkspaceIdでプロジェクトの branch workspace を lazy 作成autoSyncProjectMainで opt-in の git fetch/checkout/pullcleanupOldSessionsが起動時にCOALESCE(completedAt, createdAt) < cutoffで終了済みセッション + artifact を掃除SchedulesSection(TodoManager 内),ScheduleFireToasts(layout に常駐)todoAgent.schedule.*(list/listAll/create/update/setEnabled/delete/previewNextRun/onFire)非目的 (v1)
セルフレビュー + Codex レビュー 対応済み
複数ラウンドで以下を自己 / Codex レビューで検出・対応:
before-quitで scheduler.stop() + tick 内 isStopped ガードで closeLocalDb 後の race 防止listDueにlte(nextRunAt, now)を入れて composite index を実際に利用failedevent を再 emit (triggered のまま残る UX 欺瞞を修正)cronstrue/i18n(~200KB 全ロケール) →cronstrue+jaロケール単体に変更useSubscriptionに onError ハンドラinsertQueuedFromTemplateに集約 (create / fire の重複排除)ensureProjectBranchWorkspaceId時に兄弟 workspace の tabOrder を +1 で詰める (既存 workspaces.create 経路と同じ挙動)"__main__"sentinel をMAIN_REPO_SENTINEL定数化projectIdを schedule update から omit + UI 編集時は Select を disabled (Codex P1)Date.now()を事前計算せず、各 fire 直前に取り直し (Codex P2)cleanupOldSessionsをCOALESCE(completedAt, createdAt)ベースに修正 (Codex P1)実装ファイル
新規:
packages/local-db/src/schema/todo-schedules.ts+ migrationapps/desktop/src/main/todo-agent/{schedule-store,scheduler,schedule-sync,sessions-cleanup}.tsapps/desktop/src/renderer/features/todo-agent/{TodoManager/SchedulesSection,ScheduleFireToasts}/...apps/desktop/plans/20260416-todo-schedule.md既存への小さい追記:
insertQueuedFromTemplateとensureProjectBranchWorkspaceIdを追加新規依存:
cron-parser(main),cronstrue(renderer)Test plan
bun run typecheck/bun run lintがグリーン0 9 * * 1-5) → cronstrue で「平日の 9:00」と表示