[codex] Add live-run stop finalization actions (#7679)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Operators supervise live agent runs from the issue detail chat surface. > - The existing run menu can pause/stop work, but operators sometimes need to stop the active run and immediately finalize the task outcome. > - Doing those as separate actions is slower and easier to leave half-finished. > - This pull request adds explicit live-run finalization actions to the issue chat run menu. > - The benefit is a clearer operator path for stopping a live run and marking the task done or cancelled in one ordered flow. ## Linked Issues or Issue Description No public GitHub issue exists for this internal Paperclip task. Internal task: PAP-10535. ## Subsystem affected ui/ — React + Vite board UI. ## Problem or motivation Operators can stop an active run from the issue detail chat, but finalizing the issue outcome requires a separate status action after the run is stopped. That extra step makes live-run finalization slower and easier to leave incomplete. ## Proposed solution Add explicit issue chat run-menu actions for `Stop and cancel` and `Stop and done`, where each action cancels the active heartbeat run before updating the issue status. ## Alternatives considered Keep the existing two-step flow of cancelling the run first and then changing issue status separately. That preserves current behavior but does not solve the operator workflow gap. ## Roadmap alignment This is a small targeted UI control-plane improvement for supervising live agent work. It does not duplicate a planned core roadmap item found in `ROADMAP.md`. This PR was split from the local `master` branch on June 7, 2026. It covers the UI-only live-run finalization action. I searched GitHub for duplicate/related PRs; the results were broader run-control PRs, not this exact issue-detail menu action. ## What Changed - Added optional `runFinalizationActions` support to `IssueChatThread` assistant message run menus. - Added `Stop and cancel` and `Stop and done` actions on the issue detail chat tab. - Each action cancels the active heartbeat run before updating the issue status. - Added focused UI coverage to assert cancellation happens before the status update. - Addressed Greptile feedback for partial-failure messaging and duplicate run-state invalidation. ## Verification - `git diff --check origin/master..HEAD` - `git diff --check` - `NODE_ENV=test pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx` - Storybook screenshot capture for the live-run menu before and after the finalization actions. ## Screenshots Before: existing live-run menu only offered the normal stop action.  After: the live-run menu includes `Stop and cancel` and `Stop and done`.  ## Risks - Medium UI behavior risk: the new actions expose faster finalization controls from the live-run menu. They are gated through the existing issue detail management surface and still use the existing run cancel and issue update APIs. - Low migration risk: no schema, API contract, or dependency changes. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex coding agent based on GPT-5, with shell, git, GitHub CLI, local test execution, and Playwright browser screenshot capture. Exact hosted model variant and context-window size were not exposed by the runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -157,6 +157,7 @@ interface IssueChatMessageContext {
|
||||
stopRunLabel?: string;
|
||||
stoppingRunLabel?: string;
|
||||
stopRunVariant?: "stop" | "pause";
|
||||
runFinalizationActions?: readonly IssueChatRunFinalizationAction[];
|
||||
onInterruptQueued?: (runId: string) => Promise<void>;
|
||||
onCancelQueued?: (commentId: string) => void;
|
||||
onDeleteComment?: (commentId: string) => Promise<void> | void;
|
||||
@@ -194,6 +195,15 @@ const IssueChatCtx = createContext<IssueChatMessageContext>({
|
||||
successfulRunHandoff: null,
|
||||
});
|
||||
|
||||
export type IssueChatRunFinalizationAction = {
|
||||
id: "cancel" | "done";
|
||||
label: string;
|
||||
pendingLabel: string;
|
||||
onSelect: (runId: string) => Promise<void> | void;
|
||||
isPending?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export function resolveAssistantMessageFoldedState(args: {
|
||||
messageId: string;
|
||||
currentFolded: boolean;
|
||||
@@ -342,6 +352,7 @@ interface IssueChatThreadProps {
|
||||
stopRunLabel?: string;
|
||||
stoppingRunLabel?: string;
|
||||
stopRunVariant?: "stop" | "pause";
|
||||
runFinalizationActions?: readonly IssueChatRunFinalizationAction[];
|
||||
imageUploadHandler?: (file: File) => Promise<string>;
|
||||
onAttachImage?: (file: File) => Promise<IssueAttachment | void>;
|
||||
draftKey?: string;
|
||||
@@ -1513,6 +1524,7 @@ function IssueChatAssistantMessage({
|
||||
stopRunLabel = "Stop run",
|
||||
stoppingRunLabel = "Stopping...",
|
||||
stopRunVariant = "stop",
|
||||
runFinalizationActions = [],
|
||||
} = useContext(IssueChatCtx);
|
||||
const custom = message.metadata.custom as Record<string, unknown>;
|
||||
const anchorId = typeof custom.anchorId === "string" ? custom.anchorId : undefined;
|
||||
@@ -1731,6 +1743,29 @@ function IssueChatAssistantMessage({
|
||||
{isStoppingRun ? stoppingRunLabel : stopRunLabel}
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
{canStopRun && runId
|
||||
? runFinalizationActions.map((action) => (
|
||||
<DropdownMenuItem
|
||||
key={action.id}
|
||||
disabled={isStoppingRun || action.isPending || action.disabled}
|
||||
className={cn(
|
||||
action.id === "cancel"
|
||||
? "text-red-700 focus:text-red-800 dark:text-red-300 dark:focus:text-red-200"
|
||||
: "text-green-700 focus:text-green-800 dark:text-green-300 dark:focus:text-green-200",
|
||||
)}
|
||||
onSelect={() => {
|
||||
void action.onSelect(runId);
|
||||
}}
|
||||
>
|
||||
{action.id === "cancel" ? (
|
||||
<Square className="mr-2 h-3.5 w-3.5 fill-current" />
|
||||
) : (
|
||||
<Check className="mr-2 h-3.5 w-3.5" />
|
||||
)}
|
||||
{action.isPending ? action.pendingLabel : action.label}
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
: null}
|
||||
{runHref ? (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to={runHref} target="_blank" rel="noreferrer noopener">
|
||||
@@ -3773,6 +3808,7 @@ export function IssueChatThread({
|
||||
stopRunLabel,
|
||||
stoppingRunLabel,
|
||||
stopRunVariant,
|
||||
runFinalizationActions,
|
||||
imageUploadHandler,
|
||||
onAttachImage,
|
||||
draftKey,
|
||||
@@ -4290,6 +4326,7 @@ export function IssueChatThread({
|
||||
stopRunLabel,
|
||||
stoppingRunLabel,
|
||||
stopRunVariant,
|
||||
runFinalizationActions,
|
||||
onInterruptQueued: stableOnInterruptQueued,
|
||||
onCancelQueued: stableOnCancelQueued,
|
||||
onDeleteComment: stableOnDeleteComment,
|
||||
@@ -4313,6 +4350,7 @@ export function IssueChatThread({
|
||||
stopRunLabel,
|
||||
stoppingRunLabel,
|
||||
stopRunVariant,
|
||||
runFinalizationActions,
|
||||
stableOnInterruptQueued,
|
||||
stableOnCancelQueued,
|
||||
stableOnDeleteComment,
|
||||
|
||||
Reference in New Issue
Block a user