feat(webviews): report visible page loops
Summary
“cmuxDebug.loops reporting and hidden_loops==0 gate, shared visible page clock if absent.”
Adds a read-only window.cmuxDebug.loops() diagnostic to the shared desktop bootstrap, visible-page clock primitives, and a common hidden-page CSS animation pause. The browser regression exercises the real diagnostic and visibility listener across hide/show, asserting hidden_loops == 0.
The diagnostic counts registered page clocks and running infinite DOM animations.
window.cmuxDebug.loops is an intentional, read-only production diagnostic: it computes only when called, installs no listener or timer of its own, and exists so the live hidden_loops gate can read pages. It does not intercept arbitrary timers; existing clock consumers and ChangesStatus timers are separate follow-ups. parked exposes the host's initial pooled-page marker, not a new claim lifecycle. Native #24806 is separate.
Validation
Published head: d0022855faa5bd345e310175738d56c29dad3d0a.
cmux-ci run 41125624718 completed successfully: page-clock.test.ts and static-status-loops.test.ts recorded 12 passing tests, 0 failures, and 40 assertions. The receipt tested the published head exactly.
Performance
| metric | before | after | delta | harness |
|---|---|---|---|---|
| Running infinite CSS animations in hidden fixture | 9 | 0 | -9 | Same Chromium fixture, measurement run |
| Running infinite CSS animations in visible fixture | 9 | 9 | 0 | Same fixture and run |
Baseline CSS is byte-identical to 844a99b91c474ef412604e2412ab157377d9654c (SHA256 9a4e6a464ba411642bbe2c52605bce9f0db29254bdf4c6bf548823ac80a5b4b2). These observed numbers were collected at 7832b156; the fixture inputs are unchanged; the root selector broadens hidden pause coverage. The root-selector change is covered by the final-head focused browser run. This measures the CSS fixture only; no CPU, wakeup, or application-wide timer reduction is claimed.
Timer ownership and cleanup:
visibleIntervalclears its timeout when the document becomes hidden and starts a fresh deadline on show; disposal clears the timeout, removes the visibility listener, and unregisters the clock.- Shared second/minute clocks release their interval and listener when the last subscriber leaves.
- Diagnostics run only when called and add no polling timer or observer.
Before / after
Before/after fixture screenshot artifact: base-visible.png, head-visible.png, and the corresponding hidden captures.
I viewed the after image and the before fixture: the visible layout and styles are unchanged. These are synthetic CSS fixtures, not full application screenshots.
Changelog
Added visible-page loop diagnostics and shared timer primitives; hidden desktop pages pause CSS animations.
Agent session: agent session