feat(overview): scope the first range option to today, not the last 24h

The Insights page's first range option read "Last 24 hours" while the
ledger it scopes is day-grained, so the rolling window and the day floor
underneath it disagreed: past midnight the 24h window still admitted
yesterday's ledger day whole, and before 1am it admitted only part of it.
"Today" is the local calendar day from midnight — the window the day-
granular heatmap beside it already draws.

`rangeStartOf` opens the window on the local midnight by date-field
arithmetic (the harness's own `setHours(0, 0, 0, 0)` idiom, as in
`startOfLocalDay`), which stays exact across DST-short and DST-long days
where an epoch-ms subtract drifts into the neighbouring day. The harness
helper itself is only reachable through a deep
`@deepseek-ai/dsh-client-ui-chat/src/*` path — a package this plugin does
not depend on — so the idiom is mirrored at its one call site instead of
taking the dependency.

Both locales carry the shorter 今天 / Today; the deprecated
`ov.range.24h` key is gone rather than aliased.

The specs that anchored time on raw epoch offsets silently assumed UTC
(`11 * 86_400_000`), and the panel fixture at `NOW - 1h` would have
failed whenever the suite runs in the first hour after midnight; every
boundary is now anchored on a local `Date`, so the day floor is proved
identically in every timezone.
This commit is contained in:
bowenliang123 committed 2026-10-09 15:06:22 +08:00
1 parent 9d2c812638
commit d3f2399440
5 files changed
+44 -26

No files matched your search

+1 -1
View File
@@ -75,7 +75,7 @@ export interface OverviewPanelProps {
useWorkspaces?: unknown
}
const RANGES: readonly OverviewRange[] = ['24h', '7d', '30d', 'all']
const RANGES: readonly OverviewRange[] = ['today', '7d', '30d', 'all']
const SORTS: readonly OverviewSort[] = ['recent', 'tokens', 'context']
/** The heatmap's depth metrics, in toggle order (steps is the default). */
const METRICS: readonly HeatMetric[] = ['sessions', 'steps']
+2 -2
View File
@@ -350,7 +350,7 @@ export const DICT_ZH: Record<string, string> = {
'ov.title': '上下文洞察',
'ov.unavailable': '会话列表暂不可用',
'ov.range.label': '统计范围',
'ov.range.24h': '近 24 小时',
'ov.range.today': '今天',
'ov.range.7d': '近 7 天',
'ov.range.30d': '近 30 天',
'ov.range.all': '全部',
@@ -789,7 +789,7 @@ export const DICT_EN: Record<string, string> = {
'ov.title': 'Context Insights',
'ov.unavailable': 'The session list is unavailable',
'ov.range.label': 'Range',
'ov.range.24h': 'Last 24 hours',
'ov.range.today': 'Today',
'ov.range.7d': 'Last 7 days',
'ov.range.30d': 'Last 30 days',
'ov.range.all': 'All',
+15 -5
View File
@@ -328,11 +328,20 @@ function mergeSkillTallies(a: ActivityDay['skills'], b: NonNullable<ActivityDay[
// ---- range / filter / sort -------------------------------------------------
export type OverviewRange = '24h' | '7d' | '30d' | 'all'
export type OverviewRange = 'today' | '7d' | '30d' | 'all'
/** The range window's start instant (epoch ms), or null for "all". */
/**
* The range window's start instant (epoch ms), or null for "all". "Today"
* opens on the local calendar day's midnight — date-field arithmetic (the
* harness's own `setHours(0, 0, 0, 0)` idiom) keeps a DST-short or long day
* exact, where an epoch-ms subtract would drift into the neighbouring day.
*/
export function rangeStartOf(range: OverviewRange, now: number): number | null {
if (range === '24h') return now - 86_400_000
if (range === 'today') {
const midnight = new Date(now)
midnight.setHours(0, 0, 0, 0)
return midnight.getTime()
}
if (range === '7d') return now - 7 * 86_400_000
if (range === '30d') return now - 30 * 86_400_000
return null
@@ -443,8 +452,9 @@ export type SkillSort = 'loads' | 'recent'
/**
* The scope's ledger-day predicate: the heatmap's pinned day admits exactly
* that day, else the range's day floor admits its own and later days (the
* ledger is day-grained: the 24h window's start day counts whole, the same
* resolution the heatmap reads at); 'all' admits every day.
* ledger is day-grained: "today" starts at this very day's key and the 7d/30d
* windows admit their start day whole, the same resolution the heatmap reads
* at); 'all' admits every day.
*/
function skillDayPredicate(opts: { range: OverviewRange; day: string | null }, now: number): (key: string) => boolean {
if (opts.day !== null) return key => key === opts.day
@@ -20,6 +20,8 @@ const PROVIDERS = {
const NOW = Date.now()
const TODAY = dayKeyOf(NOW) ?? ''
const YESTERDAY = dayKeyOf(NOW - 86_400_000) ?? ''
/** Local midnight of NOW — the "today" range's floor, so today's session stays inside it whatever hour the spec runs. */
const MIDNIGHT = new Date(new Date(NOW).setHours(0, 0, 0, 0)).getTime()
/** A wire-shaped timeline head. */
function timeline(over: Record<string, unknown> = {}): Record<string, unknown> {
@@ -47,7 +49,7 @@ function sessionsSnapshot(): { ids: string[]; byId: Record<string, Record<string
a: {
displayTitle: 'alpha session',
cwd: '/repo/alpha',
updatedAt: NOW - 3_600_000,
updatedAt: MIDNIGHT,
running: true,
projectionValues: {
contextTimeline: timeline(),
@@ -207,8 +209,8 @@ describe('OverviewPanel', () => {
await click(rangeButtons[3]) // All
assert.equal(queryAll(m.container, '.lc-ov-grid > .lc-ov-session').length, 3, 'the stale session joins')
assert.equal(queryAll(m.container, '.lc-stat-value')[0].textContent, '3')
await click(rangeButtons[0]) // Last 24 hours
assert.equal(queryAll(m.container, '.lc-ov-grid > .lc-ov-session').length, 1, 'only the hour-fresh session stays')
await click(rangeButtons[0]) // Today
assert.equal(queryAll(m.container, '.lc-ov-grid > .lc-ov-session').length, 1, "only today's session stays")
await click(rangeButtons[1]) // Last 7 days
assert.equal(queryAll(m.container, '.lc-ov-grid > .lc-ov-session').length, 1, 'only the freshest stays')
await click(rangeButtons[2]) // back to 30d
+21 -15
View File
@@ -195,8 +195,9 @@ describe('billedOf / turnsOf', () => {
describe('rangeStartOf', () => {
test('each window’s start instant; all is unbounded', () => {
const now = 31 * 86_400_000
assert.equal(rangeStartOf('24h', now), now - 86_400_000)
// Local anchors, so the calendar-day window reads the same in every zone.
const now = new Date(2026, 8, 20, 12).getTime()
assert.equal(rangeStartOf('today', now), new Date(2026, 8, 20).getTime(), 'today opens on the local midnight')
assert.equal(rangeStartOf('7d', now), now - 7 * 86_400_000)
assert.equal(rangeStartOf('30d', now), now - 30 * 86_400_000)
assert.equal(rangeStartOf('all', now), null)
@@ -204,28 +205,30 @@ describe('rangeStartOf', () => {
})
describe('filterRows', () => {
const midnight = new Date(2026, 8, 11).getTime()
const now = midnight + 12 * 3_600_000
const rows = [
rowOf({ id: 'old', title: 'ancient logs', updatedAt: 100 }),
rowOf({ id: 'new', title: 'fresh fix', cwd: '/repo/app', updatedAt: 10 * 86_400_000 }),
rowOf({ id: 'old', title: 'ancient logs', updatedAt: midnight - 20 * 86_400_000 }),
rowOf({ id: 'new', title: 'fresh fix', cwd: '/repo/app', updatedAt: now - 2 * 3_600_000 }),
rowOf({
id: 'active',
title: 'busy bee',
updatedAt: 10 * 86_400_000,
updatedAt: now - 2 * 3_600_000,
activity: { days: { '2026-09-16': { tokens: 5, requests: 1 }, '2026-09-10': { tokens: 0, requests: 0 } } },
}),
]
const now = 11 * 86_400_000
test('the range window filters by last activity', () => {
assert.deepEqual(filterRows(rows, { range: '24h', day: null, query: '' }, now).map(r => r.id), ['new', 'active'])
assert.deepEqual(filterRows(rows, { range: 'today', day: null, query: '' }, now).map(r => r.id), ['new', 'active'])
assert.deepEqual(filterRows(rows, { range: '7d', day: null, query: '' }, now).map(r => r.id), ['new', 'active'])
assert.deepEqual(filterRows(rows, { range: 'all', day: null, query: '' }, now).map(r => r.id), ['old', 'new', 'active'])
// The 24h window's own boundary: inside by minutes, outside by an hour.
const hRows = [
rowOf({ id: 'fresh', title: 'just now', updatedAt: now - 2 * 3_600_000 }),
rowOf({ id: 'yesterday', title: 'a day ago', updatedAt: now - 25 * 3_600_000 }),
// Today's own boundary: hours in, one minute before the local midnight out.
const dRows = [
rowOf({ id: 'in', title: 'this morning', updatedAt: now - 2 * 3_600_000 }),
rowOf({ id: 'out', title: 'last night', updatedAt: midnight - 60_000 }),
]
assert.deepEqual(filterRows(hRows, { range: '24h', day: null, query: '' }, now).map(r => r.id), ['fresh'])
assert.deepEqual(filterRows(dRows, { range: 'today', day: null, query: '' }, now).map(r => r.id), ['in'])
assert.deepEqual(filterRows(dRows, { range: '7d', day: null, query: '' }, now).map(r => r.id), ['in', 'out'], 'last night is still inside the week')
})
test('the day pin keeps only sessions contributing to that day', () => {
@@ -744,10 +747,13 @@ describe('skillLoadsOf', () => {
[day(1)]: { tokens: 1, requests: 1, skills: { tdd: { n: 3, last: NOW - 2000 }, grill: { n: 1, last: NOW - 500 } } },
}),
]
assert.deepEqual(skillLoadsOf(rows, { range: '24h', day: null }, NOW), [
assert.deepEqual(skillLoadsOf(rows, { range: '7d', day: null }, NOW), [
{ name: 'tdd', loads: 5, sessions: 2, last: NOW - 1000 },
{ name: 'grill', loads: 1, sessions: 1, last: NOW - 500 },
], 'the 10-days-ago entries fall below the floor')
assert.deepEqual(skillLoadsOf(rows, { range: 'today', day: null }, NOW), [
{ name: 'tdd', loads: 2, sessions: 1, last: NOW - 1000 },
], "today's floor admits this very day only")
assert.deepEqual(skillLoadsOf(rows, { range: 'all', day: null }, NOW), [
{ name: 'tdd', loads: 10, sessions: 2, last: NOW - 1000 },
{ name: 'grill', loads: 1, sessions: 1, last: NOW - 500 },
@@ -845,8 +851,8 @@ describe('rowLoadedSkill', () => {
},
},
})
assert.equal(rowLoadedSkill(row, 'tdd', { range: '24h', day: null }, NOW), true, 'loaded today — inside the 24h floor')
assert.equal(rowLoadedSkill(row, 'grill', { range: '24h', day: null }, NOW), false, 'the 10-days-ago load falls below the floor')
assert.equal(rowLoadedSkill(row, 'tdd', { range: 'today', day: null }, NOW), true, "loaded today — inside today's floor")
assert.equal(rowLoadedSkill(row, 'grill', { range: 'today', day: null }, NOW), false, 'the 10-days-ago load falls below the floor')
assert.equal(rowLoadedSkill(row, 'grill', { range: 'all', day: null }, NOW), true)
assert.equal(rowLoadedSkill(row, 'tdd', { range: 'all', day: day(1) }, NOW), false, 'the pinned day admits only itself')
assert.equal(rowLoadedSkill(row, 'grill', { range: 'all', day: day(10) }, NOW), true)