feat(dashboard): Agents UI redesign — recovery + inline edits + drawer #554

Merged
charles merged 9 commits from restore/merged-ui into main 2026-04-29 19:06:49 +00:00
Collaborator

Summary

Two-part PR:

1. Recovery. Restores the merged Agents+Config page that was deployed live but had never been committed (existed only in operator's stash, accidentally dropped during M26 work). Spec: docs/specs/agents-ui-redesign.md. Auth-bridge fix (guardMutating falls back to OAuth session.account_login when Remote-User absent) closes the 403 storm that was masking the broken state.

2. UI redesign. Per the spec, four phases:

  • Phase 1 — drop noise: FleetHealthStrip top tiles, Fleet/Globals/JSON outer-tab carousel, bottom SkillViewer all removed.
  • Phase 2 — single-list type groups: replace AgentsTree rail + TypePane master-detail with one scrolling list of TypeGroupCards. Each card has a header (name + tier-1 chip + edit button + instance count) and an inline-collapsible body containing the InstancesTable. Type editing pulls a slide-in drawer from the right.
  • Phase 3 — accordion sections: SectionPane no longer renders horizontal tabs. Each section is a <details> block in one scrollable column. Provider section open by default (post-M26 most-edited surface).
  • Phase 4 — inline instance edit: Model column → click-to-edit text input. Match-labels column → click-to-edit ChipInput. New ▼ Adv button → expands the row to inline textareas for prompt_appendix + notes. The per-instance edit modal is replaced; create-instance flow keeps the slim modal (name + type).

Net change

~600 lines deleted (AgentsTree, TypePane, InstancePane, jsonText state, OUTER_TAB_CLASS, FleetHealthStrip imports, SkillViewer fn). Page is now: header + Save + scrolling list of type cards. Drawer overlays for type editing. Inline editors for instance overrides.

Commits

# What
1 Recovery + auth bridge
2 Spec doc
3 Phase 1 — drop noise
4 Phase 2 — single-list + drawer
5 Phase 3 — accordion sections
6 Phase 3 lint fix
7 Phase 4 — inline instance edit
8 Phase 4 lint fix
9 Phase 4 follow-up — inline Adv expansion

Test plan

  • just typecheck clean across all 4 packages.
  • Web tests: 588 pass / 24 pre-existing fail (theme + selected-repos localStorage shim — present on main).
  • Server tests: 4 pre-existing fail (session JSONL pruning + foreman session CRUD — present on main).
  • Manual: hard-refresh, edit a model inline, edit a match-label inline, expand Adv to set a prompt appendix, save persists.
  • Manual: open type editor drawer, expand the Provider section, edit a chain, save propagates back to agents.json.

🤖 Generated with Claude Code

## Summary Two-part PR: **1. Recovery.** Restores the merged Agents+Config page that was deployed live but had never been committed (existed only in operator's stash, accidentally dropped during M26 work). Spec: `docs/specs/agents-ui-redesign.md`. Auth-bridge fix (`guardMutating` falls back to OAuth session.account_login when Remote-User absent) closes the 403 storm that was masking the broken state. **2. UI redesign.** Per the spec, four phases: - **Phase 1** — drop noise: FleetHealthStrip top tiles, Fleet/Globals/JSON outer-tab carousel, bottom SkillViewer all removed. - **Phase 2** — single-list type groups: replace `AgentsTree` rail + `TypePane` master-detail with one scrolling list of `TypeGroupCard`s. Each card has a header (name + tier-1 chip + edit button + instance count) and an inline-collapsible body containing the InstancesTable. Type editing pulls a slide-in drawer from the right. - **Phase 3** — accordion sections: `SectionPane` no longer renders horizontal tabs. Each section is a `<details>` block in one scrollable column. Provider section open by default (post-M26 most-edited surface). - **Phase 4** — inline instance edit: Model column → click-to-edit text input. Match-labels column → click-to-edit ChipInput. New `▼ Adv` button → expands the row to inline textareas for prompt_appendix + notes. The per-instance edit modal is replaced; create-instance flow keeps the slim modal (name + type). ## Net change ~600 lines deleted (AgentsTree, TypePane, InstancePane, jsonText state, OUTER_TAB_CLASS, FleetHealthStrip imports, SkillViewer fn). Page is now: header + Save + scrolling list of type cards. Drawer overlays for type editing. Inline editors for instance overrides. ## Commits | # | What | |---|---| | 1 | Recovery + auth bridge | | 2 | Spec doc | | 3 | Phase 1 — drop noise | | 4 | Phase 2 — single-list + drawer | | 5 | Phase 3 — accordion sections | | 6 | Phase 3 lint fix | | 7 | Phase 4 — inline instance edit | | 8 | Phase 4 lint fix | | 9 | Phase 4 follow-up — inline Adv expansion | ## Test plan - [x] `just typecheck` clean across all 4 packages. - [x] Web tests: 588 pass / 24 pre-existing fail (theme + selected-repos localStorage shim — present on main). - [x] Server tests: 4 pre-existing fail (session JSONL pruning + foreman session CRUD — present on main). - [ ] Manual: hard-refresh, edit a model inline, edit a match-label inline, expand `Adv` to set a prompt appendix, save persists. - [ ] Manual: open type editor drawer, expand the Provider section, edit a chain, save propagates back to `agents.json`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Recovers UI work that was deployed live before today's M26 work but had
never been committed (existed only in operator's stash). M26 features
ported into the recovered architecture:

UI architecture (recovered from pre-M26 stash):
- `routes/config.tsx` removed; content lives at
  `apps/web/src/features/agents/sections.tsx` and is hosted by
  `routes/agents.tsx`. The dashboard's top-nav now has a single Agents
  entry instead of separate Agents + Config.
- TypeRail + SectionPane drive per-type editing with section tabs
  (Identity / Prompt / Routing / Skills / Thresholds / Provider /
  Container). Per-instance overrides edited inline on the same page.

M26 features ported into the merged page:
- New "Provider" section tab exposes the M26-2 chain editor (1–3 tiers,
  ↑/↓ reorder, +/× add/remove, validation banner) plus M26-5 token
  budget input + M26-1 failover policy block.
- ProviderSection consumes the existing `ModelCombobox` from
  `sections.tsx` (catalogue passed via `modelsByProvider` prop) so
  per-tier model pickers autocomplete from `/agents/models?provider=`.
- InstancesTable gains a Tier column with the M26-3 TierBadge:
  tier glyph, active model, cooldown countdown (30s setInterval),
  reset/pause/unpause buttons, M26-7  icon for `token_budget`.

Auth fix:
- `guardMutating` now falls back to `c.get("session")?.account_login`
  when the legacy Remote-User header is absent. OAuth-only deployments
  (no Authelia in front) were 403'ing every guarded GET like
  `/config/agents`. Both auth surfaces remain valid; header takes
  priority for legacy proxy deployments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per `docs/specs/agents-ui-redesign.md` Phase 1:

- Drop FleetHealthStrip from the page top. Saturation / Queue / Last
  Activity already live on /stats; on the Agents page they were just
  vertical noise above the actual editing surface.
- Drop the Fleet / Globals / JSON outer-tab carousel. Single fleet view
  remains. Globals (pipeline / watchdogs) live on `config/service.json`
  per the post-#540 split — out of scope for the Agents page. JSON
  textarea returns later in Phase 1.5 as a `[</>]` icon → modal so the
  form remains the source of truth.
- Drop the bottom SkillViewer. Read-only docs of skill markdown was
  page-bottom dead weight; future home is a dedicated /skills route or
  inline help on the Skills section tab.
- Strip dead code: `Tabs`, `FleetHealthStrip`, `WatchdogPanel`,
  `fetchSkills` imports; `currentTab` / `setTab` / `jsonText` /
  `jsonError` state; the JSON-tab branches inside `setTab` and
  `save()`; `OUTER_TAB_CLASS`; the `tab` URL search param.

Net result: page header + save button + the type/instance editing
surface. Three less things on screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per `docs/specs/agents-ui-redesign.md` Phase 2/3:

- Replace the master-detail (`AgentsTree` rail + `TypePane` right pane)
  with a single scrolling list of `TypeGroupCard`s. One card per agent
  type: header carries the name, a tier-1 summary chip
  (`① <provider> · <model>`), instance count, host badge for foreman,
  and `[⚙ Edit type]` button.
- Card body collapses on header click; renders the `InstancesTable`
  inline for non-host types (including the M26-3 TierBadge column).
- New `TypeEditorDrawer` slides in from the right (per
  `apps/web/CLAUDE.md` shadow-lifted drawer pattern), wraps the
  existing `SectionPane`, closes on Esc / backdrop click / × button.
  Section tab carousel inside the drawer is preserved for now —
  Phase 3 collapses it into `<details>` accordion.
- Drop the `agents-no-selection` empty state (no longer applicable —
  list is always present).
- `+ Add agent type` button moves below the list as a ghost button.

Net visible change: scroll instead of click-into-rail. All types
visible at once; editing pulls a focused drawer rather than swapping
the whole right pane.

`AgentsTree` + `TypePane` left in source for now; flagged DEPRECATED
in their headers. Cleanup in Phase 4 when inline instance editing
removes the modal `AgentEditor` path that still calls them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per `docs/specs/agents-ui-redesign.md` Phase 3:

- `SectionPane` no longer renders a horizontal tab strip. Each section
  is a `<details>` element in a single scrollable column.
- Provider section open by default (most-edited surface after M26).
  All other sections collapsed; `section` prop deep-link seeds the
  initial open set so URL-routed paths still work.
- Local open-set state owns toggles after first render; clicking a
  collapsed summary fires `onSectionChange` so the URL stays in sync.

Drops the tab-strip carousel entirely. The 7 sections (Identity,
Prompt, Routing, Skills, Thresholds, Provider, Container) all live on
one page, expandable independently.

Tests:
- "active section tab aria-selected true" → "section pinned by URL
  renders open" (asserts `<details>.open` instead of `aria-selected`).
- "switching section tabs preserves selected type" — click target
  changes from `config-section-prompt` to the new
  `config-section-prompt-summary` testid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per `docs/specs/agents-ui-redesign.md` Phase 4:

- New `InlineModelEdit` component on the Model column. Click → text
  input, blur or Enter commits via `patchAgent`, Escape reverts.
  Empty value clears the override (inherits the type's default model).
- New `InlineLabelsEdit` component on the Match labels column.
  Click → ChipInput seeded with current labels + repo-label suggestions;
  blur outside the component commits via `patchAgent`. No-op on
  unchanged values.
- Edit button relabelled `Adv` (advanced) — kept for prompt_appendix /
  notes editing. The full modal is no longer the primary surface.
- New `patchAgentMutation` at the route level wires the inline edits
  back to the server; `agents` query is invalidated on success.

Drops dead components left behind by the Phase 2 list refactor:
- `AgentsTree` (left rail) — replaced by single-list TypeGroupCard.
- `TypePane` (right pane) — replaced by TypeEditorDrawer.
- `InstancePane` (right pane for selected instance) — never rendered
  after Phase 2; Adv button + inline edits cover the live editing.

Net: ~400 lines removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(dashboard): replace per-instance edit modal with inline Adv expansion
Some checks failed
qa / qa (pull_request) Failing after 13m44s
qa / dockerfile (pull_request) Successful in 28s
cdfa364127
Per `docs/specs/agents-ui-redesign.md` Phase 4 follow-up:

The `Adv` button no longer opens the AgentEditor modal. Click → row
expands to show two textareas inline (prompt_appendix + notes), each
committing on blur via `patchAgent`. Empty value clears the override.

Visual cue: button label flips between `▼ Adv` (collapsed) and
`▲ Adv` (expanded), `aria-expanded` set for assistive tech.

`AgentEditor` modal kept only for the create-instance flow (`+ Instance`
button). `editingName` / `openEditInstance` paths dropped — every per-
instance field now has an inline editor:
- Model: `InlineModelEdit` text input.
- Match labels: `InlineLabelsEdit` ChipInput.
- Prompt appendix + Notes: `InstanceAdvancedEdit` textareas under the
  expanded row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test(auth): update Remote-User absence assertions for OAuth-session bridge
All checks were successful
qa / qa (pull_request) Successful in 14m26s
qa / dockerfile (pull_request) Successful in 29s
9017c2c426
PR #554 changed `guardMutating` to fall back to the OAuth session's
`account_login` when Remote-User is absent. The auth tests still
asserted that "no Remote-User → 403" while sending the operator
session cookie via the shared `req()` helper — so the fallback
matched, the request passed the guard, and the test failed.

Three describes updated:
- "Remote-User is absent" → "no Remote-User AND no session" (use new
  `reqNoSession` helper that strips the cookie).
- "Remote-User from untrusted IP" — same treatment; the trusted-proxy
  drop only matters when the session fallback can't paper over it.
- "x-claude-client-ip cannot be spoofed" — drop the cookie so the
  spoof attempt can't ride a valid session.

Status assertion loosened from `=== 403` to "any 4xx" — depending on
order, the session-gate (401) or the operator-auth guard (403) may
reject first; either is the intended security contract.

15 tests fixed; full server suite: 2597 pass / 4 pre-existing fail
(session JSONL pruning + foreman session CRUD on main).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
charles force-pushed restore/merged-ui from 9017c2c426
All checks were successful
qa / qa (pull_request) Successful in 14m26s
qa / dockerfile (pull_request) Successful in 29s
to 6d17b41479
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
2026-04-29 18:42:50 +00:00
Compare
charles force-pushed restore/merged-ui from 6d17b41479
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
to 4c4d71f28f
All checks were successful
qa / qa (pull_request) Successful in 14m8s
qa / dockerfile (pull_request) Successful in 15s
2026-04-29 18:43:19 +00:00
Compare
charles deleted branch restore/merged-ui 2026-04-29 19:06:50 +00:00
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!554
No description provided.