You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(workspace): unfreeze re-run pipeline visual + add cooperative run cancel
Two coupled analysis-run-lifecycle changes (AnalysisRunner.tsx carries
both, so they ship together).
1. fix: the re-run pipeline visual was frozen at all-done.
On a re-run the parent keeps the previous completed `analysisState`
mounted (it is only swapped when the NEW result lands). AnalysisRunner
tested `analysisState` BEFORE `analysisLoading` in the stage
computation, status pip, sub-text and summary, so the stale completed
result short-circuited every `analysisState ? : analysisLoading ?`
ternary — every stage stuck at done/100% for the whole re-run. The Run
button was the lone loading-first branch, which is exactly why it
flipped to "Running..." while the agent cards stayed frozen. Flipped
the precedence so `analysisLoading` wins everywhere a re-run needs
live state; result-derived stale/outage notices are suppressed while
a re-run is in flight.
2. feat: stop a run mid-flight (Cancel/Abort).
There was no off-switch — a misfired premium (gpt-5.5) run burned
tokens with no way to stop it. Added cooperative cancellation at the
existing stage boundary:
- WorkspaceRunJob.cancel_requested + cancel_workspace_analysis_job()
+ POST /workspace/analyze-jobs/{job_id}/cancel. WorkspaceRunJobCancelled
is a plain Exception (not AppError) so it travels unchanged through
the orchestrator's per-agent / AgentExecutionError handlers.
- _update_job_progress (runs at every begin_stage) raises it once the
flag is set; _run_job catches it BEFORE the failure handlers and
ends the job in a distinct terminal "cancelled" state (no error
banner, INFO log).
- Credit auto-refunds: the cancel exception flows through
run_workspace_analysis' existing `except BaseException` refund path,
so a stopped run costs zero application/premium credits.
- Frontend: cancelWorkspaceAnalysisJob() client; useAnalysisJob treats
`cancelled` as terminal (info notice + quota refetch) + cancelAnalysis()
+ sticky analysisCancelling; "Stop run" button (replaces Clear role
while running, "Stopping..." while it unwinds). Idempotent /
double-click-guarded; unknown id -> actionable 404.
Honest caveat by design: a Python thread mid-OpenAI-call cannot be
force-killed, so cancel lands at the NEXT agent boundary (a few-30s,
<=120s worst case). The UI says so rather than implying instant.
Tests: +10 unit (tests/test_workspace_run_jobs_cancel.py — cancel
mechanics, idempotency, the cooperative seam via a faithful fake,
regression guard that real failures still report `failed`) + 2 route
integration. 67 workspace-suite green (no regression); error-message
allowlist green; frontend tsc + eslint clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"Our AI provider (OpenAI) is having a moment, so we built a baseline version of your application. Re-run in a few minutes for the full AI-tailored result."}
@@ -361,7 +398,7 @@ export function AnalysisRunner({
361
398
line. Hidden on desktop via CSS. The pipeline cards
362
399
themselves are also hidden on mobile in the idle / all-done
0 commit comments