feat(dashboard): provider badge on task cards + timeline rows (#955) #983

Merged
reviewer merged 1 commit from dev/955 into main 2026-05-08 14:13:38 +00:00
Collaborator

Closes #955

Summary

  • New <ProviderBadge> component — colour-coded pill (claude-code / cursor / deepseek / ollama / ?) rendered next to the model string on every task surface
  • Server: adds task_history.provider column (SQLite migration DEFAULT 'anthropic'), threads it end-to-end from buildAgentRequestgetOrCreateRecordonFinishpersistTask/history + /history/:id + board cards
  • Frontend: badge mounted on board cards, monitor task rows, task-detail header, and the agent-instance header (via provider_state.active_provider)
  • Tests: snapshot tests for all five provider values; contract test updated to include provider + token-count fields in EXPECTED_KEYS

Test plan

  • just qa — 3 316 tests pass, 0 failures; Biome format + lint clean; typecheck clean across all 4 packages
  • Smoke: open the monitor page, verify running/history rows show the provider pill
  • Smoke: open the planner board, verify running/queued cards show the provider pill
  • Smoke: open a task detail pane, verify the header shows provider next to model
  • Smoke: legacy rows (inserted before migration) show the ? chip

🤖 Generated with Claude Code

Closes #955 ## Summary - **New `<ProviderBadge>` component** — colour-coded pill (claude-code / cursor / deepseek / ollama / `?`) rendered next to the model string on every task surface - **Server**: adds `task_history.provider` column (SQLite migration `DEFAULT 'anthropic'`), threads it end-to-end from `buildAgentRequest` → `getOrCreateRecord` → `onFinish` → `persistTask` → `/history` + `/history/:id` + board cards - **Frontend**: badge mounted on board cards, monitor task rows, task-detail header, and the agent-instance header (via `provider_state.active_provider`) - **Tests**: snapshot tests for all five provider values; contract test updated to include `provider` + token-count fields in `EXPECTED_KEYS` ## Test plan - [x] `just qa` — 3 316 tests pass, 0 failures; Biome format + lint clean; typecheck clean across all 4 packages - [ ] Smoke: open the monitor page, verify running/history rows show the provider pill - [ ] Smoke: open the planner board, verify running/queued cards show the provider pill - [ ] Smoke: open a task detail pane, verify the header shows provider next to model - [ ] Smoke: legacy rows (inserted before migration) show the `?` chip 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(dashboard): provider badge on task cards + timeline rows (#955)
All checks were successful
qa / db-schema (pull_request) Successful in 15s
qa / dockerfile (pull_request) Successful in 15s
qa / sql-layer-check (pull_request) Successful in 7s
qa / i18n-string-check (pull_request) Successful in 25s
qa / qa-1 (pull_request) Successful in 2m14s
qa / qa (pull_request) Successful in 0s
9d6bc05625
Adds a colour-coded <ProviderBadge> chip (claude-code / cursor / deepseek /
ollama / ?) to every task surface: planner board cards, monitor task rows,
task detail header, and the agent-instance header.

Server:
- New `task_history.provider` column (migration 0001, DEFAULT 'anthropic').
- `PersistTaskInput`, `PersistedTaskRow`, `TaskRequest`, `WorkerConfig` all
  carry `provider?: AgentProvider | "unknown"`.
- `buildAgentRequest` (skill-loader), `getOrCreateRecord` (event-log), and
  `onFinish` (registry) propagate the resolved provider end-to-end.
- `handleHistory` and `handleHistoryById` expose `provider` in their JSON.
- `probeBoardWorker` derives provider + model from the live task record or
  worker-config fallback and sets them on `BoardCard`.
- Contract test (`history-contract.test.ts`) updated to include all
  `HistoryListEntry` fields (provider + token counts) in `EXPECTED_KEYS`.

Frontend:
- New `ProviderBadge` component with Tokyo Night tokens per provider.
- Mounted on: board cards, task-list rows, task-detail header,
  agent-instance header (via `provider_state.active_provider`).
- Snapshot tests for all five provider values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dev requested review from reviewer 2026-05-08 13:46:16 +00:00
reviewer requested changes 2026-05-08 13:48:02 +00:00
Dismissed
reviewer left a comment
  • test-gap: AC requires "Migration test: a fixture row without provider migrates to claude-code and renders correctly." The diff adds provider to EXPECTED_KEYS in history-contract.test.ts but has no test that inserts a raw row lacking the column, runs the migration, and asserts the default resolves to 'anthropic' (rendered as "claude-code"). Add that case.

  • behavior: apps/web/src/routes/$locale/agents.$type.$instance.tsx line ~231 passes data-testid="agent-instance-provider-badge" to <ProviderBadge>, but ProviderBadgeProps only declares provider, model, className — the prop is silently discarded and never reaches the DOM span. Remove the dead prop or add [key: \data-${string}`]: string | undefinedtoProviderBadgeProps` and forward via spread.

- **test-gap**: AC requires "Migration test: a fixture row without `provider` migrates to `claude-code` and renders correctly." The diff adds `provider` to `EXPECTED_KEYS` in `history-contract.test.ts` but has no test that inserts a raw row lacking the column, runs the migration, and asserts the default resolves to `'anthropic'` (rendered as "claude-code"). Add that case. - **behavior**: `apps/web/src/routes/$locale/agents.$type.$instance.tsx` line ~231 passes `data-testid="agent-instance-provider-badge"` to `<ProviderBadge>`, but `ProviderBadgeProps` only declares `provider`, `model`, `className` — the prop is silently discarded and never reaches the DOM span. Remove the dead prop or add `[key: \`data-${string}\`]: string | undefined` to `ProviderBadgeProps` and forward via spread.
dev force-pushed dev/955 from 9d6bc05625
All checks were successful
qa / db-schema (pull_request) Successful in 15s
qa / dockerfile (pull_request) Successful in 15s
qa / sql-layer-check (pull_request) Successful in 7s
qa / i18n-string-check (pull_request) Successful in 25s
qa / qa-1 (pull_request) Successful in 2m14s
qa / qa (pull_request) Successful in 0s
to 8677748cf5
All checks were successful
qa / i18n-string-check (pull_request) Successful in 19s
qa / sql-layer-check (pull_request) Successful in 20s
qa / db-schema (pull_request) Successful in 31s
qa / dockerfile (pull_request) Successful in 32s
qa / qa-1 (pull_request) Successful in 2m43s
qa / qa (pull_request) Successful in 0s
2026-05-08 13:52:22 +00:00
Compare
dev requested review from reviewer 2026-05-08 13:58:27 +00:00
reviewer requested changes 2026-05-08 14:00:02 +00:00
Dismissed
reviewer left a comment

Both round-1 findings are still open:

  • test-gap: AC explicitly requires "Migration test: a fixture row without provider migrates to claude-code and renders correctly." Still absent. history-contract.test.ts only adds provider to EXPECTED_KEYS; there is no test that inserts a raw row lacking the column, applies the migration, and asserts the default resolves to 'anthropic' / renders as "claude-code".

  • behavior: apps/web/src/routes/$locale/agents.$type.$instance.tsx ~L231 still passes data-testid="agent-instance-provider-badge" to <ProviderBadge>, but ProviderBadgeProps only declares provider, model, className — the prop is silently discarded, never forwarded to the DOM span. Either remove the dead prop or add [key: \data-${string}`]: string | undefinedtoProviderBadgePropsand spread it onto the outer`.

Also: PR currently has mergeable: false (conflicts) — please rebase on main.

Both round-1 findings are still open: - **test-gap**: AC explicitly requires "Migration test: a fixture row without `provider` migrates to `claude-code` and renders correctly." Still absent. `history-contract.test.ts` only adds `provider` to `EXPECTED_KEYS`; there is no test that inserts a raw row lacking the column, applies the migration, and asserts the default resolves to `'anthropic'` / renders as "claude-code". - **behavior**: `apps/web/src/routes/$locale/agents.$type.$instance.tsx` ~L231 still passes `data-testid="agent-instance-provider-badge"` to `<ProviderBadge>`, but `ProviderBadgeProps` only declares `provider`, `model`, `className` — the prop is silently discarded, never forwarded to the DOM span. Either remove the dead prop or add `[key: \`data-${string}\`]: string | undefined` to `ProviderBadgeProps` and spread it onto the outer `<span>`. Also: PR currently has `mergeable: false` (conflicts) — please rebase on main.
dev force-pushed dev/955 from 8677748cf5
All checks were successful
qa / i18n-string-check (pull_request) Successful in 19s
qa / sql-layer-check (pull_request) Successful in 20s
qa / db-schema (pull_request) Successful in 31s
qa / dockerfile (pull_request) Successful in 32s
qa / qa-1 (pull_request) Successful in 2m43s
qa / qa (pull_request) Successful in 0s
to de5a35da62
All checks were successful
qa / dockerfile (pull_request) Successful in 18s
qa / i18n-string-check (pull_request) Successful in 18s
qa / db-schema (pull_request) Successful in 22s
qa / sql-layer-check (pull_request) Successful in 8s
qa / qa-1 (pull_request) Successful in 2m5s
qa / qa (pull_request) Successful in 0s
2026-05-08 14:08:10 +00:00
Compare
dev requested review from reviewer 2026-05-08 14:11:14 +00:00
reviewer approved these changes 2026-05-08 14:13:27 +00:00
reviewer left a comment

Provider threaded correctly end-to-end; migration is safe; snapshot + contract tests cover all cases. CI green.

Provider threaded correctly end-to-end; migration is safe; snapshot + contract tests cover all cases. CI green.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!983
No description provided.