refactor(webviews): migrate clock consumers to page clock
Summary
Request: “B (clock consumers) and C (changes-status) as their own PRs from current base.”
Migrates the existing WorkingFor, SubagentGroup, and relative-time clock consumers onto PR A's shared visible page clock. Hidden pages now release these consumers' repeating work through the same page-owned lifecycle; showing a page starts a fresh visible deadline.
Prior art read: #22485 (SubagentGroup session clock offset), #23995 (shared page animation infrastructure), #24017/#25201 (open SubagentGroup settings work; this PR changes no pane behavior). PR A #28254 is merged. Native #24806 remains separate.
No visual change: timer scheduling only; rendering is byte-identical (same text, same cadence while visible).
Validation
-
Final-head
cmux-ci testatbffe8fe288db556db9410da390e0764cd38f074b: 36 tests passed, 0 failed, followed by webviews typecheck with 0 errors in 3,319 files, in one run 41138682314. Command:cd webviews && bun test test/subagent-tray.test.tsx test/page-clock-consumers.test.ts && bun run typecheck. The tray regression records the shared 1000 ms timeout and asserts one visible, none hidden, one on show, and none after unmount. -
Focused
cmux-ci testpassed all 4 tests inpage-clock.test.ts,page-clock-consumers.test.ts, andsubagent-peer-clock.test.ts, plus lint (0 warnings/errors), at17251344a917f038adfa7d0560b20b6f2a979165: run 41134686458. -
The consumer test asserts these modules use the shared page clock and own no private repeating timer or visibility listener.
-
Every shared timer stops when the page hides or its last subscriber unsubscribes; cleanup removes its timeout and visibility listener.
Performance
| metric | before | after | delta | harness |
|---|---|---|---|---|
| Private repeating-timer registration sites in migrated consumers | 3 | 0 | -3 | Same Bun source harness at base/head; cmux-ci run 41132299009 |
| Hidden timer allocations (source-derived lifecycle, not runtime benchmark) | 2 | 0 | -2 | Same base/head source inspection with the active subscriptions described below |
| Visible elapsed-label cadence | 1,000 ms | 1,000 ms | unchanged | Same source comparison: WorkingFor/SubagentGroup at base versus subscribePageSecond at head |
| Visible relative-time cadence | Next minute boundary + 5 ms | Next minute boundary + 5 ms | unchanged | Same source comparison: clock.ts at base versus aligned subscribePageMinute at head |
The metric counts the three consumer-local setTimeout/setInterval registration sites present at the base and none after migration. It does not claim CPU or wakeup savings beyond this timer ownership change. The shared page clock's one visible timer per cadence and its stop condition are covered by PR A's browser harness.
Hidden behavior from source inspection: with one active WorkingFor, one minute-clock subscriber and one live tray, base retains two timers while hidden (WorkingFor and the minute clock; the tray already pauses), while head retains zero. This is a lifecycle analysis, not a before/after runtime measurement. The combined tray/page-clock-consumers run linked above validates the timeout lifecycle regression.
Cleanup
useMinuteClock,useTicking, anduseNowunsubscribe on component cleanup.- The shared second/minute page clocks clear their timeout, remove
visibilitychange, and unregister when their final subscriber leaves. - Hidden pages own no repeating timer; showing a page schedules a fresh deadline.
Visual evidence
I viewed both after images and compared them with the matching before images. These are 2× crops of synthetic gallery fixtures in light and dark, captured at production-equivalent head 17251344a917f038adfa7d0560b20b6f2a979165 (the final commit changes only the tray test) in gallery run 41134637989. The timer migration changes no visible layout or styling; running-spinner phase can differ between captures.
| Theme | Before | After |
|---|---|---|
| Light | ![]() | ![]() |
| Dark | ![]() | ![]() |
CI warning: SigningHooksInStepWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check fails with the same missing upstream hook hash on base 901c7698. It is a release-notary guard and does not hide web test or compile signal.
Agent session: agent session
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Migrates WorkingFor, SubagentGroup, and the relative-time clock consumers onto the shared page clock so hidden pages no longer run private repeating timers.
- Consumers now use
subscribePageMinute/usePageNowinstead of their ownsetIntervalticks andvisibilitychangelisteners. - Hidden pages stop all timer work; showing a page schedules a fresh visible deadline, and unmounting unsubscribes from the shared clock.
- Adds
page-clock-consumers.test.tsasserting the migrated modules use the shared clock and declare no private repeating timer or visibility listener.



