mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
fix(doctor): probe-health 'Latest' reports the true newest run — sort audit events chronologically (#3366)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. doctor's probe-health "Latest" tail-picked the oldest cross-week event instead of the newest. Chronological sort at the reader seam, matching the writer's own documented contract, with all 14 consumers audited. Follow-up to file: `doctor.ts:1017` `self_upgrade_health` has the identical bug class. Verified before merge: the PR's own tests fail when the production change is reverted (11 of the previous 32 PRs failed exactly there — one had 7 of 8 new tests passing on master); typecheck clean; MERGEABLE/CLEAN with 22/22 checks green on the current base, not a stale one.
This commit is contained in:
@@ -37,7 +37,13 @@ export function readRecentParserProbeEvents(
|
||||
days = 7,
|
||||
now: Date = new Date(),
|
||||
): ParserProbeAuditEvent[] {
|
||||
return writer.readRecent(days, now);
|
||||
// Chronological order (oldest → newest). The shared reader walks the
|
||||
// CURRENT week's file first, then the previous week's, so without sorting
|
||||
// the array tail is the OLDEST in-window event whenever last week's file
|
||||
// has entries — and doctor's "latest" (which reads the tail) reported a
|
||||
// days-old run while counts included the newest one.
|
||||
return writer.readRecent(days, now)
|
||||
.sort((a, b) => Date.parse(a.ts) - Date.parse(b.ts));
|
||||
}
|
||||
|
||||
/** Exposed for tests pinning the rotation edge cases. */
|
||||
|
||||
@@ -118,5 +118,10 @@ export function readRecentQualityProbeEvents(
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
// Chronological order (oldest → newest). Events accumulate across two
|
||||
// week files read current-week-FIRST, so without sorting the array tail
|
||||
// is the OLDEST in-window event whenever last week's file has entries —
|
||||
// and doctor's "Latest:" (which reads the tail) reported a days-old run
|
||||
// while the counts included the newest one.
|
||||
return out.sort((a, b) => Date.parse(a.ts) - Date.parse(b.ts));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* the env override is process-global.
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
||||
import { mkdtempSync, rmSync, readdirSync } from 'node:fs';
|
||||
import { mkdtempSync, rmSync, readdirSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
@@ -73,6 +73,33 @@ describe('parser-probe audit trail', () => {
|
||||
logParserProbeEvent(makeEvent({ ts: old }));
|
||||
expect(readRecentParserProbeEvents(7).length).toBe(0);
|
||||
});
|
||||
|
||||
test('cross-week ordering: events come back chronological so the tail is the newest run', () => {
|
||||
// Regression: the shared week-file reader walks the current week's file
|
||||
// first, then the previous week's. Without sorting, the array tail —
|
||||
// which doctor's conversation_parser_probe_health reports as "latest" —
|
||||
// was the OLDEST in-window event whenever last week's file had entries.
|
||||
// Write the two week files directly so the cross-file case is genuinely
|
||||
// exercised (the writer routes by write time, not event ts).
|
||||
const now = new Date('2026-07-23T12:00:00Z');
|
||||
const thisWeekFile = computeParserProbeAuditFilename(now);
|
||||
const prevWeekFile = computeParserProbeAuditFilename(new Date(now.getTime() - 7 * 86400000));
|
||||
writeFileSync(join(auditDir, thisWeekFile), [
|
||||
JSON.stringify(makeEvent({ ts: '2026-07-22T08:00:00Z' })),
|
||||
JSON.stringify(makeEvent({ ts: '2026-07-23T08:00:00Z' })),
|
||||
].join('\n') + '\n');
|
||||
writeFileSync(join(auditDir, prevWeekFile), [
|
||||
JSON.stringify(makeEvent({ ts: '2026-07-17T08:00:00Z' })),
|
||||
JSON.stringify(makeEvent({ ts: '2026-07-18T08:00:00Z' })),
|
||||
].join('\n') + '\n');
|
||||
const events = readRecentParserProbeEvents(7, now);
|
||||
expect(events.map(e => e.ts)).toEqual([
|
||||
'2026-07-17T08:00:00Z',
|
||||
'2026-07-18T08:00:00Z',
|
||||
'2026-07-22T08:00:00Z',
|
||||
'2026-07-23T08:00:00Z',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parserProbeRanWithin — 24h rate-limit gate', () => {
|
||||
|
||||
@@ -298,6 +298,39 @@ describe('computeNightlyQualityProbeHealthCheck — pure doctor branch coverage'
|
||||
expect(check.message).toMatch(/1 non-PASS run /); // "run " not "runs "
|
||||
});
|
||||
|
||||
test('cross-week ordering: reader sorts chronologically so "Latest" is the newest run', async () => {
|
||||
// Regression: the reader walks the CURRENT week's file first, then the
|
||||
// previous week's. Without sorting, the array tail — which this check
|
||||
// reports as "Latest:" — was the OLDEST in-window event whenever last
|
||||
// week's file had entries (observed live: counts updated as new runs
|
||||
// landed while "Latest" stayed pinned days behind).
|
||||
const { computeQualityProbeAuditFilename, readRecentQualityProbeEvents } =
|
||||
await import('../src/core/audit-quality-probe.ts');
|
||||
const { computeNightlyQualityProbeHealthCheck } = await import('../src/commands/doctor.ts');
|
||||
const now = new Date('2026-07-23T12:00:00Z');
|
||||
const thisWeekFile = computeQualityProbeAuditFilename(now);
|
||||
const prevWeekFile = computeQualityProbeAuditFilename(new Date(now.getTime() - 7 * 86400000));
|
||||
writeFileSync(join(auditTmp, thisWeekFile), [
|
||||
JSON.stringify({ outcome: 'fail', ts: '2026-07-22T08:00:00Z' }),
|
||||
JSON.stringify({ outcome: 'fail', ts: '2026-07-23T08:00:00Z' }),
|
||||
].join('\n') + '\n');
|
||||
writeFileSync(join(auditTmp, prevWeekFile), [
|
||||
JSON.stringify({ outcome: 'fail', ts: '2026-07-17T08:00:00Z' }),
|
||||
JSON.stringify({ outcome: 'fail', ts: '2026-07-18T08:00:00Z' }),
|
||||
].join('\n') + '\n');
|
||||
await withEnv({ GBRAIN_AUDIT_DIR: auditTmp }, async () => {
|
||||
const events = readRecentQualityProbeEvents(7, now);
|
||||
expect(events.map(e => e.ts)).toEqual([
|
||||
'2026-07-17T08:00:00Z',
|
||||
'2026-07-18T08:00:00Z',
|
||||
'2026-07-22T08:00:00Z',
|
||||
'2026-07-23T08:00:00Z',
|
||||
]);
|
||||
const check = computeNightlyQualityProbeHealthCheck(true, events);
|
||||
expect(check.message).toContain('Latest: fail at 2026-07-23T08:00:00Z');
|
||||
});
|
||||
});
|
||||
|
||||
test('single PASS event uses singular grammar', async () => {
|
||||
const { computeNightlyQualityProbeHealthCheck } = await import('../src/commands/doctor.ts');
|
||||
const events = [{ outcome: 'pass', ts: '2026-05-22T03:00:00Z' }];
|
||||
|
||||
Reference in New Issue
Block a user