From 3e74f28e4ff4ba44ff19e41d45c09b466d5dc6f4 Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Fri, 11 Sep 2026 03:34:03 +0300 Subject: [PATCH] Dock END button speaks the real abort contract: already-gone is success, only true failures report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /api/activity/abort landed on main (fde46c2). 404 not_found / 409 not_running mean the process reached the desired end state on its own — refresh and clear the abort state, no error surfaced. Genuine failures (network/400) show 'Abort failed — try again' on the blob. --- components/activity-dock.tsx | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/components/activity-dock.tsx b/components/activity-dock.tsx index 0a56cac..cdf349d 100644 --- a/components/activity-dock.tsx +++ b/components/activity-dock.tsx @@ -26,8 +26,9 @@ import { formatTokens, formatUsd } from "@/components/usage-format"; * in place — polling never flickers. Idle (no jobs, no extractions) renders * NOTHING. Narrow viewports collapse to a count badge → click opens the * drawer. Per blob: kind icon, elapsed, cost, context, activity line, and an - * explicit END button (POST /api/activity/abort — the backend lands with the - * agents stream; failures degrade to an inline "not available yet", never a + * explicit END button (POST /api/activity/abort — the wave-4 backend is + * live): 404/409 mean the process is already gone (refresh, no error); + * real failures degrade to an inline "Abort failed", never a * dead control). */ @@ -246,7 +247,19 @@ export function ActivityDock({ feed: feedProp }: { feed?: ActivityFeed }) { id: blob.id, }), }); - if (!res.ok) throw new Error(String(res.status)); + if (!res.ok) { + // 404 not_found / 409 not_running: the process is already gone — + // the desired end state. Refresh and clear; nothing to report. + if (res.status === 404 || res.status === 409) { + abortRef.current.refresh(); + set((s) => { + const { [blob.id]: _drop, ...rest } = s; + return rest; + }); + return; + } + throw new Error(String(res.status)); + } abortRef.current.refresh(); } catch { set((s) => ({ ...s, [blob.id]: "failed" })); @@ -368,14 +381,14 @@ function ActivityBlobRow({ {/* The explicit END always on the blob's face (SCOPE) — the popover - is for DETAIL, not the control. Until the abort backend lands, a - click fails gracefully: the button locks, the popover says why. */} + is for DETAIL, not the control. A failed abort locks the button and + says why; a succeeded one just makes the blob vanish on refresh. */}