From 50d10c792a98438c554f05ab7fe072c3cf6e6c75 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 17 Jun 2026 09:42:54 +0200 Subject: [PATCH] chore(plan): file T-461 (red error border on API-error cards) Flag API-error conversation cards with a red statusError border (the card already has an error status + bordered variant; just needs wiring). UI tracker T-276. Co-Authored-By: Claude Opus 4.8 (1M context) --- .pql/changelog/ticket_history/2026-06.sql | 16 ++++++++++++++++ .pql/changelog/ticket_idmap/2026-06.sql | 1 + .pql/changelog/tickets/2026-06.sql | 17 +++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 57404c8d..f6bb4b59 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -5877,3 +5877,19 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang **Files:** `pubspec.yaml` (fonts:), `assets/fonts/inter/`, `assets/licenses.yaml`, `lib/widgets/src/typography.dart`, `lib/src/shell/root_shell.dart` (DefaultTextStyle), mono consumers (editor / terminal / markdown / code-block), the Appearance settings category (T-452) + schema registration. **Related:** T-444 (epic), T-452 (Appearance category — where these render), T-448 (field renderer), T-449 (scope), ui-design `theme.md` (typography section: clideUiFamily / clideMonoFamily / clideFont* sizes), D-42 (bundled-dep documentation), D-44 (theming).', NULL, '2026-06-17 07:39:55', '2026-06-17 07:39:55.628', '2026-06-17 07:39:55.628', NULL, '316143b93910994fac553f6f515f6645', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD91RWDWZPHFPJ72FHHHWS08', 'description', NULL, 'User report 2026-06-16 (screenshot ~/.cache/clide/pasted/paste-1781681999949.png). An API error renders as a plain conversation card with no error styling: + + clide + API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited + +It should be visually flagged with a **red error border** on the card so it stands out from normal turns. + +**Already supported — just needs wiring.** `lib/builtin/claude/src/conversation_card.dart`: +- `ConversationCardStatus.error` exists (line ~302 → `tokens.statusError`) but today only tints the header status tick, not the border. +- The `bordered` variant draws `Border.all(color: widget.borderColor ?? tokens.panelBorder)` (lines ~61 / ~224) — so the border defaults to the neutral `panelBorder`. + +**Fix:** render the API-error card as `variant: bordered, status: error, borderColor: tokens.statusError` (or, cleaner, have the card derive the border from `status == error → tokens.statusError` so any error card gets the red border without each caller passing it). Wire the producer — the error / system message that carries the "API Error …" text (in `conversation_view.dart` / `transcript_reader.dart`, e.g. the stream error / `SessionEnd.reason` path) — to mark the card as an error. + +**Acceptance:** an API error (rate-limit, server error, etc.) renders a card with a red `statusError` border; normal cards unchanged; a widget test asserts the error card''s border colour. + +**Files:** lib/builtin/claude/src/conversation_card.dart (border from error status), the error/system-message rendering in lib/builtin/claude/src/conversation_view.dart / transcript_reader.dart.', NULL, '2026-06-17 07:41:47', '2026-06-17 07:41:47.267', '2026-06-17 07:41:47.267', NULL, 'aa0330e9165b9b9fc3f1c516abc3a07c', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/ticket_idmap/2026-06.sql b/.pql/changelog/ticket_idmap/2026-06.sql index cfe96f9a..6aaea6ab 100644 --- a/.pql/changelog/ticket_idmap/2026-06.sql +++ b/.pql/changelog/ticket_idmap/2026-06.sql @@ -286,3 +286,4 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD1HK7YKJTEK1WV4VHK0RT8R', 'T-458', '2026-06-16 14:12:04.212', '2026-06-16 14:12:04.212', NULL, '070f1def635ee013df639cb9fe81e357', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash); INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD1JBRABJHX804CPMZJDC444', 'T-459', '2026-06-16 14:15:25.011', '2026-06-16 14:15:25.011', NULL, '44d5db70ef13ead00c4dcc437c387cec', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash); INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD91A7VEW3VCY7QX1END2Z3G', 'T-460', '2026-06-17 07:39:25.019', '2026-06-17 07:39:25.019', NULL, 'e7e749d35dc413a560d3347be5f4a9ee', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash); +INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD91RWDWZPHFPJ72FHHHWS08', 'T-461', '2026-06-17 07:41:24.975', '2026-06-17 07:41:24.975', NULL, '4e3fba7aaad762b7b7cd854459cf664a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash); diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index 805c1f9e..2c2c03f9 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -7080,3 +7080,20 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat **Files:** `pubspec.yaml` (fonts:), `assets/fonts/inter/`, `assets/licenses.yaml`, `lib/widgets/src/typography.dart`, `lib/src/shell/root_shell.dart` (DefaultTextStyle), mono consumers (editor / terminal / markdown / code-block), the Appearance settings category (T-452) + schema registration. **Related:** T-444 (epic), T-452 (Appearance category — where these render), T-448 (field renderer), T-449 (scope), ui-design `theme.md` (typography section: clideUiFamily / clideMonoFamily / clideFont* sizes), D-42 (bundled-dep documentation), D-44 (theming).', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-17 07:39:25.019', '2026-06-17 07:39:55.628', NULL, '0bd5fdb6f8d924a23eb45be2c48bedaf', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD91RWDWZPHFPJ72FHHHWS08', 'bug', '06FB0TNQM5TWC00GW0P3X02HZW', 'API-error cards: flag with a red error border (statusError)', NULL, 'backlog', 'medium', NULL, NULL, NULL, '2026-06-17 07:41:24.975', '2026-06-17 07:41:24.975', NULL, '38eac435cdb43c08db84de486c65a527', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FD91RWDWZPHFPJ72FHHHWS08', 'bug', '06FB0TNQM5TWC00GW0P3X02HZW', 'API-error cards: flag with a red error border (statusError)', 'User report 2026-06-16 (screenshot ~/.cache/clide/pasted/paste-1781681999949.png). An API error renders as a plain conversation card with no error styling: + + clide + API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited + +It should be visually flagged with a **red error border** on the card so it stands out from normal turns. + +**Already supported — just needs wiring.** `lib/builtin/claude/src/conversation_card.dart`: +- `ConversationCardStatus.error` exists (line ~302 → `tokens.statusError`) but today only tints the header status tick, not the border. +- The `bordered` variant draws `Border.all(color: widget.borderColor ?? tokens.panelBorder)` (lines ~61 / ~224) — so the border defaults to the neutral `panelBorder`. + +**Fix:** render the API-error card as `variant: bordered, status: error, borderColor: tokens.statusError` (or, cleaner, have the card derive the border from `status == error → tokens.statusError` so any error card gets the red border without each caller passing it). Wire the producer — the error / system message that carries the "API Error …" text (in `conversation_view.dart` / `transcript_reader.dart`, e.g. the stream error / `SessionEnd.reason` path) — to mark the card as an error. + +**Acceptance:** an API error (rate-limit, server error, etc.) renders a card with a red `statusError` border; normal cards unchanged; a widget test asserts the error card''s border colour. + +**Files:** lib/builtin/claude/src/conversation_card.dart (border from error status), the error/system-message rendering in lib/builtin/claude/src/conversation_view.dart / transcript_reader.dart.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-17 07:41:24.975', '2026-06-17 07:41:47.266', NULL, 'e4c3db66fb4ba42ba08896b97ef3ad89', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);