fix(web): day-scale duration formatting in network timeline

fmtDuration rolls hours into days above 24h ("768d 8h" instead of
"18440h 58m") - minutes are noise at day scale. Surfaced by the first
fleet session with a CMOS clock years off (0bd4af1c, 2024 -> 2026 step);
also keeps the slanted clock-marker label inside its headroom. Test
pinned on the real fleet delta.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-08-21 21:05:04 +02:00
parent f15e115dea
commit a2f448cad0
2 changed files with 20 additions and 1 deletions
@@ -91,6 +91,22 @@ describe('clock-era correction', () => {
expect(clockMarker!.detail).toContain('before agent start');
});
it('formats day-scale steps in days (CMOS clock years off)', () => {
// Real fleet case 0bd4af1c: clock stepped +66,387,532,699 ms (2024 → 2026).
const events = [
ethernetInfo(1, 0),
ev(2, 'system_clock_changed', 5 * MIN, {
timeDeltaMs: 66_387_532_699,
backfilled: true,
timeCreated: iso(2 * MIN),
}),
ev(3, 'log_entry', 10 * MIN),
];
const model = buildNetworkModel(session, events)!;
const marker = model.lifeMarkers.find((m) => m.label.startsWith('Clock'));
expect(marker!.label).toBe('Clock +768d 8h');
});
it('ignores sub-30s steps (NTP nudges)', () => {
const events = [
ethernetInfo(1, 0),
@@ -130,7 +130,10 @@ export function fmtDuration(ms: number): string {
const m = Math.floor(s / 60);
if (m < 60) return `${m}m ${s % 60}s`;
const h = Math.floor(m / 60);
return `${h}h ${m % 60}m`;
if (h < 24) return `${h}h ${m % 60}m`;
// Day scale (e.g. a CMOS clock jumping years): minutes are noise here.
const d = Math.floor(h / 24);
return `${d}d ${h % 24}h`;
}
export function segmentTitle(seg: NetSegment): string {