fix: sort missing inspection dates after pre-epoch timestamps

(cherry picked from commit 774dfd6894)
This commit is contained in:
Eneko Ruiz authored and haelyra committed 2026-10-20 17:40:57 -04:00
1 parent c2a28b2963
commit 3ac640f6cc
2 files changed
+40 -2

No files matched your search

+10 -2
View File
@@ -66,6 +66,14 @@ function groupFailures(skillRuns) {
return groups;
}
function compareTimestampsDescending(a, b) {
const timeA = Date.parse(a);
const timeB = Date.parse(b);
if (Number.isNaN(timeA)) return Number.isNaN(timeB) ? 0 : 1;
if (Number.isNaN(timeB)) return -1;
return timeB - timeA;
}
/**
* Detect recurring failure patterns from skill runs.
*
@@ -85,7 +93,7 @@ function detectPatterns(skillRuns, options = {}) {
}
const sortedRuns = [...group.runs].sort(
(a, b) => (Date.parse(b.createdAt) || 0) - (Date.parse(a.createdAt) || 0)
(a, b) => compareTimestampsDescending(a.createdAt, b.createdAt)
);
const firstSeen = sortedRuns[sortedRuns.length - 1].createdAt || null;
@@ -112,7 +120,7 @@ function detectPatterns(skillRuns, options = {}) {
// Sort by count descending, then by lastSeen descending
return patterns.sort((a, b) => {
if (b.count !== a.count) return b.count - a.count;
return (Date.parse(b.lastSeen) || 0) - (Date.parse(a.lastSeen) || 0);
return compareTimestampsDescending(a.lastSeen, b.lastSeen);
});
}
+30
View File
@@ -232,6 +232,36 @@ async function runTests() {
assert.strictEqual(patterns[2].lastSeen, null);
})) passed += 1; else failed += 1;
if (await test('detectPatterns keeps undated runs and patterns stable after dated ones', async () => {
const historical = '1969-12-31T23:59:59Z';
const epoch = '1970-01-01T00:00:00Z';
const runs = [
{ ...makeSkillRun({ id: 'missing-a' }), createdAt: null },
makeSkillRun({ id: 'historical', createdAt: historical }),
{ ...makeSkillRun({ id: 'missing-b' }), createdAt: null },
makeSkillRun({ id: 'epoch', createdAt: epoch }),
];
const [pattern] = detectPatterns(runs, { threshold: 2 });
assert.deepStrictEqual(pattern.runIds, ['epoch', 'historical', 'missing-a', 'missing-b']);
assert.strictEqual(pattern.lastSeen, epoch);
const patternRuns = [
{ ...makeSkillRun({ id: 'a1', skillId: 'undated-a' }), createdAt: null },
{ ...makeSkillRun({ id: 'a2', skillId: 'undated-a' }), createdAt: null },
makeSkillRun({ id: 'h1', skillId: 'historical', createdAt: '1969-12-31T23:59:58Z' }),
makeSkillRun({ id: 'h2', skillId: 'historical', createdAt: '1969-12-31T23:59:59Z' }),
{ ...makeSkillRun({ id: 'b1', skillId: 'undated-b' }), createdAt: null },
{ ...makeSkillRun({ id: 'b2', skillId: 'undated-b' }), createdAt: null },
];
const patterns = detectPatterns(patternRuns, { threshold: 2 });
assert.deepStrictEqual(patterns.map(pattern => pattern.skillId), ['historical', 'undated-a', 'undated-b']);
assert.deepStrictEqual(patterns[1].runIds, ['a1', 'a2']);
assert.deepStrictEqual(patterns[2].runIds, ['b1', 'b2']);
assert.strictEqual(patterns[1].firstSeen, null);
assert.strictEqual(patterns[1].lastSeen, null);
})) passed += 1; else failed += 1;
if (await test('detectPatterns uses default threshold', async () => {
const runs = Array.from({ length: DEFAULT_FAILURE_THRESHOLD }, (_, i) =>
makeSkillRun({ id: `r${i}`, failureReason: 'permission denied' })