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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user