From 1a9b162615d8ce65e8f769ac57eef00bd7e011d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 16 Aug 2026 02:06:57 +0000 Subject: [PATCH] a stopped job stays stopped, and Stop is a real button when expanded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things from stopping one by hand. ── It restarted itself ── Pressing stop produced the exit notification, and the agent read that as a crash: it brought the server back up, then wrote three paragraphs theorising that "something outside this conversation sent that SIGTERM" and counting it as the third external touch on the repo today. It was not wrong about the signal. It could not have been right about the cause, because the stop button did not exist when the preamble was written, and the preamble said only "report" — leaving what to do with an exit entirely open. So it now says: on exit, one line, and STOP. Do not restart. Do not diagnose. Do not theorise about what else on the machine might have touched it. A job I stopped that comes back on its own is the opposite of a stop button — and an exit is at least as likely to be me as a failure now that pressing a button is how you end one. Deliberately not written as "ignore exit code 143": the phrasing is about intent, not signal numbers, so it holds whether the job dies by SIGTERM, SIGKILL or its own hand. ── Stop, labelled, in the expanded panel ── The chip keeps its icon; the panel gets a bordered button that says "Stop". The panel is where you go to decide whether something should keep running, and the control that ends it should not be a glyph you have to hover to identify. Both appear only while the task is running. tsgo clean, frontend builds, 851 pass / 7 fail unchanged. Co-Authored-By: Claude Opus 5 --- .../src/apps/Chat/background-job-prompt.ts | 9 +++++++ .../Chat/components/BackgroundTaskTray.tsx | 26 +++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/Chat/background-job-prompt.ts b/src/workspaces/officerdev/src/apps/Chat/background-job-prompt.ts index 2e0c5009..59182667 100644 --- a/src/workspaces/officerdev/src/apps/Chat/background-job-prompt.ts +++ b/src/workspaces/officerdev/src/apps/Chat/background-job-prompt.ts @@ -120,5 +120,14 @@ export function buildBackgroundJobPrompt(spec: BackgroundJobSpec): string { `Do not read the output back to check it started. If it fails, the exit reaches me on its own —`, `that is the entire point of using the harness's mechanism, and checking by hand just adds noise`, `to the one case where nothing is wrong.`, + ``, + `IF IT EXITS:`, + ``, + `Say so in one line and STOP. Do not restart it. Do not diagnose why. Do not theorise about what`, + `else on the machine might have touched it.`, + ``, + `I can stop a job myself, from a button on its chip — so an exit is at least as likely to be me`, + `as it is to be a failure, and a job I stopped that comes back on its own is the opposite of a`, + `stop button. If I want it running again I will start it again.`, ].join('\n'); } diff --git a/src/workspaces/officerdev/src/apps/Chat/components/BackgroundTaskTray.tsx b/src/workspaces/officerdev/src/apps/Chat/components/BackgroundTaskTray.tsx index 79caa898..6b12b9d8 100644 --- a/src/workspaces/officerdev/src/apps/Chat/components/BackgroundTaskTray.tsx +++ b/src/workspaces/officerdev/src/apps/Chat/components/BackgroundTaskTray.tsx @@ -71,7 +71,13 @@ export const BackgroundTaskTray = ({ messages }: BackgroundTaskTrayProps) => { return (
- {open && setOpenId(null)} />} + {open && ( + setOpenId(null)} + onStop={open.status ? undefined : () => void stopTask(open.taskId)} + /> + )} {pinned.length > 0 && ( 0}> @@ -222,11 +228,13 @@ const TaskChip = ({ task, active, pinned, onClick, onTogglePin, onDismiss, onSto // ── Panel ── type TaskPanelProps = { + /** Present only while the task is running — the panel shows a real button rather than a bare glyph. */ + onStop?: () => void; task: BackgroundTask; onClose: () => void; }; -const TaskPanel = ({ task, onClose }: TaskPanelProps) => { +const TaskPanel = ({ task, onClose, onStop }: TaskPanelProps) => { const running = !task.status; const { data, isLoading } = useTaskDetail(task.taskId, running); const bodyRef = useRef(null); @@ -249,6 +257,20 @@ const TaskPanel = ({ task, onClose }: TaskPanelProps) => {
{running && } + {/* Labelled here, unlike the chip's icon-only version. The panel is where you come to decide + whether to keep something running, so the control that ends it should not be a glyph you + have to hover to identify. */} + {running && onStop && ( + + )}