From c7d0a1722ae024e5ad54f155673eee04337c74d1 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Wed, 21 Oct 2026 11:59:52 -0700 Subject: [PATCH] Make merge dialog Enter follow the task's offered merge action --- api/openapi.yaml | 5 + internal/api/api.gen.go | 392 ++++++++++-------- .../092-store-ready-merge-choice.sql | 6 + internal/store/store.go | 13 +- mac/Sources/Byre/Chat.swift | 13 +- mac/Sources/Byre/MergeDialog.swift | 3 +- mac/Sources/Byre/TranscriptBuilder.swift | 6 +- 7 files changed, 240 insertions(+), 198 deletions(-) create mode 100644 internal/store/migrations/092-store-ready-merge-choice.sql diff --git a/api/openapi.yaml b/api/openapi.yaml index 666ee1e..e5d809b 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1481,6 +1481,11 @@ components: parent_task_id: { type: string, description: "The task whose agent created this one as its subtask, or for a stage task its workflow run's task; absent for other tasks." } merging_into_parent: { type: boolean, description: "Whether a merge of this subtask into its parent was requested and the parent hasn't merged it yet." } merge_after_review: { type: boolean, description: "Whether a Merge with after_review is waiting for the running review of the latest snapshot to finish." } + ready_merge: + type: string + description: "The merge the agent's standing ready_for_merge offers as the default: complete lands the work and closes the task, continue lands it and keeps the task open. Absent while no ready_for_merge stands. It changes only with a task.ready, task.discard_suggested, task.ready_cleared, snapshot.created or inbox.created event." + enum: [complete, continue] + x-enum-varnames: [ReadyMergeComplete, ReadyMergeContinue] summary_seq: { type: integer, format: int64, description: "The seq of the task's latest turn.ended event with summary: true; 0 when it has none." } merge_facts: { $ref: "#/components/schemas/MergeFacts" } diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 4d5591d..1a2b614 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -411,6 +411,24 @@ func (e StageAttemptState) Valid() bool { } } +// Defines values for TaskReadyMerge. +const ( + ReadyMergeComplete TaskReadyMerge = "complete" + ReadyMergeContinue TaskReadyMerge = "continue" +) + +// Valid indicates whether the value is a known member of the TaskReadyMerge enum. +func (e TaskReadyMerge) Valid() bool { + switch e { + case ReadyMergeComplete: + return true + case ReadyMergeContinue: + return true + default: + return false + } +} + // Defines values for TaskState. const ( TaskStateAsking TaskState = "asking" @@ -1424,6 +1442,9 @@ type Task struct { PortBase int `json:"port_base"` ProjectId string `json:"project_id"` + // ReadyMerge The merge the agent's standing ready_for_merge offers as the default: complete lands the work and closes the task, continue lands it and keeps the task open. Absent while no ready_for_merge stands. It changes only with a task.ready, task.discard_suggested, task.ready_cleared, snapshot.created or inbox.created event. + ReadyMerge *TaskReadyMerge `json:"ready_merge,omitempty"` + // State The one state the task is in, kept by the store in the same transaction as every change to it (a task.state event carries each change). The first that holds: merged or discarded once closed; for a workflow run's task, finished once the run finished, running while one of its stage tasks is running, else idle; discarding; asking while it has an open question; merging while a Merge or a merge into its parent is under way; merge_held while its Merge waits for another merge in the project; merge_after_review while a Merge with after_review waits for its review; running while one of its agents is mid-turn or has subagents running; reviewing while a review runs; review_pending between a turn's end and its snapshot's review starting or being skipped; ready_to_merge or discard_suggested when the agent's latest ready_for_merge or suggest_discard still stands; finished when it runs a workflow stage whose finish_stage has taken; idle; no_agent when it has no live agent. State TaskState `json:"state"` Status TaskStatus `json:"status"` @@ -1445,6 +1466,9 @@ type Task struct { WorktreePath string `json:"worktree_path"` } +// TaskReadyMerge The merge the agent's standing ready_for_merge offers as the default: complete lands the work and closes the task, continue lands it and keeps the task open. Absent while no ready_for_merge stands. It changes only with a task.ready, task.discard_suggested, task.ready_cleared, snapshot.created or inbox.created event. +type TaskReadyMerge string + // TaskState The one state the task is in, kept by the store in the same transaction as every change to it (a task.state event carries each change). The first that holds: merged or discarded once closed; for a workflow run's task, finished once the run finished, running while one of its stage tasks is running, else idle; discarding; asking while it has an open question; merging while a Merge or a merge into its parent is under way; merge_held while its Merge waits for another merge in the project; merge_after_review while a Merge with after_review waits for its review; running while one of its agents is mid-turn or has subagents running; reviewing while a review runs; review_pending between a turn's end and its snapshot's review starting or being skipped; ready_to_merge or discard_suggested when the agent's latest ready_for_merge or suggest_discard still stands; finished when it runs a workflow stage whose finish_stage has taken; idle; no_agent when it has no live agent. type TaskState string @@ -4627,189 +4651,191 @@ var swaggerSpec = []string{ "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", - "DHHEmnw1Cu0PH/7XAA==", + "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", + "FNp+voIgOyRuwpwUMAxxxJp8NQrtDx/+1wA=", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/store/migrations/092-store-ready-merge-choice.sql b/internal/store/migrations/092-store-ready-merge-choice.sql new file mode 100644 index 0000000..5c8b07a --- /dev/null +++ b/internal/store/migrations/092-store-ready-merge-choice.sql @@ -0,0 +1,6 @@ +UPDATE tasks SET ready = coalesce(( + SELECT json_extract(e.payload, '$.merge') FROM events e + WHERE e.task_id = tasks.id AND e.kind = 'task.ready' + ORDER BY e.seq DESC LIMIT 1 +), 'complete') +WHERE ready = 'merge'; diff --git a/internal/store/store.go b/internal/store/store.go index 6e6e7d4..17e8291 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -769,7 +769,7 @@ func (s *Store) SetReadyPromptSeq(ctx context.Context, id string, seq int64) err } func (s *Store) MarkReady(ctx context.Context, id, overview, merge, actor string) error { - return s.setReady(ctx, id, actor, "merge", "task.ready", map[string]any{"id": id, "overview": overview, "merge": merge}) + return s.setReady(ctx, id, actor, merge, "task.ready", map[string]any{"id": id, "overview": overview, "merge": merge}) } func (s *Store) SuggestDiscard(ctx context.Context, id, reason, actor string) error { @@ -1631,7 +1631,7 @@ const taskStateExpr = `CASE WHEN s.subagents > 0 THEN 'running' WHEN EXISTS (SELECT 1 FROM reviews r WHERE r.task_id = tasks.id AND r.state = 'running') THEN 'reviewing' WHEN tasks.review_requests > 0 THEN 'review_pending' - WHEN tasks.ready = 'merge' THEN 'ready_to_merge' + WHEN tasks.ready IN ('complete', 'continue') THEN 'ready_to_merge' WHEN tasks.ready = 'discard' THEN 'discard_suggested' WHEN tasks.stage_output != '' THEN 'finished' ELSE 'idle' @@ -1681,7 +1681,7 @@ func (s *Store) ClearReviewRequests(ctx context.Context) error { const projectColumns = `id, name, repo_path, default_branch, remote, created_by, created_at, review_workflow, audit_workflow` -const taskColumns = `id, project_id, title, merge_title, branch, worktree_path, base_ref, port_base, color, status, created_by, created_at, closed_at, merge_commit, todo_item, forked_from, forked_at, merge_queued_at, workflow_name, workflow_stage, parent_task_id, parent_merge_by, merge_after_review_by != '', state, merging_by != '' OR finalizing != '', discard_state != '' AND status = 'active', (SELECT coalesce(max(seq), 0) FROM events WHERE events.task_id = tasks.id AND kind = 'turn.ended' AND json_extract(payload, '$.summary') = 1), audit, ` + backgroundExpr + `, ` + mergeFactsColumns +const taskColumns = `id, project_id, title, merge_title, branch, worktree_path, base_ref, port_base, color, status, created_by, created_at, closed_at, merge_commit, todo_item, forked_from, forked_at, merge_queued_at, workflow_name, workflow_stage, parent_task_id, parent_merge_by, merge_after_review_by != '', state, merging_by != '' OR finalizing != '', discard_state != '' AND status = 'active', (SELECT coalesce(max(seq), 0) FROM events WHERE events.task_id = tasks.id AND kind = 'turn.ended' AND json_extract(payload, '$.summary') = 1), audit, ready, ` + backgroundExpr + `, ` + mergeFactsColumns // Keep the child lookup separate from the task itself so SQLite can use tasks_parent. const backgroundExpr = `(audit != '' OR EXISTS (SELECT 1 FROM tasks r WHERE r.id = tasks.parent_task_id AND tasks.workflow_stage != '' AND r.audit != '')) @@ -1721,9 +1721,10 @@ func scanTask(row scanner) (api.Task, error) { parentMergeBy := "" taskState := "" audit := "" + ready := "" participants := "" f := &t.MergeFacts - err := row.Scan(&t.Id, &t.ProjectId, &t.Title, &t.MergeTitle, &t.Branch, &t.WorktreePath, &t.BaseRef, &t.PortBase, &t.Color, &status, &t.CreatedBy, &createdAt, &closedAt, &mergeCommit, &todoItem, &forkedFrom, &forkedAt, &mergeQueuedAt, &workflowName, &workflowStage, &parentTaskID, &parentMergeBy, &t.MergeAfterReview, &taskState, &t.Merging, &t.Discarding, &t.SummarySeq, &audit, &t.Background, + err := row.Scan(&t.Id, &t.ProjectId, &t.Title, &t.MergeTitle, &t.Branch, &t.WorktreePath, &t.BaseRef, &t.PortBase, &t.Color, &status, &t.CreatedBy, &createdAt, &closedAt, &mergeCommit, &todoItem, &forkedFrom, &forkedAt, &mergeQueuedAt, &workflowName, &workflowStage, &parentTaskID, &parentMergeBy, &t.MergeAfterReview, &taskState, &t.Merging, &t.Discarding, &t.SummarySeq, &audit, &ready, &t.Background, &f.UnmergedCommits, &f.UnmergedFiles, &f.UnreviewedChanges, &f.ReviewSnapshot, &f.ReviewCount, &f.ReviewRunning, &f.ReviewFailed, &f.ReviewFindings, &participants) if err != nil { return t, err @@ -1733,6 +1734,10 @@ func scanTask(row scanner) (api.Task, error) { return t, err } t.State = api.TaskState(taskState) + if ready == string(api.ReadyMergeComplete) || ready == string(api.ReadyMergeContinue) { + m := api.TaskReadyMerge(ready) + t.ReadyMerge = &m + } if audit != "" { t.Audit = &audit } diff --git a/mac/Sources/Byre/Chat.swift b/mac/Sources/Byre/Chat.swift index 93929ec..d74d031 100644 --- a/mac/Sources/Byre/Chat.swift +++ b/mac/Sources/Byre/Chat.swift @@ -295,7 +295,7 @@ final class ChatModel { if let r = row.review, r.snapshotN == latest, findingOpen(r, i) { open.append(row.id) } - case .ready(_, _, let d, _) where row.id == builder.lastReady && active: + case .ready(_, _, let d) where row.id == builder.lastReady && active: ready.append(row.id) discard = d default: @@ -644,7 +644,7 @@ private struct MergeButton: View { : "The agent merges \(target) in, then byre squashes into one commit on \(target) and pushes it to origin; held while another task in the project is merging (⌘S)" Button(intoParent ? "Merging into parent" : held ? "Merge held" : task.mergeAfterReview ? "Merge after review" : keepOpen == false ? "Merge & complete" : "Merge") { Task { - await confirmMerge(task.id, store, overlays) + await confirmMerge(task.id, store, overlays, keepOpen: keepOpen) } } .buttonStyle(WebButtonStyle(primary: primary)) @@ -1264,8 +1264,8 @@ struct RowView: View { case .findingResponse(let snapshot, let index, let response, let reason): FindingResponseView(row: row, model: model, snapshot: snapshot, index: index, response: response, reason: reason) - case .ready(let when, let blocks, let discard, let keepOpen): - ReadyBubble(when: when, blocks: blocks, discard: discard, keepOpen: keepOpen, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged) + case .ready(let when, let blocks, let discard): + ReadyBubble(when: when, blocks: blocks, discard: discard, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged) case .handoff: DetailsBox(boxed: false) { @@ -1385,7 +1385,6 @@ private struct ReadyBubble: View { let when: Date let blocks: [MDBlock] let discard: Bool - let keepOpen: Bool let taskId: String? let latest: Bool let unmerged: Bool @@ -1412,8 +1411,8 @@ private struct ReadyBubble: View { DiscardButton(task: task, busy: busy) } trailing: { if !discard { - MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: true, primary: keepOpen) - MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: false, primary: !keepOpen) + MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: true, primary: task.readyMerge == ._continue) + MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: false, primary: task.readyMerge != ._continue) } } .padding(.top, 4) diff --git a/mac/Sources/Byre/MergeDialog.swift b/mac/Sources/Byre/MergeDialog.swift index be2c412..58c7b14 100644 --- a/mac/Sources/Byre/MergeDialog.swift +++ b/mac/Sources/Byre/MergeDialog.swift @@ -50,10 +50,11 @@ final class MergeModel { } @MainActor -func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays, keepOpen: Bool = false) async { +func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays, keepOpen: Bool? = nil) async { guard let task = store.findTask(taskId), task.status == .active else { return } + let keepOpen = keepOpen ?? (task.readyMerge == ._continue) if store.taskBusy(taskId) { overlays.showAlert("Agent is mid-turn; merge when it's idle") return diff --git a/mac/Sources/Byre/TranscriptBuilder.swift b/mac/Sources/Byre/TranscriptBuilder.swift index 3e90fee..f76a95a 100644 --- a/mac/Sources/Byre/TranscriptBuilder.swift +++ b/mac/Sources/Byre/TranscriptBuilder.swift @@ -54,7 +54,7 @@ enum RowKind { case review(note: Note, md: [MDBlock]) case finding(index: Int) case findingResponse(snapshot: Int, index: Int, response: String, reason: String) - case ready(when: Date, md: [MDBlock], discard: Bool, keepOpen: Bool) + case ready(when: Date, md: [MDBlock], discard: Bool) case handoff case snapshot(Snapshot) case question @@ -972,10 +972,10 @@ final class TranscriptBuilder { return note([.text("\(ev.actor) set the task summary")]) case "task.ready": - return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"])), discard: false, keepOpen: str(p, "merge") == "continue"), taskId: rowTask) + return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"])), discard: false), taskId: rowTask) case "task.discard_suggested": - return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true, keepOpen: false), taskId: rowTask) + return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true), taskId: rowTask) case "task.discard_started": return note([.text("\(ev.actor) asked to discard the task; the agent writes the task summary first")])