docs: document the unified parent-task model
Add WaitingForChildren to the status tables, document the single parent lifecycle (planning + improvement) and approve-merges-the-whole-unit across the root, Worker, and Data CLAUDE.md files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
- EF Core migrations manage schema (Migrations/ folder in ClaudeDo.Data)
|
||||||
- `IDbContextFactory<ClaudeDoDbContext>` used by singleton consumers (e.g. Worker)
|
- `IDbContextFactory<ClaudeDoDbContext>` used by singleton consumers (e.g. Worker)
|
||||||
- Entity configuration via `IEntityTypeConfiguration<T>` in Configuration/ folder
|
- Entity configuration via `IEntityTypeConfiguration<T>` in Configuration/ folder
|
||||||
- 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 (merges the worktree into the target branch, then Done; conflicts keep it in WaitingForReview), reject-rerun (Queued, resumes the session with feedback), reject-park (Idle), or cancel (Cancelled). Tasks with no active worktree (sandbox run / improvement parent) are approved straight to Done.
|
- Task status flow: Idle | Queued -> Running -> WaitingForReview -> Done | Failed | Cancelled. A task that spawns/has children passes through WaitingForChildren first, then surfaces for review once every child is terminal — this is the single parent model for both planning and improvement parents (planning/improvement *children* themselves go straight to Done, only the parent is reviewed). From review you can approve, reject-rerun (Queued, resumes the session with feedback), reject-park (Idle), or cancel. Approve is the single review+merge action: a childless task merges its own worktree then Done (conflicts keep it in WaitingForReview); a task with children drives the unit merge (parent worktree if any + each Done child in order, with conflict continue/abort). Tasks with no active worktree (sandbox run) approve straight to Done.
|
||||||
- Worktree state flow: Active -> Merged | Discarded | Kept
|
- 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
|
- 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)
|
- Interfaces live in an `Interfaces/` subfolder beside their consumers (namespace unchanged)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Shared data layer: models, repositories, SQLite infrastructure, and git operatio
|
|||||||
|
|
||||||
## Models
|
## Models
|
||||||
|
|
||||||
- **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 / MaxTurns (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|WaitingForChildren|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 / MaxTurns (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
|
- **ListEntity** — Id, Name, WorkingDir, DefaultCommitType, CreatedAt
|
||||||
- **ListConfigEntity** — ListId (PK, 1:1 with list), Model, SystemPrompt, AgentPath, MaxTurns (all nullable)
|
- **ListConfigEntity** — ListId (PK, 1:1 with list), Model, SystemPrompt, AgentPath, MaxTurns (all nullable)
|
||||||
- **WorktreeEntity** — TaskId (PK, 1:1 with task), Path, BranchName, BaseCommit, HeadCommit, DiffStat, State (Active|Merged|Discarded|Kept)
|
- **WorktreeEntity** — TaskId (PK, 1:1 with task), Path, BranchName, BaseCommit, HeadCommit, DiffStat, State (Active|Merged|Discarded|Kept)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ Interfaces (e.g. `IQueueWaker`, `IPrimeClock`, `ITaskStateService`) live in an `
|
|||||||
|
|
||||||
| Field | Values | Meaning |
|
| Field | Values | Meaning |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Status` | `Idle`, `Queued`, `Running`, `WaitingForReview`, `Done`, `Failed`, `Cancelled` | Lifecycle only. |
|
| `Status` | `Idle`, `Queued`, `Running`, `WaitingForChildren`, `WaitingForReview`, `Done`, `Failed`, `Cancelled` | Lifecycle only. `WaitingForChildren` = parent's own work is done, waiting on its children. |
|
||||||
| `PlanningPhase` | `None`, `Active`, `Finalized` | Parent-only marker. `Active` ≈ legacy `Planning`; `Finalized` ≈ legacy `Planned`. |
|
| `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. |
|
| `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). |
|
| `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). |
|
||||||
@@ -66,22 +66,42 @@ Interfaces (e.g. `IQueueWaker`, `IPrimeClock`, `ITaskStateService`) live in an `
|
|||||||
Allowed transitions (enforced by `TaskStateService`):
|
Allowed transitions (enforced by `TaskStateService`):
|
||||||
|
|
||||||
```
|
```
|
||||||
Idle → Queued | Running (RunNow)
|
Idle → Queued | Running (RunNow)
|
||||||
Queued → Running | Cancelled | Idle
|
Queued → Running | Cancelled | Idle
|
||||||
Running → WaitingForReview (standalone success) | Done (planning child success) | Failed | Cancelled
|
Running → WaitingForReview (standalone success, no children)
|
||||||
WaitingForReview → Done (approve: merges worktree first; conflicts keep it in WaitingForReview) | Queued (reject-rerun, +feedback) | Idle (reject-park) | Cancelled
|
| WaitingForChildren (parent with pending children)
|
||||||
Done → Idle (re-run)
|
| Done (planning/improvement child success) | Failed | Cancelled
|
||||||
Failed → Idle | Queued
|
WaitingForChildren → WaitingForReview (all children terminal) | Cancelled
|
||||||
Cancelled → Idle | Queued
|
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`).
|
**Unified parent model.** Every parent — planning *or* improvement — flows
|
||||||
|
`… → WaitingForChildren → WaitingForReview → Done`, advanced by the single
|
||||||
|
`TaskStateService.TryAdvanceParentAsync` (surfaces any `WaitingForChildren` parent for
|
||||||
|
review once all children are terminal; failed/cancelled children are annotated on the
|
||||||
|
result, not wedged). A planning parent enters `WaitingForChildren` at
|
||||||
|
`FinalizePlanningAsync` (or `WaitingForReview` directly if it has no children); an
|
||||||
|
improvement parent enters it from `TaskRunner.HandleSuccess` when its run spawned
|
||||||
|
children. Planning/improvement **children** still go straight to `Done` (no individual
|
||||||
|
review) — only the parent is reviewed.
|
||||||
|
|
||||||
|
**Approve = merge the whole unit.** `ApproveReview`/`review_task` approve, for a task
|
||||||
|
that has children, drives `PlanningMergeOrchestrator` (merges the parent worktree if
|
||||||
|
Active + each `Done` child in order, sets the parent `Done`, and on a mid-merge
|
||||||
|
conflict pauses for `ContinuePlanningMerge`/`AbortPlanningMerge`). Childless tasks use
|
||||||
|
`TaskMergeService.ApproveAndMergeAsync`. There is no separate "Merge all" entry —
|
||||||
|
approve is the single review+merge action. Review transitions live in `TaskStateService`
|
||||||
|
(`SubmitForReviewAsync`, `SubmitForChildrenAsync`, `ApproveReviewAsync`,
|
||||||
|
`RejectToQueueAsync`, `RejectToIdleAsync`, `ClearReviewFeedbackAsync`).
|
||||||
|
|
||||||
## Planning Flow
|
## Planning Flow
|
||||||
|
|
||||||
`PlanningSessionManager.FinalizeAsync` is the single path:
|
`PlanningSessionManager.FinalizeAsync` is the single path:
|
||||||
|
|
||||||
1. `_state.FinalizePlanningAsync(parent)` flips parent `PlanningPhase` to `Finalized`.
|
1. `_state.FinalizePlanningAsync(parent)` flips parent `PlanningPhase` to `Finalized` and sets `Status` to `WaitingForChildren` (or `WaitingForReview` if the parent has no children).
|
||||||
2. `PlanningChainCoordinator.SetupChainAsync` attaches the `agent` tag to every child, enqueues child[0], and `BlockOn`s child[i] → child[i-1].
|
2. `PlanningChainCoordinator.SetupChainAsync` attaches the `agent` tag to every child, enqueues child[0], and `BlockOn`s child[i] → child[i-1].
|
||||||
3. The first child is woken automatically; successors unblock as their predecessor reaches a terminal state via `OnChildFinishedAsync`.
|
3. The first child is woken automatically; successors unblock as their predecessor reaches a terminal state via `OnChildFinishedAsync`.
|
||||||
|
|
||||||
@@ -121,7 +141,7 @@ Each CLI invocation is recorded in the `task_runs` table via `TaskRunRepository`
|
|||||||
|
|
||||||
## SignalR Hub
|
## SignalR Hub
|
||||||
|
|
||||||
**WorkerHub** methods: `Ping`, `GetActive`, `RunNow`, `CancelTask`, `WakeQueue`, `ContinueTask`, `ResetTask`, `ApproveReview(taskId, targetBranch) -> MergeResultDto` (merges worktree then transitions to Done; on conflict stays WaitingForReview), `PreviewMerge(taskId, targetBranch) -> MergePreviewDto` (non-destructive mergeability check), `RejectReviewToQueue`, `RejectReviewToIdle`, `CancelReview`, `GetAgents`, `RefreshAgents`, `GetAppSettings`, `UpdateAppSettings`, `CleanupFinishedWorktrees`, `ResetAllWorktrees`, `MergeTask`, `GetMergeTargets`, `UpdateList`, `UpdateListConfig`, `GetListConfig`, `UpdateTaskAgentSettings`, `GetWeekReport`, `GenerateWeekReport`, `GetDailyNotes`, `AddDailyNote`, `UpdateDailyNote`, `DeleteDailyNote`, `RunDailyPrepNow`, `ClearMyDay`, `GetLastPrepLog`
|
**WorkerHub** methods: `Ping`, `GetActive`, `RunNow`, `CancelTask`, `WakeQueue`, `ContinueTask`, `ResetTask`, `ApproveReview(taskId, targetBranch) -> MergeResultDto` (childless task: merges its worktree then Done, conflict stays WaitingForReview; task with children: drives `PlanningMergeOrchestrator` to merge the whole unit), `ContinuePlanningMerge` / `AbortPlanningMerge` (resolve a unit-merge conflict), `PreviewMerge(taskId, targetBranch) -> MergePreviewDto` (non-destructive mergeability check), `RejectReviewToQueue`, `RejectReviewToIdle`, `CancelReview`, `GetAgents`, `RefreshAgents`, `GetAppSettings`, `UpdateAppSettings`, `CleanupFinishedWorktrees`, `ResetAllWorktrees`, `MergeTask`, `GetMergeTargets`, `UpdateList`, `UpdateListConfig`, `GetListConfig`, `UpdateTaskAgentSettings`, `GetWeekReport`, `GenerateWeekReport`, `GetDailyNotes`, `AddDailyNote`, `UpdateDailyNote`, `DeleteDailyNote`, `RunDailyPrepNow`, `ClearMyDay`, `GetLastPrepLog`
|
||||||
|
|
||||||
**HubBroadcaster** events: `TaskStarted`, `TaskFinished`, `TaskMessage`, `WorktreeUpdated`, `TaskUpdated`, `RunCreated`, `ListUpdated`, `PrimeFired`, `PrepStarted`, `PrepLine`, `PrepFinished`
|
**HubBroadcaster** events: `TaskStarted`, `TaskFinished`, `TaskMessage`, `WorktreeUpdated`, `TaskUpdated`, `RunCreated`, `ListUpdated`, `PrimeFired`, `PrepStarted`, `PrepLine`, `PrepFinished`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user