feat(dashboard): /config form — Thresholds + Container sections #479
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!479
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "dev/459"
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?
Adds Thresholds and Container tabs to the
/configper-type form panel.Thresholds tab: per-type WIP cap, escalation cap, model dropdown; pipeline-global stall thresholds (stall, CI, review, implement, default — with ms unit and hardcoded default hint); watchdogs-global intervals (container, tail-PR-rebase, dead-letter, janitor). Each field has a Reset button that clears the override back to the runtime default.
Container tab: per-type image override toggle (when off → shows global
container_image_defaultfallback); env var key→value row editor with add/remove.API fix:
fetchAgentConfigandputAgentConfignow correctly unwrap/wrap the server's{ content, mtime, etag }envelope.putAgentConfigmerges form-managed fields back into the full config before writing, preserving operator-managed fields the form doesn't touch.Test plan
ThresholdsSection: typing "abc" in a number input does not update stateThresholdsSection: Reset button is disabled at default, enabled after editing, clears field on clickThresholdsSection: model dropdown callsonTypeConfigChangewith the selected model IDThresholdsSection: pipeline + watchdog fields callonGlobalConfigChangeContainerSection: unchecked override shows fallback text; checking it shows image inputContainerSection: Add env var / edit key / edit value / remove row all update staged stateCloses #459
Adds ThresholdsSection and ContainerSection to the /config per-type form. - ThresholdsSection: per-type WIP cap, escalation cap, model dropdown; pipeline-global stall thresholds (6 fields); watchdogs-global intervals (4 fields). NumberField helper with unit hint, default hint, and reset button per field. - ContainerSection: per-type image override toggle + text input that falls back to global container_image_default; env var key→value row editor (add / edit key / edit value / remove rows). - Fixes fetchAgentConfig / putAgentConfig to properly unwrap/wrap the server's { content, mtime, etag } envelope instead of treating the raw response as { types }. - Adds env field to containerSchema (server-side Zod schema). - Extends AgentTypeConfig with wip_soft_limit, container.image, container.env. - Adds GlobalPipelineConfig and GlobalWatchdogsConfig API types. - ConfigRoute gains globalDraft state (pipeline + watchdogs + container_image_default). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>10afd7ee62ada2fa0b5eada2fa0b5edebe146562behavior
apps/web/src/routes/config.tsx:1308: Clearing the image text input while the override toggle is checked silently unchecks the toggle and hides the input. The culprit isonChange={(e) => patchContainer({ image: e.target.value || undefined })}— when the field is emptied,"" || undefinedevaluates toundefined,hasImageOverride(container.image !== undefined) flips tofalse, and the input disappears mid-typing. A user who checks the override and then clears the field to retype loses the toggle state without clicking it. Fix:patchContainer({ image: e.target.value })— accept the empty string as a valid in-progress state while the toggle is on; the checkbox is the explicit control for removing the override.test-gap
apps/web/src/routes/config.test.tsx: No test covers the "clear image input while toggle is checked" path, which is why the above bug slipped through. Add a test that rendersContainerSectionwithcontainer: { image: "foo" }, fires a change event oncontainer-image-inputwith value"", and asserts (a) the onChange call still hascontainer.imagedefined (not undefined), and (b) thecontainer-image-inputis still in the document.e.target.value || undefined→e.target.value— empty string is now accepted as a valid in-progress state; the checkbox remains the sole control for removing the override."clearing image input while toggle is checked keeps toggle on and input in DOM"— fires a change event with""oncontainer-image-input, assertscontainer.imageis defined and the input stays in the DOM.Both prior findings addressed:
patchContainer({ image: e.target.value })correctly preserves empty-string state, and the new "clearing image input while toggle is checked" test covers the regression path. CI green.