Context
⚠️ Read this before starting. This is specifically about the two named plugin files below and the
configurePreviewServer Vite hook. A fix that touches any other vite-*-api.ts file, or that adds a
new /api/* route instead of registering the existing plugin hook, does NOT resolve this issue.
apps/loopover-miner-ui/README.md's "Running as a persistent service" section states, as the documented
contract for this app's production deployment path:
npm run build followed by npm run preview serves the built dashboard and its local-SQLite-backed
API routes (the vite-*-api.ts plugins register for both configureServer and
configurePreviewServer, so nothing extra is needed beyond the build step)
systemd/loopover-miner-ui.service.example — the repo's own recommended persistent-deployment unit for a
fleet/bare-host operator — runs exactly this: npm run build then npm run preview. vite preview only
runs a plugin's configurePreviewServer hook, never configureServer.
Every one of the 12 local Vite plugins wired in vite.config.ts implements both hooks — confirmed by
grep across every vite-*-api.ts file — except two:
vite-chat-governor-actions.ts's chatGovernorActionsPlugin() — only configureServer()
vite-chat-discover-attempt-actions.ts's chatDiscoverAttemptActionsPlugin() — only configureServer()
Both plugins exist solely to call registerGovernorChatActions() / registerDiscoverAttemptChatActions()
on server start, populating the shared chat-action registry (packages/loopover-miner/lib/chat-action-registry.js)
that the chat rail's governor pause/resume and discover/attempt commands dispatch through (#6521, #6837).
Because neither plugin implements configurePreviewServer, that registration call never runs under
vite preview — i.e. never runs under the exact deployment mode the README documents and the systemd
unit uses. An operator running the dashboard as a persistent service can still read the read-only ledgers/
run-history/portfolio views and the read-only chat stream (vite-chat-api.ts, which does implement both
hooks) — but any chat-issued "pause the governor" / "resume the governor" / "release owner/repo" /
"requeue owner/repo #12" / discover / attempt command dispatches against an empty registry, which
dispatchChatAction reports as status: "unknown_action" (rendered to the operator as "Couldn't
{pause/release/…}: action is not registered.") — a confusing, silent-looking failure with no indication
it's a vite preview-only gap, since the exact same command works fine under npm run dev.
Requirements
chatGovernorActionsPlugin() (vite-chat-governor-actions.ts) and
chatDiscoverAttemptActionsPlugin() (vite-chat-discover-attempt-actions.ts) must each implement
configurePreviewServer(server) that performs the exact same registration call their existing
configureServer(server) does, matching the shape every other plugin in this app already uses (e.g.
vite-chat-api.ts's chatApiPlugin — attach() called from both hooks).
- The registration functions themselves (
registerGovernorChatActions, registerDiscoverAttemptChatActions)
are already idempotent (guarded by sharedRegistrationDone-style flags in their respective src/lib/*
modules per their own doc comments) — do not add new idempotency logic in the plugin files; just call the
same function from both hooks.
- No change to
configureServer's existing behavior, to the registration modules under src/lib/, or to
any other plugin file.
Deliverables
Test Coverage Requirements
apps/** is outside this repo's Codecov coverage.include scope (confirmed in PR #7082's own body), so
this is not gated by codecov/patch. The actual bar is apps/loopover-miner-ui's own Vitest suite
(npm test in that workspace) passing, plus the new regression test above actually failing against the
current code (which only wires configureServer) before the fix and passing after.
Expected Outcome
Chat-issued governor pause/resume and discover/attempt commands work identically under npm run dev and
under npm run build && npm run preview (and therefore under the systemd-deployed persistent service) —
matching the README's existing claim that every vite-*-api.ts plugin "register[s] for both
configureServer and configurePreviewServer, so nothing extra is needed beyond the build step."
Links & Resources
apps/loopover-miner-ui/vite-chat-governor-actions.ts, apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts,
apps/loopover-miner-ui/vite-chat-api.ts (the sibling pattern to mirror), apps/loopover-miner-ui/vite.config.ts,
apps/loopover-miner-ui/README.md ("Running as a persistent service"),
systemd/loopover-miner-ui.service.example, #6521, #6837.
Context
apps/loopover-miner-ui/README.md's "Running as a persistent service" section states, as the documentedcontract for this app's production deployment path:
systemd/loopover-miner-ui.service.example— the repo's own recommended persistent-deployment unit for afleet/bare-host operator — runs exactly this:
npm run buildthennpm run preview.vite previewonlyruns a plugin's
configurePreviewServerhook, neverconfigureServer.Every one of the 12 local Vite plugins wired in
vite.config.tsimplements both hooks — confirmed bygrep across every
vite-*-api.tsfile — except two:vite-chat-governor-actions.ts'schatGovernorActionsPlugin()— onlyconfigureServer()vite-chat-discover-attempt-actions.ts'schatDiscoverAttemptActionsPlugin()— onlyconfigureServer()Both plugins exist solely to call
registerGovernorChatActions()/registerDiscoverAttemptChatActions()on server start, populating the shared chat-action registry (
packages/loopover-miner/lib/chat-action-registry.js)that the chat rail's governor pause/resume and discover/attempt commands dispatch through (#6521, #6837).
Because neither plugin implements
configurePreviewServer, that registration call never runs undervite preview— i.e. never runs under the exact deployment mode the README documents and the systemdunit uses. An operator running the dashboard as a persistent service can still read the read-only ledgers/
run-history/portfolio views and the read-only chat stream (
vite-chat-api.ts, which does implement bothhooks) — but any chat-issued "pause the governor" / "resume the governor" / "release owner/repo" /
"requeue owner/repo #12" / discover / attempt command dispatches against an empty registry, which
dispatchChatActionreports asstatus: "unknown_action"(rendered to the operator as "Couldn't{pause/release/…}: action is not registered.") — a confusing, silent-looking failure with no indication
it's a
vite preview-only gap, since the exact same command works fine undernpm run dev.Requirements
chatGovernorActionsPlugin()(vite-chat-governor-actions.ts) andchatDiscoverAttemptActionsPlugin()(vite-chat-discover-attempt-actions.ts) must each implementconfigurePreviewServer(server)that performs the exact same registration call their existingconfigureServer(server)does, matching the shape every other plugin in this app already uses (e.g.vite-chat-api.ts'schatApiPlugin—attach()called from both hooks).registerGovernorChatActions,registerDiscoverAttemptChatActions)are already idempotent (guarded by
sharedRegistrationDone-style flags in their respectivesrc/lib/*modules per their own doc comments) — do not add new idempotency logic in the plugin files; just call the
same function from both hooks.
configureServer's existing behavior, to the registration modules undersrc/lib/, or toany other plugin file.
Deliverables
vite-chat-governor-actions.tsregisters on bothconfigureServerandconfigurePreviewServervite-chat-discover-attempt-actions.tsregisters on bothconfigureServerandconfigurePreviewServerconfigureServer/configurePreviewServerboth call the middleware-attach function — see e.g.vite-chat-api.ts'stest) proving the governor and discover/attempt registration functions are invoked when only
configurePreviewServeris exercised, not justconfigureServerTest Coverage Requirements
apps/**is outside this repo's Codecovcoverage.includescope (confirmed in PR #7082's own body), sothis is not gated by
codecov/patch. The actual bar isapps/loopover-miner-ui's own Vitest suite(
npm testin that workspace) passing, plus the new regression test above actually failing against thecurrent code (which only wires
configureServer) before the fix and passing after.Expected Outcome
Chat-issued governor pause/resume and discover/attempt commands work identically under
npm run devandunder
npm run build && npm run preview(and therefore under the systemd-deployed persistent service) —matching the README's existing claim that every
vite-*-api.tsplugin "register[s] for bothconfigureServerandconfigurePreviewServer, so nothing extra is needed beyond the build step."Links & Resources
apps/loopover-miner-ui/vite-chat-governor-actions.ts,apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts,apps/loopover-miner-ui/vite-chat-api.ts(the sibling pattern to mirror),apps/loopover-miner-ui/vite.config.ts,apps/loopover-miner-ui/README.md("Running as a persistent service"),systemd/loopover-miner-ui.service.example, #6521, #6837.