fix(merge): conventional merge-commit default and live verify progress
The merge commit message was hand-rolled per caller ("Merge task: <title>",
"Merge <branch>", "Merge subtask") and ignored the task's commit type. Every
caller now passes a blank message and TaskMergeService fills in
CommitMessageBuilder.BuildMerge -> {commitType}(list-slug): merge <title> plus the
ClaudeDo-Task trailer; the merge modal prefills it from GetMergeTargets.
A merge whose list has a verify command holds the MergeTask call for minutes (5m46s
on this repo), during which the modal only disabled its button - no spinner, no
message, so a landed merge looked like a dead app. TaskMergeService now broadcasts
MergeProgress(taskId, phase, elapsedSeconds) for the merging and verifying phases
(re-reported every 30s) plus a WorkerLog line when verify starts; the modal shows a
spinner and the localized phase.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# Review, merge & conflict resolution
|
||||
|
||||
> **Explore-note — verify before trusting.** Distilled map of a subsystem, not authoritative.
|
||||
> Last verified against `6a2a19c` (2026-08-10), which added the preview-time verify build.
|
||||
> Last verified against `fc9df7f` (2026-08-11), which added the conventional merge-commit default
|
||||
> and the `MergeProgress` broadcast.
|
||||
> Planning mode renders per file and `DiffLinesView` is retired.
|
||||
> Drift check: `git log --oneline 20bce9b..HEAD -- src/ClaudeDo.Worker/Lifecycle src/ClaudeDo.Worker/State src/ClaudeDo.Worker/Planning src/ClaudeDo.Ui/ViewModels/Conflicts src/ClaudeDo.Worker/External`
|
||||
> Stable structure only (no line numbers). See docs/explore-notes/README.md.
|
||||
@@ -82,6 +83,17 @@ gating `CanMergeAll`. The worker-side guard remains the actual correctness fix;
|
||||
just avoids inviting a click the worker would reject. `CancelReviewAsync`'s catch now raises
|
||||
`ErrorReported` (→ shell `FlashFooterError`) instead of swallowing the rejection silently.
|
||||
|
||||
## Merge commit message
|
||||
|
||||
`MergeAsync` takes a `commitMessage`, but **every caller passes it blank** and lets
|
||||
`TaskMergeService.DefaultMergeMessage` build `CommitMessageBuilder.BuildMerge(task.CommitType,
|
||||
list.Name, task.Title, task.Id)` → `fix(my-list): merge <title>` + `ClaudeDo-Task:` trailer. Same
|
||||
`type(scope)` shape as the task's own worktree commits, so merges stay Conventional-Commits-clean.
|
||||
The only non-blank caller is the user editing the field in the merge modal, which is prefilled from
|
||||
`GetMergeTargets().DefaultCommitMessage` (the UI can't build it — it knows neither the commit type
|
||||
nor the list name). Callers used to hand-roll `"Merge task: {title}"` / `"Merge {branch}"` /
|
||||
`"Merge subtask"`; don't add a fourth.
|
||||
|
||||
## Post-merge verify gate
|
||||
|
||||
A list can set `ListConfigEntity.VerifyCommand` (List Settings modal → Verification).
|
||||
@@ -110,6 +122,15 @@ but `ApproveAndMergeAsync` still runs the verify command (same per-repo gate, wo
|
||||
`list.WorkingDir`) before the task may reach `Done`. Without that, the run that lands the most on
|
||||
the target branch at once would be the one run nothing checks.
|
||||
|
||||
**The gate is what makes a merge look broken.** A verify command that builds and tests a solution
|
||||
occupies the whole `MergeTask` hub call — measured at ~5m 46s on this repo. `TaskMergeService`
|
||||
therefore broadcasts `MergeProgress(taskId, phase, elapsedSeconds)`: `PhaseMerging` before the
|
||||
per-repo gate wait, `PhaseVerifying` when the verify starts and again on every
|
||||
`ProgressReportInterval` tick (30 s, shared with the MCP progress reports), plus one
|
||||
`WorkerLog` Info line so the footer strip shows it too. The merge modal renders that as a spinner
|
||||
plus phase text; without it the only feedback was the disabled Submit button, which reads as a
|
||||
dead app for minutes while the merge has in fact already landed.
|
||||
|
||||
**Serialization:** a process-wide `ConcurrentDictionary<string, SemaphoreSlim>` keyed by
|
||||
`list.WorkingDir` serializes `MergeAsync` / `ContinueMergeAsync` (git ops + verify) per repo,
|
||||
so a verify run can't be interrupted by a second merge landing in the same working dir
|
||||
|
||||
Reference in New Issue
Block a user