refactor(shared): single TaskStatus enum replaces dual db-status / ui_status split #1183
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/agent-hooks!1183
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "dev/1163"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
packages/shared/src/task-status.tswith the canonicalTaskStatusunion (all DB + in-memory values includingaborted_no_skill),DisplayStatus(UI display states, same values as the oldTaskUiStatus), andtoDisplayStatus(s: TaskStatus): DisplayStatus— a simple, exhaustive mapping function.ui_statusandui_status_reasonfrom the/historyAPI wire format; components now calltoDisplayStatus(task.status)directly.HistoryListEntryloses both fields;fallbackUiStatushelper removed.task_history.statusDrizzle column typed with.$type<TaskStatus>()to align schema to the shared union.StatusPillandboard.tsupdated to referenceDisplayStatusinstead ofTaskUiStatus;TaskUiStatusis kept as a@deprecatedalias intask-ui-status.tsfor backward compat.Closes #1163
Test plan
just qapasses (typecheck + lint + format + all tests)ui_status/ui_status_reasonremoved fromEXPECTED_KEYSui_status(from board API, not history API) intentionally unchangedCode Review —
refactor(shared): single TaskStatus enum replaces dual db-status / ui_status splitCI: ✅ green. Architecture is sound and the refactor is well-executed overall. Two issues need to be fixed before merge.
✅ What's good
TaskStatus(DB/runtime) andDisplayStatus(UI) live together intask-status.tswith a single exhaustive conversion function. Conceptually correct.default— TypeScript will catch any futureTaskStatusadditions that aren't mapped. The right pattern.aborted_no_skillproperly included — it was missing from the oldTaskStatusintask-record.ts. Now canonically modelled.fallbackUiStatusremoval is safe — withHistoryListEntry.statusnow typed asTaskStatusandtoDisplayStatusreplacing the derivation, the client-side fallback is correctly eliminated.TaskUiStatus = DisplayStatusas a@deprecatedre-export is the right way to handle this without breaking other consumers.🔴 Issues requiring changes
1. Dead code in
live-runs-board.tsx—reviewingbranch is unreachableFile:
apps/web/src/features/agents/live-runs-board.tsx,taskColumnfunctiontoDisplayStatusis documented to never produce"reviewing"fromTaskStatusalone. SinceHistoryListEntry.ui_statushas been removed,uiStatushere is always the result oftoDisplayStatus(task.status ?? "running"), meaning"reviewing"is unreachable dead code. This is misleading and should be removed:Same reasoning applies to any other condition in this file that checks
uiStatus === "reviewing"oruiStatus === "waiting"— audit and trim them.2.
ui_status_reasonhover tooltip silently dropped from history/fleet viewsFiles:
task-detail.tsx,task-list.tsx,live-runs-board.tsx(three<StatusPill>callsites)The
reasonprop is gone from all three callsites becauseui_status_reasonhas been removed fromHistoryListEntry. For active tasks the reason strings ("Awaiting approval to write .env", etc.) came from the server viaderiveTaskUiStatusReason. Removing them silently is a user-visible regression — the hover tooltip that tells an operator why a task is in thewaitingstate disappears.The board card API is intentionally unchanged (noted in the PR description), but the fleet view (
live-runs-board.tsx) usesfetchHistory(), not the board endpoint. Operators watching a running task from the fleet/history views lose the context tooltip entirely.Options:
ui_status_reasononHistoryListEntry(server still has the data viaderiveTaskUiStatusReason) — keep it as a standalone field independent ofui_status.Either is fine, but the current state silently removes a feature without acknowledgement in the PR body.
ℹ️ Minor observations (no change required)
deriveTaskUiStatusdeprecated but still in use server-side — the board endpoint (not in this diff) presumably still calls it. Deprecating without a migration path is a bit confusing; a follow-up to move the rich derivation to usetoDisplayStatusas the base would clean this up.tasks.tsimport ordering —import type { TaskStatus }is inserted before the drizzle import. Biome will normalise this; no issue.Summary: Fix the dead
reviewingbranch intaskColumn, and either restoreui_status_reasononHistoryListEntryor explicitly acknowledge its removal. Everything else is clean.f1d7c47a1f1bfda8d2faCode Review — PR #1183
Clean, well-scoped refactor. The new
task-status.tsmodule is clearly written, thetoDisplayStatusswitch is exhaustive (TypeScript will catch regressions), and removingui_status/ui_status_reasonfrom the/historywire format is the right call. CI is green. Two issues to fix before merge.🔴 Required changes
1. Error hover tooltip silently dropped from history rows
StatusPill'sreasonprop is now alwaysundefinedfor every history row intask-detail.tsx,task-list.tsx, andlive-runs-board.tsx. Before this PR, failure tasks showedtask.error.slice(0, 200)on hover viaui_status_reason; that detail is now gone with no replacement.task.erroris still present onHistoryListEntry. The fix is a one-liner in each of the three components — passreason={task.error ?? undefined}to the<StatusPill>when rendering history rows. No API change needed.AC #1163 never explicitly said to remove the reason; this looks like an unintended side-effect of stripping
ui_status_reasonfrom the wire type.2.
deriveTaskUiStatusfalls through foraborted_no_skill→ maps to"executing"(wrong)task-status.tsaddsaborted_no_skilltoTaskStatus, butderiveTaskUiStatusintask-ui-status.tsdoesn't handle it — the if-chain falls through toreturn "executing". It should return"abandoned".In practice this is unreachable today (board cards are live tasks;
aborted_no_skillis terminal), but it's a silent semantic error on a deprecated-but-still-exported function. Fix: addif (status === "aborted_no_skill") return "abandoned";alongside the existingcancelled/interruptedbranch.🟡 Worth noting (non-blocking)
deriveTaskUiStatusalso missespaused— falls to"executing"instead of"waiting". Pre-existing bug not introduced here, but since you're already touching the deprecation annotations this is the natural time to fix it.AC "No raw string literals for status values in query files" — not addressed in the diff. If any infrastructure query files use raw
"running"/"queued"etc. in SQL expressions the typecheck won't catch them. Worth a quick grep before closing #1163.mergeable: false— the PR has a conflict with main. Needs a rebase before merge even after the above fixes.AC verification against #1163
task-status.tsexportsTaskStatus,DisplayStatus,toDisplayStatusindex.tsre-exports all three.$type<TaskStatus>()ui_statusremoved from/historywire formattoDisplayStatus(task.status)StatusPillprops unchangedjust qapasses1bfda8d2fa19aabe1c91