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
The run detail page already knows how to render the result: TERMINAL_RUN_STATUS includes "cancelled" ($runId.tsx:96), with a badge case at :181 and a colour at :194.
A repo-wide search for cancelLineupRun returns exactly one hit — its own definition. Nothing in apps/web, apps/tv-web or apps/desktop calls it. So the UI can display a cancelled run, it just has no way to create one.
Why it's worth a button
A deterministic plan failure still retries to max_attempts = 3, and each attempt is a full planner call. Concrete case here today, self-hosted, qwen3.6-35b on vLLM:
Plan attempt 1: AI_NoObjectGeneratedError — finishReason=length rawTextChars=22643, i.e. it hit plannerMaxOutputTokens (32000). Reasoning mode was on, and reasoning + answer share that budget; the same library plans in 12,787 output tokens with thinking off, so it overflowed.
plannerMaxOutputTokens is frozen into the run's args at dispatch, so raising the setting can't rescue the run in flight.
Attempts 2 and 3 will therefore fail identically — about 10 minutes each on local hardware. ~20 minutes of GPU time to re-derive a conclusion that was known after attempt 1.
Nothing in the admin UI can stop that.
The workarounds don't work
Restarting the server doesn't stop a run — it resumes it. Observed here: a run created 2026-09-02, whose plan step failed 3× that day, completed on 2026-09-04 after a container recreate. It then ran wipe-and-rebuild using its original frozen args — so it built at the hardcoded concurrency of 6, predating the channelBuildConcurrency is ignored — the ai-lineup-build job never passes it to the workflow #21 fix that was live by then. A restart is the opposite of an escape hatch.
Deleting the graphile-worker job leaves workflow_runs.status non-terminal, so the run stays eligible to come back the same way.
That leaves calling the mutation by hand from the browser console, which is what I ended up doing.
Suggested shape
A Stop run button in the run detail header beside the status badge, shown when !TERMINAL_RUN_STATUS.has(status) — and optionally on the runs-list rows. A confirm step seems reasonable, since stopping mid-build leaves a partially-built lineup.
There's no way to stop a running AI lineup run from the admin UI — but the backend for it is already written and wired, just never called.
It already exists
ai.cancelLineupRun—packages/api/src/routers/ai.ts:138-143requireLineupRunner().cancel(runId)→run.cancel()—apps/server/src/workflow-engine.ts:118-121TERMINAL_RUN_STATUSincludes"cancelled"($runId.tsx:96), with a badge case at:181and a colour at:194.A repo-wide search for
cancelLineupRunreturns exactly one hit — its own definition. Nothing inapps/web,apps/tv-weborapps/desktopcalls it. So the UI can display a cancelled run, it just has no way to create one.Why it's worth a button
A deterministic plan failure still retries to
max_attempts = 3, and each attempt is a full planner call. Concrete case here today, self-hosted, qwen3.6-35b on vLLM:AI_NoObjectGeneratedError—finishReason=length rawTextChars=22643, i.e. it hitplannerMaxOutputTokens(32000). Reasoning mode was on, and reasoning + answer share that budget; the same library plans in 12,787 output tokens with thinking off, so it overflowed.plannerMaxOutputTokensis frozen into the run's args at dispatch, so raising the setting can't rescue the run in flight.Nothing in the admin UI can stop that.
The workarounds don't work
workflow_runs.statusnon-terminal, so the run stays eligible to come back the same way.Suggested shape
A Stop run button in the run detail header beside the status badge, shown when
!TERMINAL_RUN_STATUS.has(status)— and optionally on the runs-list rows. A confirm step seems reasonable, since stopping mid-build leaves a partially-built lineup.Happy to test whatever you put together.