Kanban boards & tasks
Last updated: 2025-11-30Board model
- Each project owns a single board representing its workflow.
- Boards are backed by
boards,columns, andcardstables in SQLite and exposed via:/boards/:boardId/*/projects/:projectId/board/*
- The Tasks module is responsible for:
- Creating and maintaining the canonical board state.
- Emitting
board.state.changedandcard.*events after mutations.
Default columns
- When a project is created, a Tasks listener seeds a default five-column workflow:
- Backlog
- In Progress
- AI Review
- Human Review
- Done
- For existing projects created before the AI Review feature, the “Review” column is automatically renamed to “Human Review” and the “AI Review” column is inserted before it on first load.
- Column titles are stored in the database and can be changed in future workflow customisation work, but the default behavior assumes these names when determining where to move cards on Attempt events.
Per-card actions menu
- Each card on the board exposes a three-dots menu in its header for quick, context-aware actions.
- Backlog columns:
- Show Enhance ticket… (opens Edit Ticket and runs a one-off inline enhancement).
- Show Start work (starts an Attempt using the project’s default agent/profile, respecting dependency blocking).
- Always include Open details and Edit….
- In Progress columns:
- Show Stop Attempt when the latest Attempt is running.
- Show AI Review button when the latest Attempt has succeeded, triggering automated code review.
- Always include Open details and Edit….
- AI Review and Human Review columns:
- Show Create PR… (opens the same pull request dialog as the inspector, with identical defaults).
- Show Open in editor (reuses the Attempt editor integration and enabled/disabled rules).
- Always include Open details and Edit….
- Done and any non-standard columns:
- Only expose Open details and Edit…, keeping Attempt and Git actions read-only for completed tasks.
Cards, ticket keys, and details
- Cards represent tasks on the board. Each card has:
titledescription(optional)ticketKey(optional, e.g.ABC-123, often derived from GitHub issues)githubIssuemetadata when a card is mapped to a GitHub issue via import, background sync, or when the Create Ticket dialog exports an issue for the card.- When present, the column view shows a GitHub icon badge that links to the original issue and displays the issue number in a tooltip on hover.
- Exported issues stay linked on the card, so editing the title/description automatically updates the GitHub issue and keeps the badge pointing at the right place.
dependsOnrelationships to other cards on the same boardisEnhanced– boolean that flips totruewhen a ticket enhancement suggestion has been accepted; enhanced cards gain a dedicated badge and subtle highlight so you can tell at a glance that the title/description were AI‑refined.aiApproved– boolean that flips totruewhen AI review approves the work; approved cards display a bot icon badge.aiReviewIteration– counter tracking the number of AI review iterations for the current work cycle; resets to 0 when approved.disableAutoCloseOnPRMerge– per-card boolean that opt-outs this card from automatic Human Review→Done transitions when the project has auto-close-on-PR-merge enabled.
- On the board itself, cards surface the ticket key (when present) alongside the title and compact icon-based status indicators for blocked/enhancing/failed/succeeded/AI-approved states so that key context is visible at a glance, while omitting the full description to keep columns compact. Card headers maintain a fixed height to prevent layout shifts when badges change.
- Blocked cards that are still waiting on dependencies wrap the card view in a tooltip listing the blockers so you can see what is pending without opening the inspector.
- Cards with failed Attempts display an alert icon badge and destructive styling (red border and background) to make issues immediately identifiable. Failed cards can be clicked to open the Card Inspector, where you can review the failure and retry the Attempt.
- The “card inspector” in the UI lets you edit these fields, with the ticket key surfaced prominently for quick scanning.
Ordering and moves
- Cards are ordered within a column using an integer index.
- Moves are performed via a single endpoint:
PATCH /boards/:boardId/cards/:cardIdwithcolumnIdandindex.- The update endpoint also accepts
isEnhanced: booleanso clients can toggle the badge/highlight it mirrors when an enhancement is accepted or reverted.
- The server:
- Validates the target column belongs to the board.
- Updates indices in the source and target columns.
- Returns the updated card plus fresh column snapshots so the UI can update without a full board reload.
- The UI provides optional client-side sorting (newest-first, oldest-first, custom) as a display layer on top of the server-side ordering. When “custom order” is selected, the UI respects the server-maintained indices; when date-based sorting is active, the UI reorders cards for display without changing their underlying positions in the database.
Task dependencies and blocking
- Dependencies are stored in a
card_dependenciestable (card_id,depends_on_card_id) and managed by thecore/projects/dependencieshelpers. - Rules enforced by the server:
- A card can only depend on other cards on the same board.
- Cycles are prevented (attempting to create them returns a validation error).
- When calculating whether a card is blocked, any dependency not in a Done column is considered incomplete.
- When you try to move a blocked card into In Progress:
- The Projects handlers call
isCardBlocked(cardId). - If
blockedistrue, the API responds with HTTP409and detail"Task is blocked by dependencies". - The UI uses this status to show a clear toast explaining that dependencies must be completed first.
- The Projects handlers call
Automatic column transitions
- Board state reacts to Attempt lifecycle events:
attempt.started→ Tasks listener moves the card into In Progress.attempt.completed:- If status is
succeeded→ card remains in In Progress until AI Review is requested. - If status is
failedorstopped→ card stays in In Progress and displays a “Failed” badge with red styling on the board.
- If status is
attempt.aiReview.requested→ card moves to AI Review column.attempt.aiReview.completed:- If approved → card moves to Human Review and displays “AI Approved” badge.
- If changes requested → card moves back to In Progress, increments iteration counter, and resumes the Attempt with AI feedback.
- If max iterations exceeded → card moves to Human Review to avoid infinite loops.
- When a card is moved into Done:
- A Tasks listener triggers workspace cleanup for the associated Attempt (removing its worktree and branch).
Real-time updates
- The WebSocket module maintains per-board channels that:
- Validate access and send an initial
statesnapshot on connect. - Accept commands for create / move / update / delete, which are forwarded to the Tasks service.
- Validate access and send an initial
- Separate listeners subscribe to domain events and broadcast updates:
board.state.changedattempt.*git.*github.pr.createdagent.profile.changed,agent.registered
- Clients use these WebSocket messages to keep boards, task details, and Attempt status in sync in real time, without manual refresh.