Context
apps/loopover-miner-ui/src/routes/portfolio.tsx's runQueueAction (~line 183) wraps the release/requeue button handlers:
const runQueueAction = (action: () => Promise<unknown>) => {
setActionPending(true);
void action().then(() => {
setRefreshKey((key) => key + 1);
refreshItems();
setActionPending(false);
});
};
releaseItem/requeueItem (lib/portfolio-queue-actions.ts) resolve to a typed PortfolioQueueActionResult — either {ok: true, entry} or {ok: false, error} — and real failure modes exist and are tested at the client-library level (e.g. a 409 queue_entry_not_in_progress/queue_entry_not_requeuable). But runQueueAction types its callback as Promise<unknown> and discards the resolved value entirely: on failure it still just re-enables the button and silently refreshes the list, with no user-visible error.
This is the one inconsistent half of a single feature (#4857, "Add real actions to the miner-ui"): the governor half in the sibling route ledgers.tsx handles this correctly — runGovernorAction threads the resolved GovernorPauseStateResult into pauseState, which GovernorControlSection renders via its !result.ok error branch. The portfolio-queue half never got the equivalent treatment. portfolio-queue-actions.test.tsx's only PortfolioPage-level action test (~lines 98-118) only exercises the success case, confirming the failure path is untested as well as unhandled.
Requirements
- Change
runQueueAction to thread the resolved PortfolioQueueActionResult through to component state (mirroring runGovernorAction's pattern exactly), instead of discarding it.
- Render the
{ok: false, error} case visibly to the operator — reuse GovernorControlSection's existing error-rendering convention (role="alert", same visual treatment) rather than inventing a new one.
- On failure, do NOT silently refresh the list as if the action succeeded — only refresh/advance
refreshKey on {ok: true}.
- Preserve the existing success-path behavior (refresh +
actionPending reset) exactly as-is.
Deliverables
Test Coverage Requirements
99%+ patch coverage (branch-counted), including the new failure-rendering branch.
Expected Outcome
A failed portfolio-queue release/requeue action (e.g. the entry already moved out of in_progress) shows a clear, visible error to the operator instead of silently doing nothing — matching the governor pause/resume controls' existing error-handling behavior in the sibling route.
Links & Resources
apps/loopover-miner-ui/src/routes/portfolio.tsx (runQueueAction, the bug)
apps/loopover-miner-ui/src/routes/ledgers.tsx (runGovernorAction, the pattern to mirror)
apps/loopover-miner-ui/src/lib/portfolio-queue-actions.ts (PortfolioQueueActionResult type, real failure modes)
apps/loopover-miner-ui/src/portfolio-queue-actions.test.tsx (existing success-only PortfolioPage action test to extend)
#4857 (the PR that added both the governor and portfolio-queue action surfaces)
Context
apps/loopover-miner-ui/src/routes/portfolio.tsx'srunQueueAction(~line 183) wraps the release/requeue button handlers:releaseItem/requeueItem(lib/portfolio-queue-actions.ts) resolve to a typedPortfolioQueueActionResult— either{ok: true, entry}or{ok: false, error}— and real failure modes exist and are tested at the client-library level (e.g. a 409queue_entry_not_in_progress/queue_entry_not_requeuable). ButrunQueueActiontypes its callback asPromise<unknown>and discards the resolved value entirely: on failure it still just re-enables the button and silently refreshes the list, with no user-visible error.This is the one inconsistent half of a single feature (
#4857, "Add real actions to the miner-ui"): the governor half in the sibling routeledgers.tsxhandles this correctly —runGovernorActionthreads the resolvedGovernorPauseStateResultintopauseState, whichGovernorControlSectionrenders via its!result.okerror branch. The portfolio-queue half never got the equivalent treatment.portfolio-queue-actions.test.tsx's onlyPortfolioPage-level action test (~lines 98-118) only exercises the success case, confirming the failure path is untested as well as unhandled.Requirements
runQueueActionto thread the resolvedPortfolioQueueActionResultthrough to component state (mirroringrunGovernorAction's pattern exactly), instead of discarding it.{ok: false, error}case visibly to the operator — reuseGovernorControlSection's existing error-rendering convention (role="alert", same visual treatment) rather than inventing a new one.refreshKeyon{ok: true}.actionPendingreset) exactly as-is.Deliverables
runQueueActioninportfolio.tsxthreads the real result instead of discarding itrefreshKey/re-fetch as if it succeeded — extendportfolio-queue-actions.test.tsx's existingPortfolioPage-level test, which currently only covers the success caseTest Coverage Requirements
99%+ patch coverage (branch-counted), including the new failure-rendering branch.
Expected Outcome
A failed portfolio-queue release/requeue action (e.g. the entry already moved out of
in_progress) shows a clear, visible error to the operator instead of silently doing nothing — matching the governor pause/resume controls' existing error-handling behavior in the sibling route.Links & Resources
apps/loopover-miner-ui/src/routes/portfolio.tsx(runQueueAction, the bug)apps/loopover-miner-ui/src/routes/ledgers.tsx(runGovernorAction, the pattern to mirror)apps/loopover-miner-ui/src/lib/portfolio-queue-actions.ts(PortfolioQueueActionResulttype, real failure modes)apps/loopover-miner-ui/src/portfolio-queue-actions.test.tsx(existing success-onlyPortfolioPageaction test to extend)#4857(the PR that added both the governor and portfolio-queue action surfaces)