Skip to content

fix(ui): background polls no longer re-flash loaded content — gate on "no data yet", honest stale banners, store-owned auto-expand, bare-gate ratchet (#1927) - #2371

Merged
vybe merged 1 commit into
devfrom
feature/1927-poll-reflash
Aug 21, 2026
Merged

fix(ui): background polls no longer re-flash loaded content — gate on "no data yet", honest stale banners, store-owned auto-expand, bare-gate ratchet (#1927)#2371
vybe merged 1 commit into
devfrom
feature/1927-poll-reflash

Conversation

@webmixgamer

Copy link
Copy Markdown
Contributor

Summary

  • Four surfaces gated their loading UI on a fetch-in-flight flag, so every 10–15 s background poll swapped rendered content for a spinner (MobileAdmin ×4, SchedulesPanel execution history, InfoPanel on Start/Restart), and Operations re-expanded a deliberately collapsed card on every poll delta (design-system p13/p14/p15/p5).
  • The rule now lives in ONE pure module — utils/loadingState.js::viewState({loading, hasLoaded, error, count}) → loading|failed|empty|ready + stale — and the four surfaces call it: loading only before the first data, LoadFailed (never the empty copy) on a failed first fetch, and a sibling InlineError stale banner ("Couldn't refresh … — showing data from HH:MM", Retry/Dismiss) when a poll fails with data on screen. Background refreshes swap values in place.
  • Operations' auto-expand becomes a store rule (autoExpandArmed + maybeAutoExpand): once per armed episode, disarmed by any human toggle, re-armed when the queue drains — a poll delta / WS arrival / remount can never override a collapse.
  • In blast radius and required for correctness: two pre-existing /m shape bugs — GET /api/operator-queue ({items,count}) and GET /api/agents/execution-stats ({agents}) were parsed as arrays, a TypeError on every poll; the /m Queue tab had always read "No pending items".
  • Loading-gate ratchet (the mechanical form of the sweep PR fix(ui): stop the Schedules list unmounting on refetch so toggling never scrolls to top (#1634) #1939 measured): scripts/scan-loading-gates.mjs + loading-gate-baseline.json + tests/unit/loadingGateRatchet.spec.js freeze the remaining 80 bare v-if="<loading flag>" gates in 63 files; counts may only shrink (this PR: MobileAdmin 4→0, InfoPanel 1→0, SchedulesPanel 2→1 — its remaining hit is a form-submit control spinner, refactor: replace bespoke spinners with the standard scanline loading motion (design-system p12) #1921's class).

Changes

  • src/frontend/src/utils/loadingState.js (new) · src/frontend/src/views/MobileAdmin.vue · src/frontend/src/components/SchedulesPanel.vue · src/frontend/src/components/InfoPanel.vue · src/frontend/src/stores/operatorQueue.js · src/frontend/src/views/Operations.vue · src/frontend/src/components/operator/QueueCard.vue (test hooks: data-testid, aria-expanded, collapse aria-label)
  • Ratchet: src/frontend/scripts/scan-loading-gates.mjs, src/frontend/loading-gate-baseline.json, src/frontend/tests/unit/loadingGateRatchet.spec.js
  • Tests: src/frontend/tests/unit/loadingState.spec.js, src/frontend/tests/unit/operatorQueueAutoExpand.spec.js, src/frontend/e2e/background-refresh-invisible.spec.js
  • Docs: feature-flows/{mobile-admin-pwa,operating-room,agent-info-display,scheduling}.md, design-system.md §9 (layer 4), design-system-contract.md self-check, CLAUDE.md rule 10, learnings.md (2 entries), docs/security-reports/cso-diff-2026-08-21-1927-poll-reflash.md

Test Plan

  • npm run test:unit: 56 files / 1,176 tests green (3 new specs: 26 + 6 + ratchet)
  • Playwright e2e/background-refresh-invisible.spec.js against a live stack: 14 passed (9 @smoke, 5 @interactive); pre-fix run (source stashed, spec kept): 7 fail for behaviour reasons — "Loading…" re-flash on /m, .ops-card count 0 from the shape bug, no failed state on a failed first load, Operations re-expanding a collapsed card
  • e2e/honest-failed-states.spec.js (bug: fetch failures render as empty states or vanish silently (design-system p15/p25) #1926) + e2e/schedules-toggle-scroll.spec.js (bug: toggling a schedule scrolls the Schedules tab back to the top #1634) still green
  • npm run check:tokens OK; raw-color counts identical to dev on every touched file; vite build OK
  • Manual verification on a local instance (operator): Operations collapse survives polls; /m Agents/Ops/System no longer flash and the Queue tab shows pending items; Schedules execution history polls in place; Info tab keeps content across Start/Restart
  • /review (0 critical; 2 informational auto-fixed) · /cso --diff (0 findings)

Out of scope, tracked separately: the ~30-file bare-gate sweep (trinity-dev DEBT_INBOX debt:2026-08-21-bare-loading-gates-sweep), spinner→scanline adoption (#1921, deferral recorded there), and the /m approval-card payload/parity bug found during verification (#2370).

Fixes #1927

🤖 Generated with Claude Code

https://claude.ai/code/session_01WHRjk7Hdswaici8pmuaeRd

…data yet", honest stale banners, store-owned auto-expand, bare-gate ratchet (#1927)

Four surfaces gated their loading UI on a fetch-in-flight flag, so every
10–15 s poll swapped rendered content for a spinner (MobileAdmin ×4,
SchedulesPanel execution history, InfoPanel on Start/Restart), and
Operations re-expanded a deliberately collapsed card on every poll delta.

- utils/loadingState.js (pure): viewState / staleBannerMessage / listFrom /
  decideAutoExpand — the one home of "loading means no data yet" (p13/p14/p15)
- MobileAdmin: hasLoaded / fetchError / lastLoadedAt per dataset,
  Promise.allSettled, LoadFailed on a failed first fetch, sibling InlineError
  stale banner; fixes two pre-existing shape bugs (operator-queue {items,count}
  and execution-stats {agents} were parsed as arrays — a TypeError on every
  poll; the /m queue tab had always read "No pending items")
- SchedulesPanel: per-schedule executionsLoading / Error / LoadedAt, honest
  chain, stale banner, agent-switch reset + poll stop
- InfoPanel: re-gated on templateInfo === null; the Start/Restart refetch is silent
- operatorQueue store: autoExpandArmed + maybeAutoExpand (disarmed by any
  human toggle, re-armed when the queue drains); Operations watches the open set
- Loading-gate ratchet: scripts/scan-loading-gates.mjs + loading-gate-baseline.json
  + tests/unit/loadingGateRatchet.spec.js freeze the remaining 80 bare gates in
  63 files (MobileAdmin 4→0, InfoPanel 1→0, SchedulesPanel 2→1)
- Tests: vitest 56 files / 1176 (3 new specs); Playwright
  e2e/background-refresh-invisible.spec.js (14 tests — 7 fail on the pre-fix
  code for behaviour reasons); #1926 / #1634 specs still green
- Docs: 4 flow docs, design-system §9 layer 4, contract self-check, CLAUDE.md
  rule 10, 2 learnings entries, cso --diff report (0 findings)

Fixes #1927

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHRjk7Hdswaici8pmuaeRd
@webmixgamer webmixgamer added the ui PR touches the frontend UI — triggers Playwright e2e tests label Aug 21, 2026
@webmixgamer
webmixgamer requested review from dolho and vybe August 21, 2026 15:33

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-pr — all categories green, falsified-before-fix e2e coverage, ratchet coherent end-to-end. Approving.

@vybe
vybe merged commit b7bac09 into dev Aug 21, 2026
31 of 36 checks passed
@webmixgamer
webmixgamer deleted the feature/1927-poll-reflash branch August 21, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui PR touches the frontend UI — triggers Playwright e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants