From 1b4be14d02d3fd17beeddcce8d5056d51b19296c Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Mon, 9 Mar 2026 01:11:05 +0000 Subject: task: allow requeueing BUDGET_EXCEEDED tasks Permitted BUDGET_EXCEEDED -> QUEUED transition in ValidTransition. Updated frontend to show 'Restart' button for BUDGET_EXCEEDED tasks, allowing them to be requeued after failure. --- internal/task/task.go | 7 ++++--- internal/task/task_test.go | 1 + web/app.js | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/task/task.go b/internal/task/task.go index 8bb3214..447c4a3 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -121,9 +121,10 @@ func ValidTransition(from, to State) bool { StateRunning: {StateReady, StateCompleted, StateFailed, StateTimedOut, StateCancelled, StateBudgetExceeded, StateBlocked}, StateReady: {StateCompleted, StatePending}, StateFailed: {StateQueued}, // retry - StateTimedOut: {StateQueued}, // retry - StateCancelled: {StateQueued}, // restart - StateBlocked: {StateQueued, StateReady}, // answer received → re-queue as resume execution + StateTimedOut: {StateQueued}, // retry + StateCancelled: {StateQueued}, // restart + StateBudgetExceeded: {StateQueued}, // retry + StateBlocked: {StateQueued, StateReady}, // answer received → re-queue as resume execution } for _, allowed := range transitions[from] { if allowed == to { diff --git a/internal/task/task_test.go b/internal/task/task_test.go index 5cb12d0..637baf5 100644 --- a/internal/task/task_test.go +++ b/internal/task/task_test.go @@ -25,6 +25,7 @@ func TestValidTransition_AllowedTransitions(t *testing.T) { {"running to blocked (question)", StateRunning, StateBlocked}, {"blocked to queued (answer resume)", StateBlocked, StateQueued}, {"blocked to ready (parent unblocked by subtasks)", StateBlocked, StateReady}, + {"budget exceeded to queued (retry)", StateBudgetExceeded, StateQueued}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/web/app.js b/web/app.js index 9742c25..adaa0a2 100644 --- a/web/app.js +++ b/web/app.js @@ -125,7 +125,7 @@ function createTaskCard(task) { } // Footer: action buttons based on state - const RESTART_STATES = new Set(['FAILED', 'CANCELLED']); + const RESTART_STATES = new Set(['FAILED', 'CANCELLED', 'BUDGET_EXCEEDED']); if (task.state === 'PENDING' || task.state === 'RUNNING' || task.state === 'READY' || task.state === 'BLOCKED' || task.state === 'TIMED_OUT' || RESTART_STATES.has(task.state)) { const footer = document.createElement('div'); footer.className = 'task-card-footer'; -- cgit v1.2.3