Files
waggle-os/scripts/check-no-invalid-snapshots.mjs
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

91 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
// CI lint guard — invalid Claude 4.6 family dated snapshots.
//
// Authority: PM-Waggle-OS/decisions/2026-04-22-model-route-naming-locked.md §4
//
// The suffix -20250514 was never a valid snapshot ID for the Claude 4.6
// family (Sonnet 4.6 + Opus 4.6). Any occurrence of this suffix in
// runtime source under packages/server/src/ sends an invalid model ID to
// Anthropic and causes 404 model_not_found at request time.
//
// This script freezes the regression: fail CI if the invalid snapshot
// appears in runtime source. Test-fixture occurrences under
// packages/*/tests/ are scoped separately and may remain until the LOW
// priority test-fixture migration ticket lands.
//
// Exit code 0 = clean. Exit code 1 = regression detected.
import fs from 'node:fs';
import path from 'node:path';
import url from 'node:url';
const REPO_ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..');
const INVALID_SNAPSHOT_REGEX = /claude-(sonnet|opus)-4-20250514/g;
// Runtime source only. Tests are scoped separately.
const SCAN_ROOTS = [
path.join(REPO_ROOT, 'packages', 'server', 'src'),
path.join(REPO_ROOT, 'packages', 'cli', 'src'),
];
function walk(dir, out) {
if (!fs.existsSync(dir)) return out;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.turbo') continue;
walk(full, out);
} else if (entry.isFile() && /\.(ts|tsx|js|mjs|cjs|jsx)$/.test(entry.name)) {
out.push(full);
}
}
return out;
}
function scanFile(file) {
const content = fs.readFileSync(file, 'utf-8');
const hits = [];
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const matches = lines[i].match(INVALID_SNAPSHOT_REGEX);
if (matches) {
for (const m of matches) {
hits.push({ file, line: i + 1, match: m, context: lines[i].trim().slice(0, 120) });
}
}
}
return hits;
}
function main() {
const files = [];
for (const root of SCAN_ROOTS) walk(root, files);
const allHits = [];
for (const file of files) {
for (const hit of scanFile(file)) allHits.push(hit);
}
if (allHits.length === 0) {
console.log('[check-no-invalid-snapshots] PASS: no -20250514 references in runtime source');
console.log('[check-no-invalid-snapshots] scanned ' + files.length + ' files across ' + SCAN_ROOTS.length + ' roots');
process.exit(0);
}
console.error('[check-no-invalid-snapshots] FAIL: ' + allHits.length + ' invalid snapshot reference(s) detected:');
for (const hit of allHits) {
const rel = path.relative(REPO_ROOT, hit.file);
console.error(' ' + rel + ':' + hit.line + ' ' + hit.match);
console.error(' -> ' + hit.context);
}
console.error('');
console.error('These IDs were never valid for the Claude 4.6 family. Use:');
console.error(' - Floating alias for runtime code: claude-sonnet-4-6, claude-opus-4-6');
console.error(' - Dated snapshot for benchmarks only: claude-opus-4-6-20250610 (verify against Anthropic docs)');
console.error('');
console.error('See PM-Waggle-OS/decisions/2026-04-22-model-route-naming-locked.md for the LOCK.');
process.exit(1);
}
main();