Skip to content

Add a "Stop run" control — ai.cancelLineupRun exists but has no caller #28

Description

@area51tazz

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.cancelLineupRunpackages/api/src/routers/ai.ts:138-143
  • requireLineupRunner().cancel(runId)run.cancel()apps/server/src/workflow-engine.ts:118-121
  • 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_NoObjectGeneratedErrorfinishReason=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.

Happy to test whatever you put together.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions