91 lines
3.3 KiB
JavaScript
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();
|