Refuse turns, discard and subtasks while a merge is finalizing
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user