Context
packages/loopover-miner/lib/chat-action-dispatch.js's dispatchChatAction wraps its paramsValidator call in a try/catch, explicitly commented "fail closed" (a validator that throws is treated as a rejection, not a dispatch error) — but the very next line, await registered.handler(request), is not wrapped at all. Every other "must never crash" write path in this package wraps its fallible call the same way paramsValidator does here: pretooluse-hook.js:32, sentry.js:37/47, and iterate-loop.ts's safeAppendAttemptLogEvent.
Today this is latent — the dashboard-chat REST endpoint that will actually invoke dispatchChatAction end-to-end is still an open epic (#6839, governor action-dispatch, and its siblings). Once wired, a handler throwing (e.g. a network error from pauseGovernor/resumeGovernor, or any other registered action's network call) becomes an unhandled rejection instead of the module's own typed { ok, status } contract every other failure path already returns.
Requirements
- Wrap
await registered.handler(request) in a try/catch, returning the module's existing typed failure shape ({ ok: false, status: ..., action, error: ... }) on a thrown error, consistent with the paramsValidator catch immediately above it.
- Choose a
status value distinct from "invalid_params"/"unknown_action" (e.g. "handler_error") so a caller can tell a validation failure apart from a handler-execution failure.
- Do not change
paramsValidator's existing catch behavior or the registry lookup logic — this issue is scoped to the one unguarded handler(request) call.
Deliverables
Test Coverage Requirements
99%+ Codecov patch coverage on every changed line and branch, plus the regression test above.
Expected Outcome
A registered chat action's handler throwing (e.g. a network failure) fails closed with the module's own typed result shape, matching every other guarded call in this dispatch path, instead of surfacing as an unhandled rejection once the dashboard-chat epic wires this module to a live REST endpoint.
Links & Resources
packages/loopover-miner/lib/chat-action-dispatch.js — the call site. packages/loopover-miner/lib/pretooluse-hook.js:32, sentry.js:37,47 — sibling fail-closed catch precedent.
Context
packages/loopover-miner/lib/chat-action-dispatch.js'sdispatchChatActionwraps itsparamsValidatorcall in a try/catch, explicitly commented "fail closed" (a validator that throws is treated as a rejection, not a dispatch error) — but the very next line,await registered.handler(request), is not wrapped at all. Every other "must never crash" write path in this package wraps its fallible call the same wayparamsValidatordoes here:pretooluse-hook.js:32,sentry.js:37/47, anditerate-loop.ts'ssafeAppendAttemptLogEvent.Today this is latent — the dashboard-chat REST endpoint that will actually invoke
dispatchChatActionend-to-end is still an open epic (#6839, governor action-dispatch, and its siblings). Once wired, a handler throwing (e.g. a network error frompauseGovernor/resumeGovernor, or any other registered action's network call) becomes an unhandled rejection instead of the module's own typed{ ok, status }contract every other failure path already returns.Requirements
await registered.handler(request)in a try/catch, returning the module's existing typed failure shape ({ ok: false, status: ..., action, error: ... }) on a thrown error, consistent with theparamsValidatorcatch immediately above it.statusvalue distinct from"invalid_params"/"unknown_action"(e.g."handler_error") so a caller can tell a validation failure apart from a handler-execution failure.paramsValidator's existing catch behavior or the registry lookup logic — this issue is scoped to the one unguardedhandler(request)call.Deliverables
registered.handler(request)is wrapped in try/catch with a typed failure result on throw{ ok: false, ... }instead of an unhandled rejectionTest Coverage Requirements
99%+ Codecov patch coverage on every changed line and branch, plus the regression test above.
Expected Outcome
A registered chat action's handler throwing (e.g. a network failure) fails closed with the module's own typed result shape, matching every other guarded call in this dispatch path, instead of surfacing as an unhandled rejection once the dashboard-chat epic wires this module to a live REST endpoint.
Links & Resources
packages/loopover-miner/lib/chat-action-dispatch.js— the call site.packages/loopover-miner/lib/pretooluse-hook.js:32,sentry.js:37,47— sibling fail-closed catch precedent.