Task board & automation

The board turns "things we intend to do" into "things that get executed": a card carries a project, an agent or a workflow, and the executor picks it up once it reaches the todo column. Status transitions, run records and comment loops all go through one service path. The frontend has no dedicated SSE stream; it relies on conditional polling.

Backlog → Todo / Archived Todo the only trigger state InProgress executor running InReview comment can re-run Done sets CompletedAt Transitions pass a whitelist first invalid moves are rejected Blocked sets BlockedReason on entry, clears on exit Frontend polling 3-second refresh while a run is Running / WaitingAnswer, or an automated task sits in Todo / InProgress Polling stops when neither condition holds
The state machine is whitelist-based: only allowed transitions pass. Todo is the single automation trigger; Done and Archived only stamp timestamps.

State machine

StatusAllowed transitions
BacklogTodo · Archived
TodoInProgress · Backlog · Blocked
InProgressInReview · Todo · Blocked
InReviewDone · InProgress · Blocked
BlockedTodo · InProgress
DoneArchived · InReview
Archived(terminal)

The transition table is a hard-coded whitelist and invalid jumps are rejected. Timestamps follow one rule set: entering Done sets CompletedAt and leaving clears it; entering Archived sets ArchivedAt and leaving clears it; entering Blocked keeps or sets BlockedReason and leaving clears it. Priority values are Low / Medium / High / Urgent.

Card model

GroupFields
CoreId · Title · Description · Status · Priority · Assignee · Tags · DueDate · SortOrder · BlockedReason · CompletedAt · ArchivedAt
ConfigurationProjectId / ProjectName · AgentId / AgentName · AgentPrompt / AgentPromptName · WorkflowId / WorkflowName
Runs & collaborationHasAutomation · LastRunStatus · LastRunId · CommentCount
CommentsKanbanTaskCommentDto: AuthorType · AuthorName · Content · RunId · CreatedAt
RunsKanbanTaskRunDto: Kind · Trigger · Status · Input · Output · Error · WorkflowRunId · StartedAt · CompletedAt
Detail aggregateKanbanTaskDetailDto = task + comments + runs + steps

HasAutomation is true when a project agent, a workflow or a custom prompt is configured. AgentPrompt / AgentPromptName are only stored when no project agent is selected (AgentId empty) — that is the real difference between the "pick an agent from the project's .github" path and the "write your own prompt" path.

Endpoints

EndpointPurpose
GET /api/kanban-tasksBoard data: seven columns plus stats; the Archived column only returns tasks archived in the last 7 days
POST /api/kanban-tasks · PUT /api/kanban-tasks/{id}Create / update; a save landing in Todo attempts to trigger automation
PUT /api/kanban-tasks/{id}/moveDrag between columns: validates the whitelist, maintains timestamps and triggers automation when entering Todo
GET /api/kanban-tasks/{id}/detailDetail aggregate (task + comments + runs + steps)
POST /api/kanban-tasks/{id}/commentsComment; in InReview / Blocked with comment-triggered automation enabled it pulls the task back to InProgress and re-runs with trigger Comment
POST /api/kanban-tasks/{id}/rerunRe-run; only allowed from Todo
GET /api/kanban-tasks/{id} · DELETE /api/kanban-tasks/{id}Fetch or delete a single card

Automation and status progression

  1. Triggers live in three places: after create, after update when the status changed, and when moving into Todo; comment loops and manual re-runs add two more.
  2. Execution has exactly one gate: the task must be in Todo and have automation configured; otherwise only the status changes.
  3. A run record is written before execution (Trigger records the source: create / move / comment / rerun) and the run produces steps and comments.
  4. Status progression happens in the service layer; failures land in the run's Error and in LastRunStatus on the task.
The precise answer to "does the board update status by itself" is: the backend writes status at the trigger points (column moves, comment loops, executor write-back) and the frontend pulls the change for display. Dragging a card to InProgress only changes status; it does not start execution on its own.

Detail enrichment and frontend refresh

ItemRule
Name enrichmentList and detail both fill ProjectName (managed project), AgentName (project agent), WorkflowName, LastRunStatus and CommentCount; the list uses batched dictionary lookups to avoid N+1
Display fallbackProject row: projectId ? projectName ?? projectId; agent row: shows agentName ?? agentPromptName ?? agentId when either agentId or agentPromptName exists, otherwise "not set"; workflow row works the same way
Detail orderingSteps by Sequence then creation time; runs newest first; comments oldest first
Polling conditionBoth board and detail refetch every 3000ms while a run is Running / WaitingAnswer, or an automated task sits in Todo / InProgress; otherwise polling stops
Refresh channelReact Query polling with invalidation; there is no task-board specific SSE event

Problems of the form "project and agent are configured but the detail page shows nothing" usually come from the display fallback: when the agent is picked from the project's .github, the persisted fields are AgentPrompt / AgentPromptName rather than AgentId, so checking only AgentId renders empty. The current implementation shows both sources (selecting a project loads that project's .github agents and writes the prompt fields back).

Stats and archive window

  • Stats: Total / Active / Done / Archived / Overdue / DueSoon.
  • The archived column keeps a 7-day window so the board cannot grow without bound; long-term history lives in run records and comments.

Practice notes

  • Automation only triggers from Todo: put the card in the todo column instead of dragging it straight to in-progress.
  • Comments are a loop, not a note: commenting in review or blocked carries context back into a re-run, which feeds feedback to the agent.
  • Polling is conditional by design: a page that stops refreshing while nothing is running is behaving correctly.
  • Project agent and custom prompt are mutually exclusive: selecting an agent means a hand-written prompt is not stored.