feat(dashboard): align base refresh to wall-clock minute boundary

Instead of a fixed 60s interval from an arbitrary start offset, the base status
refresh now fires on each wall-clock minute boundary (:00), keeping the
minute-precision timestamp shown in the message accurate. Event-driven updates
(new message / agent activity) still refresh in between. Adds
msUntilNextMinuteBoundary with tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-08-21 13:01:02 +09:00
parent 65ef6e8830
commit d57dd68fc0
2 changed files with 29 additions and 1 deletions

View File

@@ -6,6 +6,7 @@ import {
createCoalescingTrigger,
editStatusMessageWithRetry,
formatStatusHeader,
msUntilNextMinuteBoundary,
getDashboardDuplicateCleanupIntervalMs,
renderUsageTable,
shouldPurgeDashboardChannelOnStart,
@@ -349,6 +350,15 @@ describe('editStatusMessageWithRetry', () => {
});
});
describe('msUntilNextMinuteBoundary', () => {
it('returns time to the next :00 boundary', () => {
expect(msUntilNextMinuteBoundary(120_000)).toBe(60_000); // exactly on boundary
expect(msUntilNextMinuteBoundary(120_000 + 15_000)).toBe(45_000); // 15s in
expect(msUntilNextMinuteBoundary(120_000 + 59_000)).toBe(1_000); // 59s in
expect(msUntilNextMinuteBoundary(120_000 + 1)).toBe(59_999); // 1ms in
});
});
describe('createCoalescingTrigger', () => {
it('coalesces a burst into one run and re-arms after firing', () => {
let runs = 0;

View File

@@ -840,6 +840,16 @@ export function createCoalescingTrigger(
};
}
/**
* Milliseconds from `nowMs` until the next wall-clock minute boundary (:00).
* Minute boundaries align across timezones (offsets are whole minutes), so this
* is timezone-agnostic. Exactly on a boundary returns a full minute.
*/
export function msUntilNextMinuteBoundary(nowMs: number): number {
const rem = nowMs % 60_000;
return rem === 0 ? 60_000 : 60_000 - rem;
}
export async function startUnifiedDashboard(
opts: UnifiedDashboardOptions,
): Promise<void> {
@@ -989,7 +999,15 @@ export async function startUnifiedDashboard(
);
}
setInterval(updateStatus, opts.statusUpdateInterval);
// Base periodic refresh fires on each wall-clock minute boundary (:00) so the
// minute-precision timestamp shown in the status message stays accurate.
const scheduleMinuteBoundaryUpdate = () => {
setTimeout(() => {
void updateStatus();
scheduleMinuteBoundaryUpdate();
}, msUntilNextMinuteBoundary(Date.now()));
};
scheduleMinuteBoundaryUpdate();
setInterval(() => {
if (!isRenderer || !statusMessageId) return;
void cleanupDashboardDuplicateMessages(opts, statusMessageId);