Context
apps/loopover-miner-ui/src/lib/use-polled-fetch.ts's usePolledFetch is the app's shared "live
refresh" mechanism (DEFAULT_POLL_INTERVAL_MS = 10s) — the Overview route (routes/index.tsx), the
Run History route, and Portfolio's own top summary card all use it, and the Overview route's header
comment describes the whole dashboard as "a live, read-only snapshot... refreshed automatically."
Two data sections in this same app never join that cadence and instead fetch exactly once, on mount:
apps/loopover-miner-ui/src/routes/ledgers.tsx's LedgersPage fetches both the ledger summary and
the governor pause state via a plain useEffect(() => { void loadLedgers().then(...) }, [loadLedgers])
(ledgers.tsx:420-428) and useEffect(() => { void loadGovernorPauseState().then(...) }, [loadGovernorPauseState])
(ledgers.tsx:430-438) — neither uses usePolledFetch, so claims, events, and governor state on this
page only update after a manual full page reload (or after the operator personally triggers a
pause/resume action, which only refreshes the governor half). Notably, routes/index.tsx's
OverviewClaimsCard fetches the SAME fetchLedgers data source and DOES poll it every 10s via
usePolledFetch — so the Overview page's claims card auto-updates while the dedicated Ledgers page
showing the same data does not.
apps/loopover-miner-ui/src/routes/portfolio.tsx's PortfolioPage polls its summary card correctly
(usePolledFetch(loadSummary, pollIntervalMs), portfolio.tsx:412), but the "Queue actions" table's
data (itemsResult) is fetched only via refreshItems inside a plain useEffect(() => { refreshItems(); }, [refreshItems])
(portfolio.tsx:414-420) — it re-fetches on mount and after the operator's own release/requeue action
(via refreshKey), but never on a timer, so a queue item that another process claims, completes, or
enqueues never appears in the actions table until the operator performs an action of their own or
reloads the page.
Requirements
LedgersPage's ledger summary fetch and governor pause-state fetch must each join the shared
usePolledFetch cadence (DEFAULT_POLL_INTERVAL_MS), matching every other data view in this app.
PortfolioPage's queue-actions items fetch (itemsResult) must also join the shared polling cadence,
independently of the summary card's own poll — a failure or slow response in one must not block the
other, matching the existing "independent StateBoundary per section" invariant this route already
documents for its two sections.
- The existing manual refresh after a successful release/requeue/pause/resume action must be preserved
(an operator's own action should still reflect immediately, not wait for the next tick) — this is
additive to the timer-based refresh, not a replacement for it.
- No change to the underlying fetch clients (
fetchLedgers, fetchGovernorPauseState,
fetchPortfolioQueueItems) or their API routes.
Deliverables
Test Coverage Requirements
apps/** is listed under ignore: in codecov.yml, so this app is not gated by Codecov's 99% patch
check — but the local npm run test:coverage invariant bar still applies, and the regression tests
above are required for all three sections. This is a data-freshness fix with no layout change, so no
before/after screenshot is required.
Expected Outcome
Every data view in the miner-ui dashboard — Overview, Run History, Portfolio's summary AND its queue
actions table, and the Ledgers page's ledger summary AND governor control — updates automatically on
the same shared cadence, with no view left requiring a manual page reload to reflect new local
activity.
Links & Resources
apps/loopover-miner-ui/src/lib/use-polled-fetch.ts, apps/loopover-miner-ui/src/routes/index.tsx
(OverviewClaimsCard, for the working comparison), apps/loopover-miner-ui/src/routes/ledgers.tsx
(lines 405-446), apps/loopover-miner-ui/src/routes/portfolio.tsx (lines 393-432).
Context
apps/loopover-miner-ui/src/lib/use-polled-fetch.ts'susePolledFetchis the app's shared "liverefresh" mechanism (
DEFAULT_POLL_INTERVAL_MS= 10s) — the Overview route (routes/index.tsx), theRun History route, and Portfolio's own top summary card all use it, and the Overview route's header
comment describes the whole dashboard as "a live, read-only snapshot... refreshed automatically."
Two data sections in this same app never join that cadence and instead fetch exactly once, on mount:
apps/loopover-miner-ui/src/routes/ledgers.tsx'sLedgersPagefetches both the ledger summary andthe governor pause state via a plain
useEffect(() => { void loadLedgers().then(...) }, [loadLedgers])(ledgers.tsx:420-428) and
useEffect(() => { void loadGovernorPauseState().then(...) }, [loadGovernorPauseState])(ledgers.tsx:430-438) — neither uses
usePolledFetch, so claims, events, and governor state on thispage only update after a manual full page reload (or after the operator personally triggers a
pause/resume action, which only refreshes the governor half). Notably,
routes/index.tsx'sOverviewClaimsCardfetches the SAMEfetchLedgersdata source and DOES poll it every 10s viausePolledFetch— so the Overview page's claims card auto-updates while the dedicated Ledgers pageshowing the same data does not.
apps/loopover-miner-ui/src/routes/portfolio.tsx'sPortfolioPagepolls its summary card correctly(
usePolledFetch(loadSummary, pollIntervalMs), portfolio.tsx:412), but the "Queue actions" table'sdata (
itemsResult) is fetched only viarefreshItemsinside a plainuseEffect(() => { refreshItems(); }, [refreshItems])(portfolio.tsx:414-420) — it re-fetches on mount and after the operator's own release/requeue action
(via
refreshKey), but never on a timer, so a queue item that another process claims, completes, orenqueues never appears in the actions table until the operator performs an action of their own or
reloads the page.
Requirements
LedgersPage's ledger summary fetch and governor pause-state fetch must each join the sharedusePolledFetchcadence (DEFAULT_POLL_INTERVAL_MS), matching every other data view in this app.PortfolioPage's queue-actions items fetch (itemsResult) must also join the shared polling cadence,independently of the summary card's own poll — a failure or slow response in one must not block the
other, matching the existing "independent StateBoundary per section" invariant this route already
documents for its two sections.
(an operator's own action should still reflect immediately, not wait for the next tick) — this is
additive to the timer-based refresh, not a replacement for it.
fetchLedgers,fetchGovernorPauseState,fetchPortfolioQueueItems) or their API routes.Deliverables
LedgersPage's ledger summary polls on the shared cadenceLedgersPage's governor pause-state polls on the shared cadencePortfolioPage's queue-actions items poll on the shared cadence, independent of the summary pollTest Coverage Requirements
apps/**is listed underignore:incodecov.yml, so this app is not gated by Codecov's 99% patchcheck — but the local
npm run test:coverageinvariant bar still applies, and the regression testsabove are required for all three sections. This is a data-freshness fix with no layout change, so no
before/after screenshot is required.
Expected Outcome
Every data view in the miner-ui dashboard — Overview, Run History, Portfolio's summary AND its queue
actions table, and the Ledgers page's ledger summary AND governor control — updates automatically on
the same shared cadence, with no view left requiring a manual page reload to reflect new local
activity.
Links & Resources
apps/loopover-miner-ui/src/lib/use-polled-fetch.ts,apps/loopover-miner-ui/src/routes/index.tsx(
OverviewClaimsCard, for the working comparison),apps/loopover-miner-ui/src/routes/ledgers.tsx(lines 405-446),
apps/loopover-miner-ui/src/routes/portfolio.tsx(lines 393-432).