feat(jobs): on-demand runs, base branch, and keep-agent toggle#361
Merged
Conversation
Jobs no longer require a cron schedule — the form and card treat a null schedule as "On demand" and rely on Run Now. Adds base_branch (mirrors the normal agent's fork-from control) and a per-job auto_archive flag that, when false, skips the post-run archive so users can hop back into the session. The base-branch combobox + worktree-branch input are extracted from CreateAgentDialog into a shared BranchSelect component reused by the job form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Surface "Open session" for kept-agent runs even after terminal status. useActiveRun now also resolves the agent for jobs with autoArchive=false once the run completes; banner copy switches accordingly. - Hide the Enabled toggle on scheduleless jobs in both AddJobFlow and SettingsTab; auto-reset enabled when the schedule is cleared. - Unify "Run now" wording across header + justAdded card. - Update AddJobFlow subtitle and sidebar empty-state copy to mention on-demand. - Advanced-settings description now mentions agent lifecycle. - Tighten Keep-agent-after-run copy to tell the user where the agent lives afterward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extract the duplicated useClickOutside hook into @/hooks/use-click-outside and import from branch-select, create-agent-dialog, and path-input. - BranchSelect stops Escape propagation on the inner Command, removing the need for the parent Dialog to mirror dropdown-open state. Drops the onDropdownOpenChange prop and the vestigial branchDropdownOpen in CreateAgentDialog. - Rename useActiveRun -> useAttachedJobAgent and the context field activeRunAgent -> attachedAgent so the name matches the contract (also resolves for kept-agent terminal runs). - Rename skipAutoArchive -> keepAgent (and JobSkipAutoArchiveOption -> JobKeepAgentOption) so state reads positively and matches the UI label. - Drop the reset-on-clear useEffect for the Enabled toggle in both AddJobFlow and SettingsTab; derive effectiveEnabled at submit time. - Document the fetch-time auto-correction on BranchSelect#onBaseBranchChange. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Radix DismissableLayer registers a document-level keydown listener
with { capture: true }. Our earlier fix relied on React synthetic
stopPropagation, which runs after the native event has already
reached document, so the dialog still closed.
Use the proper Radix escape hatch: onEscapeKeyDown on DialogContent.
A new helper swallowEscapeFromCombobox calls event.preventDefault()
when the event target is inside a [cmdk-root], which tells Radix's
DismissableLayer to skip its dismiss. The combobox's own React
onKeyDown handler then fires normally and closes the dropdown.
Wired into the AddJob DialogPrimitive.Content in jobs-pane and the
CreateAgentDialog DialogContent. Both paths verified in Playwright:
first Escape closes only the dropdown, second closes the dialog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BranchSelect: surface fetch errors with a Retry affordance, and stop silently overwriting the saved baseBranch when it's missing from the fetched list. The saved branch stays selectable, flagged as "not found", and an inline warning asks the user to pick a new one. - Move the Enabled toggle in AddJobFlow and SettingsTab from the top of the card to directly after the Cron schedule input so it appears next to the field that gives it meaning. - Job card: show a "keeps agent" pill when autoArchive is false so the two on-demand variants are distinguishable from the list. - Advanced-settings subhead explicitly names the kept-agent behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the toggle from a new row below both Cron + Agent type into the Cron field's own column so it sits immediately below the input that gives it meaning. Applied to both AddJobFlow and SettingsTab. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
selfcontained
marked this pull request as ready for review
April 19, 2026 02:10
3 tasks
niiyeboah
added a commit
that referenced
this pull request
Jul 8, 2026
- Bound MessageStore.listForAgent to the most recent 500 (mirrors the history-detail LIMIT 500); the sidebar refetches this eagerly, so it must not be unbounded. (security #360, architecture #362) - Only publish message.created when the row actually persisted; the insert failure path stays swallow-and-log so delivery is never blocked, but the UI no longer gets an event with no backing row. (security #361) - Serve unreadCount from GET /agents/:id/messages via countUnreadForAgent (uses the partial unread index) and drive the sidebar badge from it, so the count stays accurate despite the list cap. (architecture #363) - Drop the unused agentManager dep from the messages routes. (architecture #364) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
selfcontained
pushed a commit
that referenced
this pull request
Jul 10, 2026
- Bound MessageStore.listForAgent to the most recent 500 (mirrors the history-detail LIMIT 500); the sidebar refetches this eagerly, so it must not be unbounded. (security #360, architecture #362) - Only publish message.created when the row actually persisted; the insert failure path stays swallow-and-log so delivery is never blocked, but the UI no longer gets an event with no backing row. (security #361) - Serve unreadCount from GET /agents/:id/messages via countUnreadForAgent (uses the partial unread index) and drive the sidebar badge from it, so the count stays accurate despite the list cap. (architecture #363) - Drop the unused agentManager dep from the messages routes. (architecture #364) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
selfcontained
added a commit
that referenced
this pull request
Jul 10, 2026
… History (#736) * Add design spec for agent messages feature Persist agent-to-agent messages, surface them in a per-agent sidebar Messages tab, and add a Messages tab to the History detail view. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add implementation plan for agent messages feature Task-by-task TDD plan: agent_messages table + MessageStore, persist/ broadcast on send, REST endpoints, useAgentMessages hook + SSE, sidebar Messages tab, History detail Messages tab, and E2E coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(messages): add agent_messages table and MessageStore Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(messages): persist and broadcast messages on send * feat(messages): add read + mark-read REST endpoints Add GET /api/v1/agents/:id/messages and POST /api/v1/agents/:id/messages/read, backed by MessageStore. Mark-read publishes a message.read UI event. Also add the message.created and message.read variants to the backend UiEvent union so sender/mark-read publishes type-check against the broker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(messages): add useAgentMessages hook and SSE wiring * feat(messages): add MessagesPanel with conversation threads * fix(messages): add loading state to MessagesPanel Expose isLoading from useAgentMessages hook and render a spinner while loading, preventing misleading empty state flash on initial mount. Modeled on BrainTabContent's loading pattern for consistency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(messages): add Messages tab to the agent sidebar * feat(messages): surface messages in History detail view Add a messages query to GET /api/v1/history/agents/:id and a Messages tab to the History detail view, backed by a new MessageTimeline component. * test(messages): e2e coverage for sidebar + history messages Add seedAgentMessageViaDB helper and agent-messages.spec.ts covering the sidebar Messages tab (unread badge appears then clears on open) and the History detail Messages tab. Also fix a markRead useCallback dependency (depended on the unstable mutation object, causing a request storm and a flickering unread badge). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(messages): clarify History is not live-invalidated Final review found the data-flow diagram implied the History detail tab refetches on message.created; it does not. History is retrospective and matches its sibling tabs (refetch on remount/focus/staleTime). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(messages): apply prettier formatting CI `pnpm run format` (prettier --check) flagged these files; the local pre-commit hook was unavailable so formatting wasn't applied on commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(messages): address persona review findings - Bound MessageStore.listForAgent to the most recent 500 (mirrors the history-detail LIMIT 500); the sidebar refetches this eagerly, so it must not be unbounded. (security #360, architecture #362) - Only publish message.created when the row actually persisted; the insert failure path stays swallow-and-log so delivery is never blocked, but the UI no longer gets an event with no backing row. (security #361) - Serve unreadCount from GET /agents/:id/messages via countUnreadForAgent (uses the partial unread index) and drive the sidebar badge from it, so the count stays accurate despite the list cap. (architecture #363) - Drop the unused agentManager dep from the messages routes. (architecture #364) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(messages): address frontend UX review findings - Only mark messages read when the sidebar is actually open on the Messages tab; gating on the persisted tab alone silently cleared unread state for a closed sidebar. (#365) - Surface unread messages on the collapsed sidebar toggle (combined with the unseen-media count) so unread is discoverable while collapsed. (#366) - Add an explanatory tooltip to the "not delivered" label in both the sidebar panel and the History timeline. (#367) - Give the populated message list min-h-0 flex-1 so it scrolls within the sidebar instead of overflowing past the bottom edge. (#368) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(messages): review feedback — split hooks, unify bubble UX, improve icons Split useAgentMessages into three focused hooks (messages list, unread count, mark-read mutation). Unify MessageTimeline and MessageBubble into a single exported component used in both sidebar and history. Add collapsible agent-grouped threads with framer-motion animation and persisted collapse state. Add generic Bot fallback icon for unknown agent types. Replace non-reactive queryClient.getQueryData with reactive useQuery+select for agent type enrichment. Delete unused plan doc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(messages): address persona review round 1 findings Fix history query to return newest 500 messages (subquery pattern), resolve recipientRepoRoot from target agent's cwd, add tab overflow scrolling for sidebar and history tabs, improve copy button touch target. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(sidebar): drop Brain tab, revert to label tabs Brain data is accessible from the global area; per-agent sidebar tab was redundant. Reverts icon experiment and restores text labels for the remaining 4 tabs (Pins, Media, Reviews, Messages). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(db): renumber agent-messages migration to 0030 Resolves duplicate prefix conflict with 0029_reviews.sql from main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: remove brain-sidebar E2E tests Brain tab was removed from the sidebar in this PR. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(e2e): increase split-pane reload timeout to reduce flakiness The jotai atom reads localStorage asynchronously after hydration; 5s was sometimes too short in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Brad Harris <bmharris@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
base_branchalongside the existingbranch_name(worktree name). Extracts the create-agent-dialog combobox into a reusableBranchSelectcomponent used by both forms.auto_archiveflag (defaulttrue). When false, the agent is kept around after the run terminates so the user can hop into the session — snapshotted ontoJobRunConfigat run creation.Changes
0015_jobs-base-branch-auto-archive.sql— addsbase_branch TEXT+auto_archive BOOLEAN DEFAULT TRUE(bothIF NOT EXISTS, idempotent). Note: verify the next-free number against prodpgmigrationsbefore merging.apps/server/src/jobs/store.ts—JobRecord,JobConfigUpdate,JobRunConfig,createJob,updateJobConfig,jobColumns,listJobsSELECT.apps/server/src/jobs/service.ts—AddJobInput,createAgent({ baseBranch })wiring,buildRunConfigsnapshotsautoArchive.apps/server/src/server.ts—AddJobBodySchemaacceptsbaseBranch+autoArchive; auto-archive handler gated onrun.config.autoArchive.apps/web/src/components/app/branch-select.tsx— new shared component.apps/web/src/components/app/create-agent-dialog.tsx— consumesBranchSelect; drops inline combobox state.apps/web/src/components/app/jobs-pane.tsx— add-flow + settings tab add base branch + "Keep agent after run completes"; card shows "On demand" (hides enabled/disabled) for scheduleless jobs.apps/web/src/hooks/use-jobs.ts—Job+AddJobConfigextended.Test plan
pnpm run checkpnpm run test(Vitest, 325 pass)pnpm run test:e2e(Playwright, 139 pass)pnpm run finalize:webPOST /api/v1/jobswithbaseBranch:"develop", autoArchive:false, schedule:nullpersists and returns correctly; existing seeded jobs default toautoArchive: true.Backwards compatibility
auto_archivedefaults toTRUE, so existing jobs keep current archive-on-terminal behavior.base_branchis nullable; server-sidecreateAgentalready falls back to"main".JobRunConfig: older runs withoutautoArchivein their config default totrueat the handler.🤖 Generated with Claude Code