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
+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 {