diff --git a/CLAUDE.md b/CLAUDE.md index e417cd8..6fc4c75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ Two-process system communicating over SignalR (`127.0.0.1:47821`): - EF Core migrations manage schema (Migrations/ folder in ClaudeDo.Data) - `IDbContextFactory` used by singleton consumers (e.g. Worker) - Entity configuration via `IEntityTypeConfiguration` in Configuration/ folder -- Task status flow: Idle | Queued -> Running -> Done | Failed | Cancelled +- Task status flow: Idle | Queued -> Running -> WaitingForReview -> Done | Failed | Cancelled. A standalone task's successful run lands in WaitingForReview (planning children go straight to Done); from review you can approve (Done), reject-rerun (Queued, resumes the session with feedback), reject-park (Idle), or cancel (Cancelled). - Worktree state flow: Active -> Merged | Discarded | Kept - The queue picker claims tasks by `Status=Queued` (with `BlockedByTaskId IS NULL`); the legacy tag system was removed - Interfaces live in an `Interfaces/` subfolder beside their consumers (namespace unchanged) diff --git a/src/ClaudeDo.Data/CLAUDE.md b/src/ClaudeDo.Data/CLAUDE.md index b3440eb..a0f3627 100644 --- a/src/ClaudeDo.Data/CLAUDE.md +++ b/src/ClaudeDo.Data/CLAUDE.md @@ -4,7 +4,7 @@ Shared data layer: models, repositories, SQLite infrastructure, and git operatio ## Models -- **TaskEntity** — Id, ListId, Title, Description, Status (`Idle|Queued|Running|Done|Failed|Cancelled`), PlanningPhase (`None|Active|Finalized` — parent-only), BlockedByTaskId (nullable FK to predecessor in a chain), ScheduledFor, Result, LogPath, timestamps, CommitType, Model / SystemPrompt / AgentPath (nullable overrides), IsStarred, IsMyDay, Notes, ParentTaskId, PlanningSessionId, PlanningSessionToken, PlanningFinalizedAt, CreatedBy. Legacy values `Manual`/`Planning`/`Planned`/`Draft`/`Waiting` were retired; existing rows backfill automatically via the `RetireLegacyTaskStatus` migration. +- **TaskEntity** — Id, ListId, Title, Description, Status (`Idle|Queued|Running|WaitingForReview|Done|Failed|Cancelled`), PlanningPhase (`None|Active|Finalized` — parent-only), BlockedByTaskId (nullable FK to predecessor in a chain), ScheduledFor, Result, ReviewFeedback (nullable; reviewer's rejection comment, consumed and cleared by the runner on the next re-run), LogPath, timestamps, CommitType, Model / SystemPrompt / AgentPath (nullable overrides), IsStarred, IsMyDay, Notes, ParentTaskId, PlanningSessionId, PlanningSessionToken, PlanningFinalizedAt, CreatedBy. Legacy values `Manual`/`Planning`/`Planned`/`Draft`/`Waiting` were retired; existing rows backfill automatically via the `RetireLegacyTaskStatus` migration. - **ListEntity** — Id, Name, WorkingDir, DefaultCommitType, CreatedAt - **ListConfigEntity** — ListId (PK, 1:1 with list), Model, SystemPrompt, AgentPath (all nullable) - **WorktreeEntity** — TaskId (PK, 1:1 with task), Path, BranchName, BaseCommit, HeadCommit, DiffStat, State (Active|Merged|Discarded|Kept) diff --git a/src/ClaudeDo.Worker/CLAUDE.md b/src/ClaudeDo.Worker/CLAUDE.md index 26de170..96c7948 100644 --- a/src/ClaudeDo.Worker/CLAUDE.md +++ b/src/ClaudeDo.Worker/CLAUDE.md @@ -27,7 +27,7 @@ Interfaces (e.g. `IQueueWaker`, `IPrimeClock`, `ITaskStateService`) live in an ` - **OverrideSlotService** — owns `RunNow` / `ContinueTask`; goes through `TaskStateService.StartRunningAsync` (caller-driven, serialized by slot lock). - **StaleTaskRecovery** — startup-only service; calls `TaskStateService.RecoverStaleRunningAsync` to flip orphaned `Running` rows to `Failed`. - **External/*** — always-on MCP tools for general Claude sessions, scoped to *starting* and *observing* sessions (no worktree/merge, multi-turn, planning, or app-settings writes). Auth via optional `X-ClaudeDo-Key` header. Registered explicitly in `Program.cs`'s external app via `.WithTools()`. Organized by concern: - - `ExternalMcpService` — task CRUD + execution: `ListTaskLists`, `ListTasks`, `GetTask`, `AddTask`, `UpdateTask`, `UpdateTaskStatus` (`Idle` / `Queued`), `RunTaskNow`, `CancelTask`, `DeleteTask` + - `ExternalMcpService` — task CRUD + execution: `ListTaskLists`, `ListTasks`, `GetTask`, `AddTask`, `UpdateTask`, `UpdateTaskStatus` (`Idle` / `Queued`), `ReviewTask` (`approve` / `reject_rerun` / `reject_park` / `cancel` for a WaitingForReview task), `RunTaskNow`, `CancelTask`, `DeleteTask` - `ListMcpTools` — `CreateList`, `UpdateList`, `DeleteList` - `ConfigMcpTools` — `GetListConfig`, `SetListConfig`, `SetTaskConfig` - `RunHistoryMcpTools` — `ListRuns`, `GetRun`, `GetTaskLog` (latest run's log, tail-capped at 256 KB) @@ -41,21 +41,25 @@ Interfaces (e.g. `IQueueWaker`, `IPrimeClock`, `ITaskStateService`) live in an ` | Field | Values | Meaning | |---|---|---| -| `Status` | `Idle`, `Queued`, `Running`, `Done`, `Failed`, `Cancelled` | Lifecycle only. | +| `Status` | `Idle`, `Queued`, `Running`, `WaitingForReview`, `Done`, `Failed`, `Cancelled` | Lifecycle only. | | `PlanningPhase` | `None`, `Active`, `Finalized` | Parent-only marker. `Active` ≈ legacy `Planning`; `Finalized` ≈ legacy `Planned`. | | `BlockedByTaskId` | nullable FK | Replaces legacy `Waiting`. A queued row with `BlockedByTaskId != NULL` is skipped by the picker. | +| `ReviewFeedback` | nullable string | Reviewer's rejection comment. Set by `RejectToQueueAsync`; consumed and cleared by `QueueService` on the next re-run (resumes the Claude session with it as the next-turn prompt). | Allowed transitions (enforced by `TaskStateService`): ``` -Idle → Queued | Running (RunNow) -Queued → Running | Cancelled | Idle -Running → Done | Failed | Cancelled -Done → Idle (re-run) -Failed → Idle | Queued -Cancelled → Idle | Queued +Idle → Queued | Running (RunNow) +Queued → Running | Cancelled | Idle +Running → WaitingForReview (standalone success) | Done (planning child success) | Failed | Cancelled +WaitingForReview → Done (approve) | Queued (reject-rerun, +feedback) | Idle (reject-park) | Cancelled +Done → Idle (re-run) +Failed → Idle | Queued +Cancelled → Idle | Queued ``` +Only standalone tasks (`ParentTaskId == null`) route to `WaitingForReview` on success. Planning children go straight to `Done` so the sequential chain (which advances on terminal states) is unaffected. `TaskRunner.HandleSuccess` makes this choice; review transitions live in `TaskStateService` (`SubmitForReviewAsync`, `ApproveReviewAsync`, `RejectToQueueAsync`, `RejectToIdleAsync`, `ClearReviewFeedbackAsync`). + ## Planning Flow `PlanningSessionManager.FinalizeAsync` is the single path: @@ -100,7 +104,7 @@ Each CLI invocation is recorded in the `task_runs` table via `TaskRunRepository` ## SignalR Hub -**WorkerHub** methods: `Ping`, `GetActive`, `RunNow`, `CancelTask`, `WakeQueue`, `ContinueTask`, `ResetTask`, `GetAgents`, `RefreshAgents`, `GetAppSettings`, `UpdateAppSettings`, `CleanupFinishedWorktrees`, `ResetAllWorktrees`, `MergeTask`, `GetMergeTargets`, `UpdateList`, `UpdateListConfig`, `GetListConfig`, `UpdateTaskAgentSettings` +**WorkerHub** methods: `Ping`, `GetActive`, `RunNow`, `CancelTask`, `WakeQueue`, `ContinueTask`, `ResetTask`, `ApproveReview`, `RejectReviewToQueue`, `RejectReviewToIdle`, `CancelReview`, `GetAgents`, `RefreshAgents`, `GetAppSettings`, `UpdateAppSettings`, `CleanupFinishedWorktrees`, `ResetAllWorktrees`, `MergeTask`, `GetMergeTargets`, `UpdateList`, `UpdateListConfig`, `GetListConfig`, `UpdateTaskAgentSettings` **HubBroadcaster** events: `TaskStarted`, `TaskFinished`, `TaskMessage`, `WorktreeUpdated`, `TaskUpdated`, `RunCreated`, `ListUpdated`