Skip to content

fix(chat): keep Author and Plan and Execute runs alive when leaving the page - #39

Merged
HackedRico merged 3 commits into
mitre:mainfrom
HackedRico:fix/chat-background-runs
Sep 1, 2026
Merged

fix(chat): keep Author and Plan and Execute runs alive when leaving the page#39
HackedRico merged 3 commits into
mitre:mainfrom
HackedRico:fix/chat-background-runs

Conversation

@HackedRico

Copy link
Copy Markdown
Collaborator

Description

Starting a run in Author or Plan and Execute disabled Back, the History toggle and
New chat until it finished, so a run could not be sent to the background.

The backend already ran detached and served /plugin/mcp/status from an in-memory
cache, but the client forgot the run once ChatWorkflow unmounted, so removing the
:disabled alone would have orphaned it. This makes the run recoverable first:

  • persist run_id on its assistant message, written where /execute answers
    rather than through a watcher that unmount stops
  • re-attach a poller on mount to the newest message still marked RUNNING
  • stop the poller in onBeforeUnmount, fixing the leaked 1s interval

hydrate() now fails only a RUNNING message it cannot resume, pointing it at the
History tab. A second commit closes three races that unlocking navigation made
reachable: a submit superseded mid-POST adopting its ids into the transcript that
replaced it, a stale poll clearing the next run's timer, and the login page CALDERA
returns as a 200 on an expired session being parsed as a run snapshot.

Not in scope: the composer stays disabled during a run, since this view polls one
run at a time and a second prompt would orphan the first. Also out of scope are the
synchronous MLflow calls noted as secondary in #30.

Refs #30

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Manually against a running CALDERA on both workflows: submit, click Back while the
header shows Working, confirm the run keeps progressing in the server log, re-enter
and confirm the bubble picks up mid-stage. Also hard-refreshed mid-run, restarted
CALDERA mid-run, and used New chat mid-run.

All nine chat SFCs compile cleanly under the same @vue/compiler-sfc version Magma
bundles them with. The two composables were exercised by 80 behavioural checks
against the real Vue runtime with a stubbed $api, stubbed timers and a fake
localStorage, including one regression check per race above; removing any guard
fails them. Not committed, as the plugin has no JS test tooling.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works

…he page

Starting a run locked the whole workflow page. Back, the History toggle
and New chat were disabled until it finished, so a run could not be sent
to the background even though the backend already runs it detached and
serves /status from an in-memory cache.

The guard was load bearing rather than gratuitous: nothing on the client
remembered a run once the component unmounted, so removing the
:disabled alone would have orphaned the run in the UI while the server
kept working.

Three pieces make the guards removable:

  - persist the run_id on its assistant message, written as soon as
    /execute answers rather than on the first poll, so clicking Back
    within the first second still keeps the handle
  - re-attach a poller on mount to the newest message still marked
    RUNNING, and read its status once up front so the view paints the
    real state instead of stale content
  - stop the poller in onBeforeUnmount, which also fixes the 1s interval
    leaking whenever the component unmounted by any other route

hydrate() now only fails a RUNNING message it cannot resume: an older
one, or one that never captured a run_id. Those still point at the
History tab, as does a re-attach that 404s because the server restarted
or the run aged out of the live cache.

The composer stays gated while a run is in flight. This view polls one
run at a time, and a second prompt would silently orphan the first, so
that guard is about concurrency rather than about trapping the user.
Its placeholder now says the page can be left.

Two supporting fixes in the poller, both reachable now that New chat
works mid-run: drop status snapshots for a run the composable has moved
on from, which would otherwise strand the view on a RUNNING status
nothing polls any more, and ride out transient status failures instead
of failing a healthy run on a single blip.

Refs mitre#30
An adversarial review of b1f5f4e found four client-side races, three of
them reachable only because that commit unlocked navigation.

start() checked neither supersession nor detachment after its POST. Back
mid-POST leaked a poller into an unmounted view, and New chat mid-POST
adopted the abandoned run's ids into the transcript that replaced it.

The interval callback cleared whichever timer was currently installed
rather than its own, so a superseded run whose status GET settled late
killed the next run's poller, stranding it on RUNNING with the composer
disabled and no in-view recovery.

The run_id reached localStorage only through watchers Vue stops on
unmount, so clicking Back during the POST stored a live run with runId
null and the next mount labelled a working run as failed. It is now
written imperatively at the point /execute answers.

An expired session arrives as a 200 carrying the login page rather than
a snapshot, which was parsed into status "unknown" and persisted. That
value is recognised by neither the resume path nor the hydrate sweep, so
the bubble could be neither re-attached nor failed. gui/composables
already documents this trap; reuse its SESSION_EXPIRED message.

Also stop reporting a lost poll as a failed run: three consecutive status
errors now say contact was lost and point at the History tab.

Refs mitre#30
@HackedRico
HackedRico merged commit 29b59d5 into mitre:main Sep 1, 2026
3 checks passed
@HackedRico
HackedRico deleted the fix/chat-background-runs branch September 1, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant