Refuse turns, discard and subtasks while a merge is finalizing

This commit is contained in:
Ian Gulliver
2026-10-16 12:46:28 -07:00
parent 0e52253d07
commit ecfae97d97
11 changed files with 287 additions and 233 deletions
+1 -5
View File
@@ -531,11 +531,7 @@ func (m *Manager) paneFinished(ctx context.Context, f api.Focus) (string, error)
if task.Status != api.TaskStatusActive {
return "is " + string(task.Status), nil
}
mergingBy, err := m.store.MergingBy(ctx, task.Id)
if err != nil {
return "", err
}
if mergingBy != "" {
if task.Merging {
return "is merging", nil
}
if f.Tab != api.TabInbox {
+13 -7
View File
@@ -283,6 +283,11 @@ func (m *Manager) Recover(ctx context.Context) error {
return err
}
err = m.recoverFinalizing(ctx)
if err != nil {
return err
}
staged, err := m.recoverStages(ctx)
if err != nil {
return err
@@ -365,7 +370,7 @@ func (m *Manager) failResume(ctx context.Context, sess api.AgentSession, task ap
if derr != nil {
return derr
}
next, serr := m.store.EndTurn(ctx, sess.Id, "byre", map[string]any{"error": err.Error()}, nil, false)
next, _, serr := m.store.EndTurn(ctx, sess.Id, "byre", map[string]any{"error": err.Error()}, nil, false, false)
if serr != nil {
return serr
}
@@ -694,7 +699,7 @@ func (m *Manager) pump(l *live) {
slog.Error("drop stage output", "session", l.id, "err", err)
}
}
next, err := m.store.EndTurn(ctx, l.id, agentActor, map[string]any{"error": "agent process exited mid-turn"}, nil, false)
next, _, err := m.store.EndTurn(ctx, l.id, agentActor, map[string]any{"error": "agent process exited mid-turn"}, nil, false, false)
if err != nil {
slog.Error("record session exit", "session", l.id, "err", err)
}
@@ -930,7 +935,7 @@ func (m *Manager) turnEnded(ctx context.Context, l *live, res endResult) error {
}
turn := m.event(l, agentActor, "turn.ended", payload)
next, err := m.store.EndTurn(ctx, l.id, agentActor, nil, &turn, review)
next, finalizeBy, err := m.store.EndTurn(ctx, l.id, agentActor, nil, &turn, review, finished && !handoff)
if err != nil {
return err
}
@@ -957,12 +962,13 @@ func (m *Manager) turnEnded(ctx context.Context, l *live, res endResult) error {
go m.continueFromHandoff(l.id, res.result)
return nil
}
if finalizeBy != "" {
m.logUndelivered(ctx, l.id, "turn ended in merge")
go m.finishMerge(l, finalizeBy)
return nil
}
mergeBy := m.takeMerge(l.taskID)
switch {
case mergeBy != "" && finished:
m.logUndelivered(ctx, l.id, "turn ended in merge")
go m.finishMerge(l, mergeBy)
return nil
case mergeBy != "":
go m.releaseMerge(l.projectID, l.taskID)
if !res.isError || interrupted {
+25
View File
@@ -135,6 +135,27 @@ func (m *Manager) recoverMerges(ctx context.Context) error {
return nil
}
func (m *Manager) recoverFinalizing(ctx context.Context) error {
finalizing, err := m.store.FinalizingTasks(ctx)
if err != nil {
return err
}
for _, t := range finalizing {
err = m.store.ClearFinalizing(ctx, t.Id)
if err != nil {
return err
}
sessions, err := m.store.ListSessions(ctx, t.Id)
if err != nil {
return err
}
slog.Error("merge was finishing at startup", "task", t.Id)
m.fileTaskError(ctx, t, sessions, "byre restarted while finishing this task's merge, so it may not have been pushed, or the task's worktree may be gone. Check the default branch before merging again.")
}
return nil
}
func (m *Manager) startHeldMerges(ctx context.Context) {
projects, err := m.store.ProjectsWithQueuedMerges(ctx)
if err != nil {
@@ -253,6 +274,10 @@ func (m *Manager) finishMerge(l *live, login string) {
}
slog.Error("finish merge", "task", l.taskID, "err", err)
cerr := m.store.ClearFinalizing(ctx, l.taskID)
if cerr != nil {
slog.Error("clear merge finalizing", "task", l.taskID, "err", cerr)
}
m.fileError(l, "Merging the task failed: "+err.Error())
err = m.Deliver(ctx, l.id)
if err != nil {
+2 -6
View File
@@ -320,11 +320,7 @@ func (m *Manager) MergeIntoParent(ctx context.Context, task api.Task, user api.U
if found && sess.State == api.AgentSessionStateRunning {
return api.Task{}, fmt.Errorf("%w: subtask %s's agent is mid-turn; wait for the turn to end or interrupt it", ErrConflict, task.Id)
}
mergingBy, err := m.store.MergingBy(ctx, task.Id)
if err != nil {
return api.Task{}, err
}
if mergingBy != "" || task.MergeQueuedAt != nil {
if task.Merging || task.MergeQueuedAt != nil {
return api.Task{}, fmt.Errorf("%w: subtask %s is merging into the default branch", ErrConflict, task.Id)
}
@@ -332,7 +328,7 @@ func (m *Manager) MergeIntoParent(ctx context.Context, task api.Task, user api.U
switch {
case errors.Is(err, store.ErrNotFound):
return api.Task{}, fmt.Errorf("%w: subtask %s is closed", ErrConflict, task.Id)
case errors.Is(err, store.ErrDiscardPending), errors.Is(err, store.ErrParentMergeRequested):
case errors.Is(err, store.ErrDiscardPending), errors.Is(err, store.ErrMerging), errors.Is(err, store.ErrParentMergeRequested):
return api.Task{}, fmt.Errorf("%w: subtask %s: %w", ErrConflict, task.Id, err)
case err != nil:
return api.Task{}, err
+190 -190
View File
@@ -1080,7 +1080,7 @@ type Task struct {
// MergeTitle The title of the squash commit Merge makes: the task title until the agent sets another with set_merge_title.
MergeTitle string `json:"merge_title"`
// Merging Whether a Merge was started and its turn hasn't ended yet.
// Merging Whether a Merge was started and hasn't finished: its turn is running, or the turn ended and byre is squashing, pushing and closing the task.
Merging bool `json:"merging"`
// MergingIntoParent Whether a merge of this subtask into its parent was requested and the parent hasn't merged it yet.
@@ -3339,74 +3339,74 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
// times slower for the Go compiler than parsing a slice literal.
var swaggerSpec = []string{
"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",
@@ -3427,127 +3427,127 @@ var swaggerSpec = []string{
"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",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+13 -6
View File
@@ -23,7 +23,7 @@ var (
ErrMerging = errors.New("the task is merging")
ErrMergingIntoParent = errors.New("the task is merging into its parent task")
ErrHasSubtasks = errors.New("the task has active subtasks")
ErrParentNotOpen = errors.New("the parent task is closed or being discarded")
ErrParentNotOpen = errors.New("the parent task is closed, being discarded or finishing its merge")
ErrParentMergeRequested = errors.New("the subtask is already merging into its parent")
)
@@ -48,12 +48,13 @@ func (s *Store) RequestDiscard(ctx context.Context, taskID, actor string) (Disca
projectID := ""
status := ""
mergingBy := ""
finalizing := ""
parentMergeBy := ""
subtasks := 0
err := tx.QueryRowContext(ctx, `
SELECT project_id, status, merging_by, parent_merge_by, discard_state,
SELECT project_id, status, merging_by, finalizing, parent_merge_by, discard_state,
(SELECT count(*) FROM tasks AS sub WHERE sub.parent_task_id = tasks.id AND sub.status = ?)
FROM tasks WHERE id = ?`, string(api.TaskStatusActive), taskID).Scan(&projectID, &status, &mergingBy, &parentMergeBy, &state, &subtasks)
FROM tasks WHERE id = ?`, string(api.TaskStatusActive), taskID).Scan(&projectID, &status, &mergingBy, &finalizing, &parentMergeBy, &state, &subtasks)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
@@ -67,7 +68,7 @@ func (s *Store) RequestDiscard(ctx context.Context, taskID, actor string) (Disca
return nil
case state != DiscardNone:
return ErrDiscardPending
case mergingBy != "":
case mergingBy != "" || finalizing != "":
return ErrMerging
case parentMergeBy != "":
return ErrMergingIntoParent
@@ -114,8 +115,9 @@ func (s *Store) StartDiscardTurn(ctx context.Context, taskID, sessionID, actor s
})
}
func (s *Store) EndTurn(ctx context.Context, sessionID, actor string, payload map[string]any, turn *api.Event, reviewRequest bool) (DiscardState, error) {
func (s *Store) EndTurn(ctx context.Context, sessionID, actor string, payload map[string]any, turn *api.Event, reviewRequest, finalizeMerge bool) (DiscardState, string, error) {
next := DiscardNone
finalizing := ""
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
taskID := ""
projectID := ""
@@ -167,10 +169,15 @@ func (s *Store) EndTurn(ctx context.Context, sessionID, actor string, payload ma
case state == DiscardRunning && discardSession == sessionID:
next = DiscardClosing
_, err = tx.ExecContext(ctx, `UPDATE tasks SET discard_state = ? WHERE id = ?`, string(DiscardClosing), taskID)
case state == DiscardNone && finalizeMerge:
err = tx.QueryRowContext(ctx, `UPDATE tasks SET finalizing = merging_by, merging_by = '' WHERE id = ? AND merging_by != '' RETURNING finalizing`, taskID).Scan(&finalizing)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
}
return err
})
return next, err
return next, finalizing, err
}
func (s *Store) CloseDiscard(ctx context.Context, taskID string) error {
+37 -11
View File
@@ -446,6 +446,8 @@ CREATE TABLE summary_reads (
seq INTEGER NOT NULL,
PRIMARY KEY (login, task_id)
);
`}, {"add-task-finalizing", `
ALTER TABLE tasks ADD COLUMN finalizing TEXT NOT NULL DEFAULT '';
`}, {"add-task-state", `
ALTER TABLE tasks ADD COLUMN state TEXT NOT NULL DEFAULT '';
ALTER TABLE tasks ADD COLUMN review_requests INTEGER NOT NULL DEFAULT 0;
@@ -758,7 +760,7 @@ func (s *Store) CreateTask(ctx context.Context, t api.Task, frozen *FrozenWorkfl
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
if parentTaskID != "" {
open := false
err := tx.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM tasks WHERE id = ? AND status = ? AND discard_state = '')`,
err := tx.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM tasks WHERE id = ? AND status = ? AND discard_state = '' AND finalizing = '')`,
parentTaskID, string(api.TaskStatusActive)).Scan(&open)
if err != nil {
return err
@@ -811,7 +813,7 @@ func (s *Store) GetTask(ctx context.Context, id string) (api.Task, error) {
func (s *Store) SetMergeTitle(ctx context.Context, id, title, actor string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
projectID := ""
err := tx.QueryRowContext(ctx, `UPDATE tasks SET merge_title = ? WHERE id = ? AND status = ? AND merging_by = '' AND merge_queued_by = '' RETURNING project_id`,
err := tx.QueryRowContext(ctx, `UPDATE tasks SET merge_title = ? WHERE id = ? AND status = ? AND merging_by = '' AND finalizing = '' AND merge_queued_by = '' RETURNING project_id`,
title, id, string(api.TaskStatusActive)).Scan(&projectID)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
@@ -826,7 +828,7 @@ func (s *Store) SetMergeTitle(ctx context.Context, id, title, actor string) erro
func (s *Store) SetMergingBy(ctx context.Context, id, login string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
projectID := ""
err := tx.QueryRowContext(ctx, `UPDATE tasks SET merging_by = ? WHERE id = ? AND discard_state = '' RETURNING project_id`, login, id).Scan(&projectID)
err := tx.QueryRowContext(ctx, `UPDATE tasks SET merging_by = ? WHERE id = ? AND discard_state = '' AND finalizing = '' RETURNING project_id`, login, id).Scan(&projectID)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
@@ -869,6 +871,24 @@ func (s *Store) TakeMergingBy(ctx context.Context, id string) (string, error) {
return login, err
}
func (s *Store) ClearFinalizing(ctx context.Context, id string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
projectID := ""
err := tx.QueryRowContext(ctx, `UPDATE tasks SET finalizing = '' WHERE id = ? AND finalizing != '' RETURNING project_id`, id).Scan(&projectID)
if errors.Is(err, sql.ErrNoRows) {
return nil
}
if err != nil {
return err
}
return emit(api.Event{Kind: "task.merge_ended", ProjectId: &projectID, TaskId: &id, Actor: "byre", Payload: map[string]any{"id": id}})
})
}
func (s *Store) FinalizingTasks(ctx context.Context) ([]api.Task, error) {
return s.queryTasks(ctx, `SELECT `+taskColumns+` FROM tasks WHERE status = ? AND finalizing != ''`, string(api.TaskStatusActive))
}
func (s *Store) MergingTasks(ctx context.Context) ([]api.Task, error) {
return s.queryTasks(ctx, `SELECT `+taskColumns+` FROM tasks WHERE status = ? AND merging_by != ''`, string(api.TaskStatusActive))
}
@@ -921,9 +941,9 @@ func (s *Store) ActiveSubtasks(ctx context.Context, parentID string) ([]api.Task
func (s *Store) SetParentMerge(ctx context.Context, id, login string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
projectID, parentID, status, parentMergeBy, discardState := "", "", "", "", ""
err := tx.QueryRowContext(ctx, `SELECT project_id, parent_task_id, status, parent_merge_by, discard_state FROM tasks WHERE id = ?`, id).
Scan(&projectID, &parentID, &status, &parentMergeBy, &discardState)
projectID, parentID, status, parentMergeBy, discardState, finalizing := "", "", "", "", "", ""
err := tx.QueryRowContext(ctx, `SELECT project_id, parent_task_id, status, parent_merge_by, discard_state, finalizing FROM tasks WHERE id = ?`, id).
Scan(&projectID, &parentID, &status, &parentMergeBy, &discardState, &finalizing)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
@@ -935,6 +955,8 @@ func (s *Store) SetParentMerge(ctx context.Context, id, login string) error {
return ErrNotFound
case discardState != "":
return ErrDiscardPending
case finalizing != "":
return ErrMerging
case parentMergeBy != "":
return ErrParentMergeRequested
}
@@ -1085,7 +1107,7 @@ func (s *Store) DiscardTask(ctx context.Context, id, actor string) (api.Task, er
func (s *Store) closeTask(ctx context.Context, id string, status api.TaskStatus, commit, actor, kind string, payload map[string]any) (api.Task, error) {
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
res, err := tx.ExecContext(ctx, `UPDATE tasks SET status = ?, closed_at = ?, merge_commit = ? WHERE id = ? AND status = ?`,
res, err := tx.ExecContext(ctx, `UPDATE tasks SET status = ?, closed_at = ?, merge_commit = ?, finalizing = '' WHERE id = ? AND status = ?`,
string(status), now(), commit, id, string(api.TaskStatusActive))
if err != nil {
return err
@@ -1373,9 +1395,10 @@ func (s *Store) transitionSession(ctx context.Context, id string, from *api.Agen
current := ""
role := ""
discard := ""
finalizing := ""
err := tx.QueryRowContext(ctx, `
SELECT coalesce(t.id, ''), coalesce(t.project_id, ''), s.owner, s.state, s.role, coalesce(t.discard_state, '') FROM agent_sessions s LEFT JOIN tasks t ON t.id = s.task_id WHERE s.id = ?
`, id).Scan(&taskID, &projectID, &owner, &current, &role, &discard)
SELECT coalesce(t.id, ''), coalesce(t.project_id, ''), s.owner, s.state, s.role, coalesce(t.discard_state, ''), coalesce(t.finalizing, '') FROM agent_sessions s LEFT JOIN tasks t ON t.id = s.task_id WHERE s.id = ?
`, id).Scan(&taskID, &projectID, &owner, &current, &role, &discard, &finalizing)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
@@ -1389,6 +1412,9 @@ func (s *Store) transitionSession(ctx context.Context, id string, from *api.Agen
if state == api.AgentSessionStateRunning && role == string(api.Implementer) && discard != "" {
return fmt.Errorf("%w: task %s is being discarded", ErrStateChanged, taskID)
}
if state == api.AgentSessionStateRunning && role == string(api.Implementer) && finalizing != "" {
return fmt.Errorf("%w: task %s is finishing its merge", ErrStateChanged, taskID)
}
_, err = tx.ExecContext(ctx, `UPDATE agent_sessions SET state = ? WHERE id = ?`, string(state), id)
if err != nil {
@@ -1586,7 +1612,7 @@ func settleTaskState(ctx context.Context, tx *sql.Tx, emit emitFunc, taskID, act
const taskStateExpr = `CASE
WHEN tasks.status != 'active' THEN tasks.status
WHEN tasks.discard_state != '' THEN 'discarding'
WHEN tasks.merging_by != '' THEN 'merging'
WHEN tasks.merging_by != '' OR tasks.finalizing != '' THEN 'merging'
ELSE coalesce((
SELECT CASE
WHEN s.state != 'idle' THEN s.state
@@ -1643,7 +1669,7 @@ func (s *Store) ClearReviewRequests(ctx context.Context) error {
const projectColumns = `id, name, repo_path, default_branch, remote, created_by, created_at`
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_run, workflow_stage, parent_task_id, parent_merge_by, state, merging_by != '', 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)`
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_run, workflow_stage, parent_task_id, parent_merge_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)`
const sessionColumns = `id, coalesce(task_id, ''), owner, vendor, model, vendor_session_id, state, role, created_by, created_at, forked_from, forked_at`