diff options
Diffstat (limited to 'internal/api/taskops.go')
| -rw-r--r-- | internal/api/taskops.go | 14 |
1 files changed, 13 insertions, 1 deletions
diff --git a/internal/api/taskops.go b/internal/api/taskops.go index 2b133ac..a2668f3 100644 --- a/internal/api/taskops.go +++ b/internal/api/taskops.go @@ -36,7 +36,9 @@ type badRequestError struct{ msg string } func (e *badRequestError) Error() string { return e.msg } -func badRequestf(format string, a ...any) error { return &badRequestError{msg: fmt.Sprintf(format, a...)} } +func badRequestf(format string, a ...any) error { + return &badRequestError{msg: fmt.Sprintf(format, a...)} +} // submitTaskSpec describes a task a chatbot wants created and immediately run. type submitTaskSpec struct { @@ -127,6 +129,16 @@ func (s *Server) acceptTask(ctx context.Context, id string, actor event.Actor) e if err != nil { return errTaskNotFound } + // BLOCKED -> COMPLETED is a valid task.ValidTransition (added so + // internal/executor.Pool's maybeUnblockParent can complete a subtask + // whose own children all finished), but that transition must only ever + // happen via that internal mechanism -- never via a direct accept call, + // which would bypass the subtask-completion invariant (or silently + // foreclose an open ask_user question) for a task that isn't actually + // done yet. + if t.State == task.StateBlocked { + return conflictf("task cannot be accepted while BLOCKED (awaiting subtasks or a question)") + } if !task.ValidTransition(t.State, task.StateCompleted) { return conflictf("task cannot be accepted from state %s", t.State) } |
