UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

679 lines (554 loc) • 46.9 kB
--- summary: "Optional Workboard boards for agent-owned cards and rule-based session views" read_when: - You want a Kanban-style workboard in the Control UI - You are enabling or disabling the bundled Workboard plugin - You want to track planned agent work without an external project manager - You want sessions grouped by session status, observer health, and pull-request state title: "Workboard plugin" doc-schema-version: 1 --- The Workboard plugin adds an optional Kanban-style board to the [Control UI](/web/control-ui): agent-sized work cards, assignment to agents, and a link back to the card's task, run, and Control UI session. A separate **Sessions board** groups existing sessions into editable columns using rules over Gateway-owned facts. Workboard is intentionally small: it tracks local operating work for one OpenClaw Gateway. It is not a replacement for GitHub Issues, Linear, Jira, or other team project management systems. ## Enable it Workboard is bundled but disabled by default: 1. Open **Plugins** in the Control UI, or use `/plugins` relative to the configured Control UI base path. For example, a base path of `/openclaw` uses `/openclaw/plugins`. 2. Open the **Workboard** plugin, select **Lifecycle**, and turn on the enabled switch. Because Workboard is included with OpenClaw, it does not need an **Install** action. 3. Wait for the lifecycle action to finish, then open the Workboard tab. The Workboard tab appears in the Control UI nav after the plugin runtime loads. While it is disabled, the tab stays hidden from navigation. Opening the `/workboard` route directly while the plugin is disabled or blocked by `plugins.allow`/`plugins.deny` shows a plugin-unavailable state instead of card data. The equivalent CLI workflow is: ```bash openclaw plugins enable workboard openclaw dashboard ``` Enablement applies to a running Gateway automatically. If it is offline, start it before opening the dashboard. See [Apply changes and inspect](/plugins/manage-plugins#apply-changes-and-inspect). ## Configuration Workboard has no plugin-specific config. Enable/disable it with the standard plugin entry: ```json5 { plugins: { entries: { workboard: { enabled: true, config: {}, }, }, }, } ``` ```bash openclaw plugins disable workboard ``` ## Board appearance While Workboard is open, its sidebar entry expands to show both Cards and Sessions boards, with the open board highlighted. Select a nested board to open it. Sidebar labels use the board name; boards with the same name include their kind in parentheses, such as **Planning (cards)** and **Planning (sessions)**. Boards you create in the Control UI are pinned in the sidebar immediately. Use **Customize** to pin any other board or remove a pin; removed pins stay removed. Pinned boards remain available as top-level entries when you leave Workboard, and you can drag them to reorder them. Choose **New board**, then **Cards** (the default) or **Sessions**. A Sessions board starts with the columns described below. A board's kind is permanent; create another board to use the other kind. Existing boards remain Cards boards. Use **Edit board** to change a board's name, icon, and color. **Reset to default** clears the icon and color when you save; canceling leaves the saved board unchanged. For Sessions boards, the same dialog also edits column labels, colors, descriptions, column order, and the fallback column. Ask the Board agent to edit column rules. For `workboard.boards.upsert`, omitting `icon` or `color`, passing `null`, or passing an empty string preserves the existing value, including for older clients. To clear appearance explicitly, send `clearAppearance: ["icon", "color"]`, or list only the field you want to clear. Listed fields are cleared even if the request also supplies a replacement value for them. Other fields retain their ordinary update behavior. `clearAppearance` must be an array containing only `"icon"` and `"color"`; an empty array changes nothing. Clients using this argument need a Gateway version that supports explicit appearance clearing; older Gateways do not implement this reset. ## Sessions board Use a Sessions board to see where your conversations stand without creating cards. By default, it includes sessions from all configured agents with activity in the last 72 hours and excludes archived sessions. Each session appears in exactly one column. Open a tile to continue its conversation; the tile also shows its agent, run state, observer headline when available, pull requests, and recent activity. The agent filter narrows the displayed sessions without changing the saved board scope. **People filter:** Choose **Everyone** (the default), **Involving me**, or a person beside the agent filter. Involving me shows sessions you own or previously prompted; a person selects their profile associations. The choice is remembered per viewer, device, Gateway, and board. It does not change the shared board, rules, or the Board agent. API clients can pass `view: { involvingMe?: boolean, involvingProfileId?: string, includePeople?: boolean }` to `workboard.sessionsBoard.read`; `includePeople` returns the people facet for the picker. Columns are rules over Gateway-owned session status, observer health, and pull-request state. Health comes from the Gateway session observer: live digests for sessions someone is watching in the Control UI, and a terminal digest when an observed run ends. Sessions nobody watches have no health, so they match only run and pull-request rules. Reads follow the current caller's session visibility; board specs and pins follow the Gateway's trusted-operator model. Interactive edits are admitted under the caller's live authority immediately before the write. Draft and incognito sessions never enter a Sessions board or its facts reads. New Sessions boards use these columns, in this order: | Column | Rule | | ----------- | ----------------------------------------------------------------- | | Needs input | Observer health is `waiting-on-user`. | | Stuck | Observer health is `stuck` or `failed`, or run state is `failed`. | | Working | Run is active. | | In review | A pull request is open or draft. | | Merged | A pull request is merged. | | Done | Observer health is `done`. This is also the fallback column. | Placement uses an operator pin when its column still exists, then the first matching column in saved order, then the fallback column. `match` accepts one rule or an any-of array such as Stuck's `[{health:["stuck","failed"]},{run:["failed"]}]`, with every field within a rule required to match. Values within a field are alternatives. Column names and descriptions help people understand the board; `match` rules decide placement. Drag a session into another column, or ask the Board agent to move it, to pin its placement. Pins remain in effect when session facts change. If a pinned column is removed, the board applies its rules again. Tile tooltips distinguish **by rule** and **pinned**. Facts update live from session changes, with automatic board rereads at most once every five seconds. Later events keep invalidating facts without delaying that reread. The board reuses unchanged facts across boards until a session change invalidates them, and concurrent reads share one facts refresh per board. Session visibility and people filters remain specific to each caller. Reads use prepared Gateway facts without waiting for Git or pull-request requests. Missing pull-request facts refresh in the background and announce a board change when ready. An inline warning names the reason when facts or pull-request information are unavailable, including on an empty board. A failed facts read keeps the last known facts and placement; sessions with no known facts use the fallback column with reason `facts-unavailable`. A session whose available facts match no rule also uses that reason while its pull-request facts are unknown. Opening a board starts any needed background refresh; unchanged sessions do not refresh merely because time passed. When the Control UI host supports a session dock, **Board agent** opens a conversation beside the board. Its first use creates and saves a dedicated conversation named **Sessions board · &lt;board name&gt;**. The Board agent is the only model used by the board, invoked on demand to change columns, rules, scope, or pins using these tools: | Tool | Arguments and behavior | | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `workboard_sessions_board_read` | Optional `boardId`; returns board, columns, sessions, and any warning. | | `workboard_sessions_board_update` | Optional `boardId`, plus `columns` or `scope`. `columns` replaces the complete ordered list; retain ids when renaming labels. | | `workboard_sessions_board_move` | Optional `boardId`, required `sessionKey` and `columnId`; pins that session's placement. | These three tools are available to every agent once the plugin is enabled; unlike the card tools they need no `tools.allow` entry, so the Board agent works out of the box. Omit `boardId` only when exactly one Sessions board exists. The dock attaches page context to the conversation, but plugin tools do not receive that context as a structured argument. With several Sessions boards, the agent must pass the board id from that context or from `workboard_boards`. Specs allow 2–12 columns with unique lowercase slug ids (1–48 characters), labels (1–60), and descriptions (1–400). Exactly one column must have `fallback: true`. Optional colors use the board palette. `scope` accepts `agentIds`, `includeArchived`, and a positive `maxAgeHours`. Rules can match `health`, `run` (`active`, `idle`, `failed`), `pullRequest` (`none`, `open`, `draft`, `merged`, `closed`), and `archived`. Unknown pull-request state does not count as `none`. The Gateway methods are `workboard.sessionsBoard.read` (`operator.read`) and `workboard.sessionsBoard.update` and `.move` (`operator.write`). All require `boardId`. Update takes a `patch` object with the spec fields, including the dock's `agentSessionKey`; move additionally requires `sessionKey` and `columnId`. Create with `workboard.boards.upsert` and `kind: "sessions"`, or with `workboard_board_create`. Changing an existing board's kind is rejected. Workboard stores board specs and operator pins in SQLite. Session status, observer digests, and pull-request state remain owned by the Gateway. On plugin startup, older automatic placements are removed; operator pins are preserved. Boards whose rules still match the previous defaults receive the new run-based rules; only the old default column order is reordered. Customized rules, labels, descriptions, and custom column order are preserved. Sessions boards never hold cards: card creation, capture, and dispatch reject a Sessions board destination. Deleting one removes its pins without deleting any sessions or its Board agent conversation. ## Card fields | Field | Values | | ----------- | ------------------------------------------------------------------------------------------------------------- | | `status` | `triage`, `backlog`, `todo`, `scheduled`, `ready`, `running`, `review`, `blocked`, `done` | | `priority` | `low`, `normal`, `high`, `urgent` | | `labels` | free-form strings | | `agentId` | optional assigned agent | | linked refs | optional task, run, session, or source URL | | `execution` | optional metadata for a Codex/Claude run started from the card (engine, mode, model, session, run id, status) | Cards also carry compact metadata: | Metadata | Values | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | card state | attempts, comments, links, proof, artifacts, automation settings, attachments, worker logs, worker protocol state, claims, diagnostics, notifications, template id, archive state, stale-session detection | | recent events | `created`, `edited`, `moved`, `linked`, `specified`, `decomposed`, `claimed`, `heartbeat`, `execution_updated`, `attempt_started`, `attempt_updated`, `comment_added`, `link_added`, `proof_added`, `artifact_added`, `attachment_added`, `diagnostic`, `notification`, `dispatch`, `orchestration`, `protocol_violation`, `archived`, `unarchived`, `stale` | This metadata lets an operator see how a card moved through the board without opening the linked session. It is local operating context, not a replacement for session transcripts or GitHub issue history. The plugin and Control UI use one Workboard card contract. Control UI refreshes therefore preserve workspace provenance and authority, claim state, diagnostic actions, and notification sequence numbers instead of projecting a smaller UI-only copy of the card. Unknown diagnostic kinds, diagnostic severities, and notification kinds are ignored until both surfaces support them. They are never rewritten into another valid state. The open Workboard tab updates from `plugin.workboard.changed` invalidations. Each event contains only a store epoch and revision. The UI then rereads canonical cards through the normal `operator.read` RPC. Multiple revisions coalesce into one follow-up read. Workboard defers that read while a card is being dragged, edited, or written, then resumes after the local interaction finishes. A reconnect always performs a canonical reload. There is no routine full-card poll, and **Refresh** remains available as manual recovery. The Gateway shares one immutable `workboard.cards.list` payload per normalized board filter at each store revision, including concurrent reads. Card and board writes invalidate it before the next read; the existing change service also invalidates it when it observes a commit from another SQLite connection. Each response still includes all board summaries, so a change to another board invalidates the payload too. Claim tokens remain redacted, and the RPC response shape is unchanged. When more than one board exists, the toolbar includes a **Board** filter backed by persisted board metadata rather than only the currently visible cards. Empty and archived boards therefore remain selectable. Cards without an explicit board id belong to the canonical `default` board. Each board has a canonical `/workboard/<boardId>` page that can be bookmarked, shared, or pinned in the sidebar. The previously shipped `/workboard?board=<boardId>` form remains a compatibility alias and redirects to that page while preserving other query parameters. Choosing **All boards** returns to `/workboard`. A board can store an `automationJobId` reference to the automation job that owns its AI-categorization prompt, model, schedule, and run history. The board page shows an **Automation** link when that reference is present. Matching session events nudge the attached automation through the active Workboard service's scheduler authority, including after the worker's tool authority closes, with events for the same board coalesced for 60 seconds. The automation's schedule remains the backstop. Disabled and auto-disabled automations are never nudged. Deleting the board does not delete or otherwise mutate the operator-owned automation job. Cards are stored in the plugin's own Gateway state and move with the rest of that Gateway's OpenClaw state (see [Storage](#storage)). ## Starting work from a card Unlinked cards without an active or unresolved task association can start work directly: - **Run Claude** / **Run OpenAI** starts a task-tracked agent run with an explicit engine, sends the card prompt, and marks the card `running`. Claude runs use `anthropic/claude-sonnet-4-6`. OpenAI runs use `openai/gpt-6-astra`. - **Open Claude** / **Open OpenAI** creates a linked Control UI session without sending the card prompt, for manual work that stays attached to the board. Opening it clears any schedule and moves a `scheduled` card to `todo`. Other cards keep their status. Autonomous starts use the Gateway's task-tracked agent run path (default agent and model unless Claude/OpenAI is chosen explicitly). Workboard then links the resulting run id and session key back onto the card. Each linked execution also records an attempt summary (engine, mode, model, run id, timestamps, status, rolling failure count) so repeated failures stay visible. The Control UI reads lifecycle from the card's linked session. Card status changes are persisted by the Gateway-side Workboard plugin using the linked run and session lifecycle (see [Session lifecycle sync](#session-lifecycle-sync)). ## Agent tools | Tool | Purpose | | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `workboard_list` | List compact cards with claim/diagnostic state; optional board filter. | | `workboard_read` | Return one card plus bounded worker context (notes, attempts, comments, links, proof, artifacts, parent results, recent assignee work, active diagnostics). | | `workboard_create` | Create a card with optional parents, tenant, skills, board, workspace metadata, idempotency key, runtime limit, retry budget. | | `workboard_link` | Link a parent to a child card. Children stay `todo` until every parent reaches `done`, then dispatch promotion moves them to `ready`. | | `workboard_claim` | Claim a card for the calling agent; moves `backlog`/`todo`/`ready` into `running`. | | `workboard_heartbeat` | Refresh the claim heartbeat during a longer run. | | `workboard_release` | Release the claim after completion, pause, or handoff; can move the card to a next status. | | `workboard_complete` / `workboard_block` | Structured lifecycle tools for final summaries, proof, artifacts, and created-card manifests (must reference cards linked back to the completed card) or blocker reasons. | | `workboard_attachment_add` / `workboard_attachment_read` / `workboard_attachment_delete` | Store small card attachments in plugin SQLite state, index on the card, expose in worker context. | | `workboard_worker_log` / `workboard_protocol_violation` | Record worker log lines and block a card when an automated worker stops without calling `workboard_complete`/`workboard_block`. | | `workboard_board_create` / `workboard_board_archive` / `workboard_board_delete` | Manage persisted board metadata (display name, description, archive state, default workspace). | | `workboard_sessions_board_read` / `workboard_sessions_board_update` / `workboard_sessions_board_move` | Read Sessions board placements, edit columns, rules, or scope, or pin a session in a column. | | `workboard_runs` | Return the persisted run-attempt history for a card. | | `workboard_specify` | Turn a rough triage/backlog card into a clarified `todo` card; records the spec summary on the card. | | `workboard_decompose` | Fan a parent orchestration card into linked children, inheriting board/tenant metadata; can complete the parent with a created-card manifest. | | `workboard_notify_subscribe` / `workboard_notify_list` / `workboard_notify_events` / `workboard_notify_advance` / `workboard_notify_unsubscribe` | Manage notification subscriptions. Event reads are replay-safe; `advance` moves the durable cursor so callers resume without losing or double-reading completed/failed/stale card events. | | `workboard_boards` / `workboard_stats` | Inspect board namespaces and queue stats. | | `workboard_promote` / `workboard_reassign` / `workboard_reclaim` | Recover or hand off stuck work. | | `workboard_comment` / `workboard_proof` | Add handoff notes or attach proof/artifact references. | | `workboard_unblock` | Move blocked work back to `todo`. | | `workboard_move` | Move a card to another status; claimed cards require the caller's agent claim scope. | | `workboard_dispatch` | Nudge dependency promotion or stale-claim cleanup without launching workers; worker launch uses Gateway or slash-command dispatch. | Proof statuses are worker-reported outcomes, not independent verification. A `passed` entry means the worker reports that its command or check succeeded. Consumers that need an independent quality gate should inspect the attached command, URL, or artifact and run their own verifier. `workboard_proof` returns the new record's `proofId`. When `workboard_complete` reports that same proof's terminal status, pass `proofId` so the pending record is resolved in place without losing its identity or timestamp. A proof that already has the same terminal status is reused unchanged. Completion proof without `proofId` remains append-only, so a later retry cannot rewrite older history merely because its command or note is identical. Claimed cards reject agent-tool mutations from other agents unless the caller holds the claim token returned by `workboard_claim`. Every card returned by an agent tool or Gateway RPC call redacts `metadata.claim.token` to `[redacted]` (the token itself is returned once, top-level, only from `workboard_claim`), so Control UI operators and other agents can inspect claim state without ever seeing a usable token. Recovery goes through `workboard_promote`/`workboard_reassign`/`workboard_reclaim`, which do not require the token. ## Dispatch Dispatch is Gateway-local: it does not spawn arbitrary OS processes. Normal OpenClaw subagent sessions still own execution. One dispatch pass: 1. Promotes dependency-ready cards. 2. Blocks expired claims or timed-out runs. 3. Marks board-configured triage cards as orchestration candidates. 4. Claims a small batch of ready cards and starts worker runs through the Gateway subagent runtime. Idle scans leave ready-card history unchanged. Existing dispatch counters and timestamps remain as historical values; new launches use the card's launch, attempt, and execution history. Workers get bounded card context plus the claim token needed to heartbeat, complete, or block the card through the Workboard tools. Workspace paths follow the caller's existing filesystem authority: - Gateway clients with `operator.write` can use configured agent workspaces. - `operator.admin` clients can use other host checkouts. - Sandboxed agent tools use their sandbox workspace access. - Unsandboxed workspace-only tools use their configured workspace root. Workboard records that authority when a workspace is assigned and intersects it with the current caller's authority again at dispatch, so a persisted card cannot widen a later caller's access. Older cards with an explicit host workspace but no recorded authority must have that workspace re-saved before a full-host dispatch. Cards without a host path adopt the current caller's authority when first dispatched. Workspace-bound dispatch accepts a directory or Git checkout only when its repository root exactly matches the target agent workspace. A worktree request is narrowed to that directory and persisted as a directory workspace, so the host does not materialize the checkout or execute repository setup code. The target worker must use a writable, non-shared Docker sandbox for that exact workspace, without elevated execution, persisted host/node exec overrides, or unclassified plugin and MCP tools. Workboard enumerates its registered tools instead of trusting a `workboard_*` prefix, and dispatch refuses a hot Docker container whose live mount/config hash is stale. Dispatch reports the incompatible target policy instead of starting a less-confined worker. Full-host dispatch may target other local checkouts and keeps normal managed- worktree setup. Workspace authority does not create a second card-lifecycle permission model. Callers that may mutate Workboard cards can manually move them through the same statuses on every surface. Read-only workspace access only prevents worker dispatch that needs writes. ### Worker selection Each pass starts **at most 3 workers by default**. Ready cards are ordered by priority, then position, then creation time. A pass starts only one card per owner/agent and skips owners that already have running or review work on the board. Archived cards, cards with an active claim, and cards not in `ready` status are never selected for worker starts (they can still be affected by the data side of dispatch: stale-claim cleanup, dependency promotion, timeout cleanup). Session keys are deterministic per board/card, so repeated dispatches route back to the same worker lane instead of creating unrelated sessions: - Assigned cards: `agent:<agentId>:subagent:workboard-<boardId>-<cardId>` - Unassigned cards: `subagent:workboard-<boardId>-<cardId>` (Gateway resolves the configured default agent) If a worker cannot be started after a card is claimed, Workboard blocks the card, clears the claim, records the run-start failure, and appends a worker log line - visible in the Control UI, CLI JSON, agent tools, and card diagnostics. ### Entry points - Control UI dispatch action - `openclaw workboard dispatch` - `/workboard dispatch` on a command-capable channel All three use the Gateway subagent runtime when the Gateway is available. The CLI has one operator fallback: if the Gateway call fails with a connection/unavailable error (or an `unknown method` error for older Gateways), and no explicit `--url`/`--token` target and no configured remote Gateway (`OPENCLAW_GATEWAY_URL` or `gateway.mode: remote`) apply, the CLI runs data-only dispatch against local SQLite state - it can promote dependencies, clean stale claims, and block timed-out runs, but cannot start workers. Auth, permission, and validation failures from a reachable Gateway are not treated as unavailable. They surface as command errors, and so does any Gateway failure when an explicit `--url`/`--token` target was given. Board metadata can set `autoDecompose`, `autoDecomposePerDispatch`, `defaultAssignee`, and `orchestratorProfile`. OpenClaw records this intent and exposes it in worker context. Actual specification/decomposition still runs through the normal Workboard tools. ## CLI and slash command ```bash openclaw workboard list [--board <id>] [--status <status>] [--include-archived] [--json] openclaw workboard create "Fix stale card lifecycle" --priority high --labels bug,workboard openclaw workboard show <card-id> [--json] openclaw workboard move <card-id> --status <status> [--json] openclaw workboard dispatch [--board <id>] [--json] ``` `list` text output hides archived cards by default (`--include-archived` overrides). `--json` always includes archived cards, matching the full-card contract used by existing scripts. `show` and `move` accept an unambiguous id prefix. `list`, `create`, `show`, and `move` always read/write local plugin state directly. Only `dispatch` calls the running Gateway, with the fallback described above. See [Workboard CLI](/cli/workboard) for full flags, JSON output, Gateway fallback behavior, id-prefix handling, dispatch selection rules, and troubleshooting. `/workboard list`, `/workboard show <card-id>`, `/workboard create <title>`, `/workboard move <card-id> --status <status>`, and `/workboard dispatch` mirror the CLI. List and show are read operations for any authorized command sender. Create, move, and dispatch require owner status on chat surfaces, or a Gateway client with `operator.write`/`operator.admin`. Manual operator moves use the same claim-override behavior as Control UI drag-and-drop. Their worktree access still follows the same workspace boundary described above. ## Session lifecycle sync Cards can link to an existing Control UI session, or one created when you start work from the card. Linked cards show the session lifecycle inline: running, stale, linked idle, done, or failed. You can also capture an existing session from its header or the Sessions tab with **Add to Workboard**. The card links to that session, uses the session label or recent user prompt as title, and seeds notes from the recent user prompt plus the latest assistant response when available. Capture keeps the destination board selected when you start the action. It reloads an inactive board's cards before reusing one, restores an archived exact match, and never infers session ownership from a provisional link. Captured sessions show **Open Workboard card** and a linked-card chip. Both use the same card state, including after a browser reload. Opening a card outside the current agent filter switches the board to **All agents** without changing the selected chat agent. Opening a card loads its linked session details independently of the sidebar's agent filter and pagination. While details are loading or unavailable, the card keeps its link and shows **Session state unknown** or **Session unavailable**. An ambiguous provisional link shows **Session link ambiguous**. Edit the card to select an exact session. Use **Refresh** to retry. To continue an existing session, select its exact link in **Edit card** and choose **Open session**. To start fresh, clear the link in **Edit card**. Clearing the link retains its task association, so **Start** remains unavailable while that task is active or unresolved. Changing a card's assignee does not change the owner of its existing session. Bare `global` and `unknown` links do not identify a session owner. They show **Session link ambiguous** when opened. Use **Edit card** to select an explicit session. Workboard does not offer these bare links for new captures or links. If an active linked session stops reporting recent activity, Workboard marks the card `stale` and stores that as metadata until the lifecycle clears it. Lifecycle writes are owned by the Gateway-side Workboard plugin, so they do not depend on an open browser tab. Agent and subagent completion hooks persist terminal outcomes immediately. A bounded session sweep runs once per minute to reconcile active, idle, missing, and stale session state. Each store mutation emits the normal `plugin.workboard.changed` invalidation, so an open Workboard tab reloads the canonical card instead of writing its own lifecycle projection. While a card is in an active work state, Workboard follows the linked session: | Linked session state | Card status | | ------------------------------------- | ----------- | | active | `running` | | completed | `review` | | failed, killed, timed out, or aborted | `blocked` | **Manual review states win.** Moving a card to `review`, `blocked`, or `done` stops auto-sync for that card until you move it back to `todo` or `running`. Starting a card uses normal Gateway sessions. Workboard only stores card metadata and links. Conversation transcript, model selection, and run lifecycle stay owned by the regular session system. Use **Stop** on a live linked card to abort the active run - Workboard marks that card `blocked` so it stays visible for follow-up. New cards can start from Workboard templates (`bugfix`, `docs`, `release`, `pr_review`, `plugin`). Templates prefill title, notes, labels, and priority. The template id is stored as card metadata. <a id="dashboard-workflow" /> ## Control UI workflow 1. Open the Workboard tab in the Control UI. 2. Create a card with a title, notes, priority, labels, optional agent, and optional linked session - or open Sessions and choose **Add to Workboard** for an existing session. 3. Drag the card between columns, or focus its compact status control and use the menu or ArrowLeft/ArrowRight. During a drag, the source card dims and available drop columns gain an outline. 4. Start an unlinked card when it has no active or unresolved task association. 5. Open the linked session from the card while the agent works. 6. Let lifecycle sync move running work into `review`/`blocked`, then manually move the card to `done` when accepted. ### Session-board widgets Workboard ships three native widgets for session dashboards (see [Dashboards](/web/dashboards)). The agent pins them with its `dashboard` tool using `content: { kind: "plugin", pluginKind, props }`, and they render as first-party UI with live data — no sandbox frame or capability grant: - `workboard:card` with `props: { cardId }` shows one card with its status control, priority, and assigned agent. - `workboard:board` with optional `props: { boardId }` shows the full Kanban board with draggable cards and status controls. Without `boardId` it shows every board. With `boardId` it shows only that board. - `workboard:mini` with optional `props: { boardId, limit }` shows per-status counts plus the top ready/running cards, and links to the full board page. Without `boardId` it aggregates every board. With `boardId` it scopes to that board (cards created without an explicit board id live on `default`). ## Diagnostics Diagnostics are computed from local card metadata. Built-in checks flag: | Kind | Condition | | --------------------------- | ------------------------------------------------------------------------------ | | `stranded_ready` | Assigned `todo`/`backlog`/`ready` card not updated in over 1 hour. | | `running_without_heartbeat` | `running` card with no claim heartbeat or execution update in over 20 minutes. | | `blocked_too_long` | `blocked` card not updated in over 24 hours. | | `repeated_failures` | Card's tracked failure count reaches 2 or more. | | `missing_proof` | `done` card with no proof, artifacts, or attachments. | | `orphaned_session` | `running` card with a `sessionKey` but no `execution` metadata. | | `archived_but_active` | Archived card remains in any non-`done` lifecycle status. | ## Permissions Gateway RPC methods live under `workboard.*`. Sessions board methods use the same read/write scopes as boards: | Scope | Methods | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `operator.read` | `cards.list`, `cards.export`, `cards.diagnostics`, attachment list/get, notification event reads, `boards.list`, `cards.stats`, `cards.runs` | | `operator.read` | `sessionsBoard.read` | | `operator.write` | `cards.diagnostics.refresh`, create/captureSession/update/move/delete/comment/link/linkDependency/proof/artifact, attachment add/delete, worker log, protocol violation, claim/heartbeat/release/promote/reassign/reclaim/complete/block/unblock/start, `cards.dispatch`, `cards.bulk`, archive, `boards.upsert`/`archive`/`delete`, `cards.specify`/`decompose`, notification subscribe/delete/advance | | `operator.write` | `sessionsBoard.update`, `sessionsBoard.move` | `workboard.cards.update`, `workboard.cards.move`, `workboard.cards.archive`, and `workboard.cards.delete` accept an optional `expectedUpdatedAt` request field. Pass the finite numeric `updatedAt` from the card you read to guard the write. If the card has changed, the request fails with `workboard_conflict` and returns its latest card in `error.details.card` (`error.details.type` is `workboard_card_conflict`). Review that card before retrying. Omitting the field keeps the method's existing unguarded request behavior. Control UI bulk actions use each card's observed revision and stop on a conflict. Remaining cards stay selected for review and retry; the batch does not silently retry against newer revisions or overwrite another client's changes. No RPC method requires `operator.admin`. Browsers connected with read-only operator access can inspect the board but cannot mutate cards. An admin scope widens accepted Workboard host paths. It does not change the methods available. ## Storage Workboard stores durable data in a plugin-owned relational SQLite database under the OpenClaw state directory: boards, cards, labels, lifecycle events, run attempts, comments, dependency links, proof, artifact references, attachment metadata and blobs, diagnostics, notifications, worker logs, protocol state, and subscriptions all live in Workboard tables (not plugin key-value entries). A card export preserves the board narrative without inlining attachment blob contents. Sessions boards add optional `kind` and `sessions_spec` columns to `workboard_boards`, plus `workboard_session_placements` for operator pins. Existing board rows are not backfilled; an absent kind still means a Cards board. Rolling back to a release without Sessions boards shows each Sessions board as an empty Cards board and ignores the placement table; cards created against it there are rejected once the newer release runs again. Session transcripts and the Board agent conversation remain in the normal session store. SQLite opening, queries, and transactions run in a background database worker. Disabling or reloading the plugin drains admitted storage work before closing its connections. Installations with retained pre-July 2026 Workboard plugin-state KV data must upgrade through OpenClaw `2026.9.7` and run `openclaw doctor --fix` before upgrading to the latest version. Current Doctor reports this requirement without changing the legacy rows; current relational SQLite stores remain supported. ## Troubleshooting **The tab says Workboard is unavailable** ```bash openclaw plugins inspect workboard --runtime --json ``` If `plugins.allow` is configured, add `workboard` to it. If `plugins.deny` contains `workboard`, remove it before enabling the plugin. **Cards do not save** Confirm the browser connection has `operator.write` access. Read-only operator sessions can list cards but cannot create, edit, move, or delete them. **Starting a card does not open the expected session** Check the card's agent id and linked session, then open Sessions or Chat to inspect the actual run state. **Dispatch does not start a worker** Confirm there is at least one `ready` card without an active claim: ```bash openclaw workboard list --status ready ``` If the CLI reports data-only dispatch, start or restart the Gateway and retry - data-only dispatch updates local board state but cannot start subagent worker runs. Cards can also be skipped when another card for the same owner or agent is already running or waiting for review. Complete, block, or release that active work before dispatching more for the same owner. ## Related - [Control UI](/web/control-ui) - [Workboard CLI](/cli/workboard) - [Plugins](/tools/plugin) - [Manage plugins](/plugins/manage-plugins) - [Sessions](/concepts/session) - [Managed worktrees](/concepts/managed-worktrees)