feat(dashboard): per-instance failover-history drawer (M26-3 follow-up) #555
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!555
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/agents-history-drawer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes the M26-3 follow-up flagged when PR #554's redesign repurposed the drawer slot for the per-type editor — no UI consumer for the
agent_provider_eventsledger that's been live since M26-1.Changes
UI:
Historybutton on each instance row alongsideAdv/Del.InstanceHistoryDrawerslides in from the right (matchesTypeEditorDrawershape: backdrop + Esc + × close). 640 px wide.computeTierIntervalswalks the ledger chronologically, closes the last interval atnow, and bins durations per tier. Empty ledger → single full-width interval atcurrent_tier. Legend underneath: percent + minutes per tier.tier 1 → 2ortier 2for resets), failure kind, optional task id.bg-state-running, tier 2bg-warning, tier 3bg-error. Matches the TierBadge palette.Wire-up:
fetchProviderEvents(name, { since, limit })fromlib/api.ts(M26-3 added this; previously had no consumer).Test plan
just typecheckclean.Historyon a tier-1 healthy agent — bar shows 100% green, list says "no failovers in last 7 days".🤖 Generated with Claude Code
- New `InstanceAdvancedDrawer` mirrors `InstanceHistoryDrawer` / `TypeEditorDrawer` (slide-in animation, leave guard, Esc/backdrop/× closes). Wraps the existing `InstanceAdvancedEdit` body so the prompt-appendix + notes textareas now live in a focused panel instead of an inline expansion row. - Drop the inline-row machinery: `expanded` Set state, `toggleAdv`, the colSpan adv `<tr>`, and the `Fragment` wrapping that needed. Net: ~30 lines lighter. - New `RowActionButton` — square 28×28 icon-only ghost button. Wraps `title` + `aria-label` + `data-testid` for consistency. Variants: default ghost or `tone="error"` (red on hover for destructive). - History → 🕘 (clock). - Adv edit → ✎ (pencil) — distinct from `⚙` used by type-level edit. - Delete → 🗑. - Operator routes the action via tooltip; row stays compact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Foundation primitive — every clickable in the app should now route through `<Button>`. Agents page is the migration template; remaining features sweep in follow-up PRs. Button changes: - Add `iconOnly` modifier (square aspect, sm/md/lg → 28/36/44 px) - Add `loading` prop (Loader2 spinner, disables click, aria-busy) - Add `tone="error"` (red focus ring + hover for destructive actions) - Add `leadingIcon` / `trailingIcon` slots typed as `LucideIcon` - `cursor-pointer` baseline; per-variant focus-visible rings Icon library: lucide-react (~1-2 kB per icon, tree-shakable, MIT). No other icon deps needed. Agents page migration: - RowActionButton primitive removed → `<Button variant="ghost" iconOnly>` - 🕘 ✎ 🗑 → Clock / Pencil / Trash2 (row actions) - ⚙ → Settings (Edit type) - ▶ ▼ → ChevronRight / ChevronDown (collapse) - ⏸ ▶ → Pause / Play (tier badge pause toggle) - ↺ → RotateCcw (reset tier) - × → X (drawer + dialog close, chip remove, env row remove, tier remove) - ⚠ → AlertTriangle (provider chain validation) Tier number glyphs (① ② ③) kept as semantic numbered markers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>The pause button on the row read as "pause running task" — confusing on idle agents (nothing to pause), and pausing flipped the row to a red ✕ + recovery icons that looked like a crash state. Tier column now shows status only: - healthy: tier number + active model (no controls) - degraded (tier > 1): adds the reset button - paused: red "Paused" pill + Resume + Reset (recovery actions only) Pause/disable moves into the Advanced drawer as a labelled toggle ("Dispatch · Enabled/Disabled"). It's a rare ops action — drawer has the room to explain what it actually does (stop accepting new tasks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Three fixes from PR review. 1. Autosave race (data loss): - Mutation snapshots payload at fire-time, not at resolve-time, so edits made between PUT fire and resolve can't be rolled back into `lastSavedRef`. - Bootstrap effect now gates on `inFlightSaveRef` so a cache invalidation triggered by another query (or a refetch) mid-save can't stomp the user's in-progress edits. - Drop the redundant `invalidateQueries(["agent-config"])` on onSuccess — the stale-while-revalidate refetch already brings the server snapshot back; the explicit invalidate just doubled writes. 2. Duplicate testids in DOM: - `InstancesTable` previously rendered both desktop table and mobile stack with `hidden sm:*` classes. Both branches lived in the DOM, so every `agent-row-*`, `agent-inspect-*`, etc. testid duplicated and Playwright strict locators would throw. - Add `useMediaQuery` hook (SSR-safe via useSyncExternalStore) and mount only the active layout. 3. Drawer mutual exclusion: - Three independent state vars (editingTypeName, inspectingInstance, advEditingInstance) let drawers stack on top of each other. - Collapse to a single tagged-union state — opening any drawer closes the other two. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>UX polish from PR review. - Tier column displays status only when not paused. Reset moves into the row's action cluster (next to History / Edit / Delete) so the eye doesn't confuse a Reset button for a status glyph — same trap the user hit with the previous red ✕. - Replace ⛽ emoji with the lucide `Fuel` icon (last emoji left after the icon migration). - Provider summary chip on the type-card header now truncates with ellipsis at `max-w-[55vw]` on mobile / `max-w-[280px]` on sm+, so long provider · model strings don't push the header to a third line on narrow viewports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Three minor fixes: 1. DeleteAgentDialog now uses the new `tone="error"` Button modifier (was raw `border-error text-error` className override). Also wires `loading={mutation.isPending}` so the spinner replaces the text. 2. ProviderSection up/down buttons migrate from raw Unicode arrows to lucide `ArrowUp` / `ArrowDown` via the foundation Button. Closes the last spot where the lucide migration was inconsistent. 3. `model_override` field removed from the GET /agents response shape (was always null), from `AgentsListEntry` on the web side, and from the e2e fixture. PATCH response now forces `model: null` so legacy rows with stale non-null `model` columns don't leak through. The column itself is left in the DB — it's dead but harmless, and a migration to drop it is out of scope. Tests updated to assert `model_override` is undefined on the response. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>