feat(audit): add rolling phase0 artifact retention tooling

This commit is contained in:
William Valentin
2026-02-27 10:20:14 -08:00
parent 149adb1c85
commit 134fa60af1
10 changed files with 420 additions and 5 deletions
@@ -0,0 +1,88 @@
import { describe, expect, it } from 'vitest';
import {
collectRollingPhase0ArtifactFiles,
planRollingPhase0ArtifactRetention,
} from './phase0BaselineArtifactRetention.js';
describe('phase0BaselineArtifactRetention', () => {
it('collects only rolling-tag phase-0 artifact files', () => {
const rows = collectRollingPhase0ArtifactFiles([
'phase0_baseline_live_2026-02-27-010203.json',
'phase0_baseline_live_gateway_2026-02-27-010203.md',
'phase0_baseline_live_backend_pi_embedded_2026-02-27-010203.jsonl',
'phase0_baseline_live_backend_native_2026-02-27-010203.json',
'phase0_baseline_live_backend_drift_2026-02-27-010203.md',
'phase0_baseline_live_2026-02-27.json',
'phase0_baseline_live_gateway_2026-02-27.jsonl',
'phase0_baseline_2026-02-25.md',
'phase0_baseline_live_backend_pi_embedded_2026-02-27.md',
'not_a_phase0_file.txt',
]);
expect(rows).toHaveLength(5);
expect(rows.map((row) => row.family).sort()).toEqual([
'backend_drift',
'backend_native',
'backend_pi_embedded',
'channel',
'gateway',
]);
});
it('keeps most recent rolling tags per family and prunes older ones', () => {
const files = [
'phase0_baseline_live_2026-02-27-010203.json',
'phase0_baseline_live_2026-02-27-010203.jsonl',
'phase0_baseline_live_2026-02-27-020304.json',
'phase0_baseline_live_2026-02-27-020304.md',
'phase0_baseline_live_gateway_2026-02-27-010203.json',
'phase0_baseline_live_gateway_2026-02-27-020304.json',
'phase0_baseline_live_backend_pi_embedded_2026-02-27-010203.json',
'phase0_baseline_live_backend_pi_embedded_2026-02-27-020304.json',
'phase0_baseline_live_backend_native_2026-02-27-010203.json',
'phase0_baseline_live_backend_native_2026-02-27-020304.json',
'phase0_baseline_live_backend_drift_2026-02-27-010203.json',
'phase0_baseline_live_backend_drift_2026-02-27-020304.json',
'phase0_baseline_live_2026-02-27.json',
];
const plan = planRollingPhase0ArtifactRetention(files, 1);
expect(plan.families).toEqual([
{ family: 'channel', total_tags: 2, keep_tags: 1, remove_tags: 1 },
{ family: 'gateway', total_tags: 2, keep_tags: 1, remove_tags: 1 },
{ family: 'backend_pi_embedded', total_tags: 2, keep_tags: 1, remove_tags: 1 },
{ family: 'backend_native', total_tags: 2, keep_tags: 1, remove_tags: 1 },
{ family: 'backend_drift', total_tags: 2, keep_tags: 1, remove_tags: 1 },
]);
const removeSet = new Set(plan.remove.map((row) => row.file_name));
expect(removeSet.has('phase0_baseline_live_2026-02-27-010203.json')).toBe(true);
expect(removeSet.has('phase0_baseline_live_2026-02-27-010203.jsonl')).toBe(true);
expect(removeSet.has('phase0_baseline_live_gateway_2026-02-27-010203.json')).toBe(true);
expect(removeSet.has('phase0_baseline_live_backend_pi_embedded_2026-02-27-010203.json')).toBe(true);
expect(removeSet.has('phase0_baseline_live_backend_native_2026-02-27-010203.json')).toBe(true);
expect(removeSet.has('phase0_baseline_live_backend_drift_2026-02-27-010203.json')).toBe(true);
const keepSet = new Set(plan.keep.map((row) => row.file_name));
expect(keepSet.has('phase0_baseline_live_2026-02-27.json')).toBe(false);
expect(keepSet.has('phase0_baseline_live_2026-02-27-020304.json')).toBe(true);
expect(keepSet.has('phase0_baseline_live_2026-02-27-020304.md')).toBe(true);
});
it('supports zero keep limit', () => {
const plan = planRollingPhase0ArtifactRetention([
'phase0_baseline_live_2026-02-27-010203.json',
'phase0_baseline_live_gateway_2026-02-27-010203.json',
], 0);
expect(plan.keep).toHaveLength(0);
expect(plan.remove.map((row) => row.file_name).sort()).toEqual([
'phase0_baseline_live_2026-02-27-010203.json',
'phase0_baseline_live_gateway_2026-02-27-010203.json',
]);
});
it('rejects negative keep limit', () => {
expect(() => planRollingPhase0ArtifactRetention([], -1)).toThrow('keepPerFamily');
});
});