feat(merge): verify once per batch via skipVerify + verify_merges
Per-merge verify made an N-task batch pay N x ~7 min, each run testing the same moving main. review_task/merge_task/continue_merge now take skipVerify: the merge lands but the gate AND the Done transition are deferred (status merged_verify_pending, task stays WaitingForReview). The new verify_merges tool runs the list verify command once for the explicitly listed tasks and promotes them to Done on success - explicit ids so the handler own submitted task and parked verify_failed tasks are never swept up; an Active worktree is refused per entry. PlanningMergeOrchestrator threads the flag through the unit merge (no more per-child verify) and skips FinalizeParentDoneAsync when verifies were deferred. The merge-helper Merge prompt approves with skipVerify=true and calls verify_merges once after the last merge. UI approve is unchanged; the no-Done-without-green-verify invariant stays server-enforced.
This commit is contained in:
@@ -79,7 +79,8 @@ Task: `ListTaskLists`, `ListTasks`, `GetTask`, `AddTask`, `AddSubtask`, `UpdateT
|
||||
description is now the canonical place for what each status means.)
|
||||
|
||||
Worktree/git: `GetTaskWorktree`, `GetTaskDiff`, `MergeTask`, `ContinueMerge`, `AbortMerge`,
|
||||
`PreviewMerge`, `PreviewMergeSet`, `RevertMerge`, `ListWorktrees`, `CleanupTaskWorktree`.
|
||||
`VerifyMerges`, `PreviewMerge`, `PreviewMergeSet`, `RevertMerge`, `ListWorktrees`,
|
||||
`CleanupTaskWorktree`.
|
||||
|
||||
Daily prep: `GetDailyPrepCandidates`, `SetMyDay`.
|
||||
|
||||
@@ -131,6 +132,18 @@ are reported in `ReviewTaskResult`.
|
||||
`Done` and merged silently with `changedFileCount: 0`, indistinguishable from a small-but-real
|
||||
change. `TaskRefDto.roadblockCount` (on every task-returning tool, stamped by `TaskRunner` from
|
||||
`result.Blocks.Count`) is the MCP-visible signal for *why* a child is empty.
|
||||
- `skipVerify=true` (also on `MergeTask`/`ContinueMerge`) is batch mode: the merge lands but the
|
||||
list's verify command is deferred and the task stays `WaitingForReview` with mergeStatus
|
||||
`merged_verify_pending`. One `VerifyMerges` call after the batch runs the verify once and
|
||||
promotes them — see [review-merge.md](review-merge.md) → batch verify.
|
||||
|
||||
**`VerifyMerges`** — `verify_merges(taskIds)`: runs the list's verify command ONCE (same per-repo
|
||||
merge gate) and promotes each listed task to Done via `ApproveReviewAsync` on success. Explicit
|
||||
ids on purpose — auto-promoting every `WaitingForReview` task would sweep up the handler's own
|
||||
task and deliberately parked `verify_failed` tasks. Eligibility per task: `WaitingForReview` AND
|
||||
(worktree `Merged` OR no worktree); an `Active` worktree means the branch never landed and is
|
||||
refused per-entry (the rest still promote). All ids must share one list. `verify_failed` promotes
|
||||
nothing; the merges stay in place.
|
||||
|
||||
**`PreviewMerge`** — non-destructive `git merge-tree --write-tree` mergeability check for one
|
||||
task's worktree branch against `targetBranch` (default: the repo's current branch). Returns
|
||||
|
||||
@@ -153,6 +153,27 @@ dead app for minutes while the merge has in fact already landed.
|
||||
so a verify run can't be interrupted by a second merge landing in the same working dir
|
||||
mid-build.
|
||||
|
||||
### Batch verify (skipVerify + verify_merges)
|
||||
|
||||
Per-merge verify made an N-task batch pay N × ~7 min, each run testing the same moving main.
|
||||
`MergeAsync`/`ContinueMergeAsync`/`ApproveAndMergeAsync` take `skipVerify` (MCP-only —
|
||||
`review_task`/`merge_task`/`continue_merge` expose it; the UI approve never passes it): the merge
|
||||
lands normally but the gate AND the Done transition are deferred — status
|
||||
`merged_verify_pending`, task stays `WaitingForReview` with a `Merged` worktree (the same shape
|
||||
`verify_failed` leaves behind). `TaskMergeService.RunListVerifyAsync` (tool `verify_merges`) then
|
||||
runs the command once under the same per-repo gate and promotes the explicitly listed tasks via
|
||||
`ApproveReviewAsync`. Explicit ids, not a query — auto-promotion would sweep up the handler's own
|
||||
submitted task and parked `verify_failed` tasks. A task whose worktree is still `Active` is
|
||||
refused per-entry (branch never landed → would go Done with an unmerged branch).
|
||||
|
||||
`PlanningMergeOrchestrator` threads `skipVerify` through the unit merge (`State.SkipVerify`), so
|
||||
children no longer pay the gate per child; when any child came back `merged_verify_pending` the
|
||||
drain skips `FinalizeParentDoneAsync` (a finalize would grant a Done nothing verified), still
|
||||
broadcasts `PlanningCompleted` (the drain IS complete; the UI banner must clear), and returns
|
||||
`merged_verify_pending` — the parent is promoted later by `verify_merges` like any other task.
|
||||
The merge-helper Merge prompt approves with `skipVerify=true` and calls `verify_merges` once
|
||||
after the last merge; the invariant "no Done without a green verify" stays server-enforced.
|
||||
|
||||
### Verify in the preview, not just post-merge
|
||||
|
||||
`TaskMergeService.PreviewAsync(taskId, targetBranch, runVerify, ct)` can additionally build/test a
|
||||
|
||||
Reference in New Issue
Block a user