summaryrefslogtreecommitdiff
path: root/web/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/app.js')
-rw-r--r--web/app.js54
1 files changed, 54 insertions, 0 deletions
diff --git a/web/app.js b/web/app.js
index 2f5aacf..9cd3100 100644
--- a/web/app.js
+++ b/web/app.js
@@ -3350,6 +3350,60 @@ export function groupStoriesByColumn(stories) {
return groups;
}
+// ---------------------------------------------------------------------------
+// Tasks board — column model
+// ---------------------------------------------------------------------------
+
+export const TASK_COLUMNS = [
+ { key: 'queue', label: 'Queue', states: ['PENDING', 'QUEUED'] },
+ { key: 'running', label: 'Running', states: ['RUNNING', 'BLOCKED'] },
+ { key: 'ready', label: 'Ready', states: ['READY'] },
+ { key: 'interrupted', label: 'Interrupted', states: ['FAILED', 'TIMED_OUT', 'CANCELLED', 'BUDGET_EXCEEDED'] },
+ { key: 'done', label: 'Done', states: ['COMPLETED'] },
+];
+
+// columnForTaskState returns the column key for a given task state, falling
+// back to 'queue' for an empty/unrecognized state so a task is never dropped
+// off the board entirely.
+export function columnForTaskState(state) {
+ const col = TASK_COLUMNS.find(c => c.states.includes(state));
+ return col ? col.key : 'queue';
+}
+
+// Sort directions are per-column, matching the semantics the old (removed)
+// panels used:
+// queue — oldest-first (FIFO)
+// ready — oldest-first (longest-waiting surfaces first)
+// interrupted — newest-first (most recent failure is most urgent)
+// done — newest-first (most recently completed is most relevant)
+// running — newest-first (new-but-inconsequential default)
+const TASK_COLUMN_SORT = {
+ queue: (a, b) => new Date(a.created_at || 0) - new Date(b.created_at || 0),
+ running: (a, b) => new Date(b.created_at || 0) - new Date(a.created_at || 0),
+ ready: (a, b) => new Date(a.created_at || 0) - new Date(b.created_at || 0),
+ interrupted: (a, b) => new Date(b.created_at || 0) - new Date(a.created_at || 0),
+ done: (a, b) => new Date(b.created_at || 0) - new Date(a.created_at || 0),
+};
+
+// groupTasksByColumn returns { [columnKey]: task[] }, each sub-array sorted
+// per the per-column sort direction above.
+export function groupTasksByColumn(tasks) {
+ const groups = {};
+ for (const col of TASK_COLUMNS) groups[col.key] = [];
+ for (const t of tasks || []) {
+ const key = columnForTaskState(t.state);
+ if (!groups[key]) groups[key] = [];
+ groups[key].push(t);
+ }
+ for (const col of TASK_COLUMNS) {
+ const cmp = TASK_COLUMN_SORT[col.key];
+ if (cmp) groups[col.key].sort(cmp);
+ }
+ return groups;
+}
+
+// ---------------------------------------------------------------------------
+
// A story "has reached validation" once evaluator verdicts could plausibly
// exist for it — VALIDATING or later in the lifecycle (see StoryOrchestrator
// stage 2 in CLAUDE.md). Used to gate the eval-verdict indicator so we don't