fix(miner-ui): resolve governor pause/resume chat intents to the registered actions - #8781
Conversation
…onversation The chat rail registered and unit-tested the whole governor pause/resume action family but never dispatched it: conversation.tsx only resolved portfolio-queue text, so governor-intent messages fell through to the read-only streaming assistant and GovernorChatActionResult was never rendered anywhere. Add resolveGovernorChatAction (mirroring chat-portfolio-queue-resolve), wire it into handleSubmit ahead of the generic stream so a matched intent dispatches through runGovernorChatAction (registration + flag-gated dispatch + gate), and render GovernorChatActionResult in the message stream: pending copy below the list while the round-trip is in flight, and the resolved Ledgers-verbatim result as the turn outcome. Closes JSONbored#8670
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-26 05:08:36 UTC
Review summary Nits — 5 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. Scroll preview
A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|

Closes #8670
What
apps/loopover-miner-ui's governor pause/resume chat-action family was fully built and unit-tested —runGovernorChatAction(dispatch wrapper),GovernorChatActionResult(result renderer),chat-governor-action-copy.ts(Ledgers-verbatim copy) — but dead in the product:conversation.tsx'shandleSubmitonly resolved portfolio-queue text, so an operator typing "pause the governor" into the chat rail was silently answered by the generic read-only streaming assistant.runGovernorChatActionhad zero non-test callers andGovernorChatActionResultwas never imported outside its own test, exactly as the issue's repo-wide grep found.How
All four Deliverables, in this one PR:
src/lib/chat-governor-resolve.ts(resolveGovernorChatAction) mirrorschat-portfolio-queue-resolve.ts's pattern: pure (no fetch, no dispatch), word-bounded pause/halt/suspend vs resume/unpause intent detection that must co-occur with the word "governor", an optional pause reason from a trailingbecause …/reason: …clause (mapped to the{ reason }params shapeisGovernorPauseChatParamsvalidates), and an explicit unresolvable result for empty/ambiguous/verb-less text so a governor question still falls through to the assistant and nothing is ever best-guess dispatched.handleSubmitwiring —conversation.tsxresolves governor intent right after the portfolio-queue check and, on a match, performs the same idempotentregisterGovernorChatActions()registration the Vite dev-server entry does, then dispatches throughrunGovernorChatAction(flag-gateddispatchChatAction+governorGatedHandler, never a direct handler call). Unresolved text streams exactly as before.GovernorChatActionResultrendered in the message stream — the in-flight round-trip renders the component's pending state (role="status", "Updating governor…") in the same viewport slot the live streaming render uses, and the resolved turn is committed to the message list with the result attached, whereMessageBubblenow renders it throughGovernorChatActionResult(error →role="alert", success → the Ledgers-verbatim "Paused since …"/"Not paused" copy). Non-executed dispatches (flag off / gated) and thrown dispatches surface as inline system notes, mirroring the existing failure convention.chat-conversation.test.tsxgains a 7-test#8670describe that rendersChatConversation, types governor-intent text into the real composer, and drives the REAL registration + dispatch pipeline (createChatActionRegistry()+ injectedpauseGovernor/resumeGovernorclients only): asserts the pause action actually fires, the because-clause reachespauseGovernor("release traffic spiked")as the structured reason, resume fires,GovernorChatActionResult's copy appears in the message list, pending copy shows while outstanding, non-executed/thrown dispatches surface, andstreamChatis never called for action turns (0 calls) while ordinary governor questions still stream. Resolver branches are pinned inchat-governor-resolve.test.ts.Before / after (RED proof)
The new end-to-end tests on unfixed
main(fix stashed, tests kept) fail exactly as the issue describes — governor-intent text falls through to the generic assistant and no action ever fires:With the fix, the full app suite:
Local vitest coverage thresholds (85/85/75/85, enforced in CI for this app) pass at 91.81% statements / 89.64% branches / 87.55% functions / 94.36% lines; every new/changed file is at 100% statements (
chat-governor-resolve.ts100% across all four metrics).tsc --noEmit,eslintandprettier --checkon the changed files, andgit diff --checkare all green.UI Evidence
The miner-ui chat rail on the Overview route, served by
vite devin the app's own demo mode (VITE_DEMO_MODE=1, sample data, no live backend), captured per viewport × theme below. The read-onlyPOST /api/chatstream is answered at the network boundary by a fixture (the generic grounded assistant reply); the governor path needs no fixture at all — in demo modepauseGovernorperforms the real demo-state write, so the After column is the actual resolver → registration → flag-gated dispatch →GovernorChatActionResultpath executing in the browser. Look at the reply to "pause the governor": before, the generic assistant answers in prose and nothing happens (the bug); after, the pause action executes and the system turn rendersGovernorChatActionResult's "Paused since …" copy. Capture note: currentmaincannot paint this SPA at all —@loopover/engine'sdist/calibration/backtest-split.js(#8097) reaches the browser graph through the chat-action registry chain and its top-levelnode:cryptoaccess makes Vite's browser-external shim throw before first render, on cleanmainexactly as on this branch — so the harness replaces that shim with an inert stub at the network boundary; none of those node-only modules are executed by the surface being captured, and the un-stubbed behavior is identical (a blank page) before and after this change.