config-audit/tests/scanners/token-hotspots.test.mjs
Kjell Tore Guttormsen 8376dab83f fix(tokens): refresh stale "Opus 4.7" framing to model-neutral + Opus 4.8 anchor
Fase 4 token-opt, Item 1 of gap-review NEXT STEP #2. The prompt-cache pattern corpus + TOK scanner were frozen at an "Opus 4.7" framing after CC shipped Opus 4.8 (default, 2.1.154) and Fable 5 (2.1.170). Model-era facts re-verified against the official changelog cache before editing.

The patterns are properties of prompt-caching, not of any model, so mechanic text is now model-neutral with a single "current default: Opus 4.8" anchor — preventing a re-freeze at the next model bump.

- rename knowledge/opus-4.7-patterns.md -> prompt-cache-patterns.md (git mv, history preserved); 6 reference sites updated
- TOK scanner: line-318 finding text (human-facing) made model-neutral; header + cache-prefix-scanner + CLI comments refreshed
- configuration-best-practices.md body + footnote 4.7 -> 4.8
- human-facing docs: commands/{tokens,help,manifest}.md, project CLAUDE.md, README, docs/scanner-internals.md
- gap-matrix row marked DONE; future Items 2/3 retargeted to new filename

Failing-test-first (Iron Law): +2 knowledge staleness guards (era-anchor + no-refreeze) +1 scanner assertion (no stale model anchor in finding text). Suite 853 -> 856 green; zero snapshot drift; self-audit A(97) PASS. CHANGELOG / v5 plan / ratified gap-plan keep historical opus-4.7-patterns refs (correct record of past state).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
2026-06-18 15:27:36 +02:00

320 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { resetCounter } from '../../scanners/lib/output.mjs';
import { scan } from '../../scanners/token-hotspots.mjs';
import { discoverConfigFiles } from '../../scanners/lib/file-discovery.mjs';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const FIXTURES = resolve(__dirname, '../fixtures');
async function fixtureDiscovery(name) {
return discoverConfigFiles(resolve(FIXTURES, name));
}
async function runScanner(fixtureName) {
resetCounter();
const path = resolve(FIXTURES, fixtureName);
const discovery = await fixtureDiscovery(fixtureName);
return scan(path, discovery);
}
describe('TOK scanner — healthy-project', () => {
let result;
beforeEach(async () => {
result = await runScanner('healthy-project');
});
it('returns status ok', () => {
assert.equal(result.status, 'ok');
});
it('reports scanner prefix TOK', () => {
assert.equal(result.scanner, 'TOK');
});
it('finding IDs match CA-TOK-NNN pattern', () => {
for (const f of result.findings) {
assert.match(f.id, /^CA-TOK-\d{3}$/);
}
});
it('exposes total_estimated_tokens as a number', () => {
assert.equal(typeof result.total_estimated_tokens, 'number');
assert.ok(result.total_estimated_tokens >= 0);
});
});
describe('TOK scanner — opus-47/cache-breaking', () => {
let result;
beforeEach(async () => {
result = await runScanner('opus-47/cache-breaking');
});
it('flags CA-TOK-001 (cache-breaking volatile top)', () => {
const f = result.findings.find(x => x.id === 'CA-TOK-001');
assert.ok(f, 'expected a CA-TOK-001 finding for cache-breaking fixture');
});
it('CA-TOK-001 severity is high (v5 F7 recalibration)', () => {
const f = result.findings.find(x => x.id === 'CA-TOK-001');
assert.equal(f.severity, 'high', `expected high after F7, got ${f.severity}`);
});
it('CA-TOK-001 description carries no stale Opus-4.7 anchor (mechanic is model-agnostic)', () => {
const f = result.findings.find(x => x.id === 'CA-TOK-001');
assert.doesNotMatch(f.description, /Opus 4\.7/,
'prompt-cache mechanics are a feature property, not model-specific; do not era-stamp a stale model');
});
});
describe('TOK scanner — opus-47/redundant-tools', () => {
let result;
beforeEach(async () => {
result = await runScanner('opus-47/redundant-tools');
});
it('emits at least one CA-TOK-002 finding (redundant tool/permission)', () => {
const has002 = result.findings.some(f => /^CA-TOK-002$/.test(f.id) || f.title?.toLowerCase().includes('redundant'));
assert.ok(has002, 'expected a CA-TOK-002 finding for redundant-tools fixture');
});
});
describe('TOK scanner — opus-47/deep-imports', () => {
let result;
beforeEach(async () => {
result = await runScanner('opus-47/deep-imports');
});
it('emits at least one CA-TOK-003 finding (deep @import chain)', () => {
const has003 = result.findings.some(f => /^CA-TOK-003$/.test(f.id) || f.title?.toLowerCase().includes('import'));
assert.ok(has003, 'expected a CA-TOK-003 finding for deep-imports fixture');
});
});
describe('TOK scanner — opus-47/sonnet-era (v5 F5: Pattern D removed)', () => {
let result;
beforeEach(async () => {
result = await runScanner('opus-47/sonnet-era');
});
it('emits zero findings (no Pattern D / CA-TOK-004 anymore)', () => {
assert.equal(result.findings.length, 0,
`expected 0 findings on sonnet-era after F5, got: ${result.findings.map(f => f.id).join(', ')}`);
});
it('never emits CA-TOK-004 (removed in v5)', () => {
assert.ok(result.findings.every(f => f.id !== 'CA-TOK-004'),
'expected no CA-TOK-004; removed in v5 F5');
});
});
describe('TOK scanner — marketplace scale ordering', () => {
it('total_estimated_tokens strictly increases across small → medium → large', async () => {
const small = await runScanner('marketplace-small');
const medium = await runScanner('marketplace-medium');
const large = await runScanner('marketplace-large');
assert.ok(small.total_estimated_tokens < medium.total_estimated_tokens,
`expected small (${small.total_estimated_tokens}) < medium (${medium.total_estimated_tokens})`);
assert.ok(medium.total_estimated_tokens < large.total_estimated_tokens,
`expected medium (${medium.total_estimated_tokens}) < large (${large.total_estimated_tokens})`);
});
});
describe('TOK scanner — readActiveConfig integration (v5 F1)', () => {
let result;
beforeEach(async () => {
result = await runScanner('tok-active-config');
});
it('exposes activeConfig summary on the result (proves readActiveConfig was called)', () => {
assert.ok(result.activeConfig, 'expected result.activeConfig to be set');
assert.equal(typeof result.activeConfig.claudeMdEstimatedTokens, 'number');
assert.ok(result.activeConfig.claudeMdEstimatedTokens > 0,
`expected claudeMd cascade > 0 tokens, got ${result.activeConfig.claudeMdEstimatedTokens}`);
});
it('hotspots include at least one MCP-source entry', () => {
const hasMcp = result.hotspots.some(h => /mcp/i.test(h.source));
assert.ok(hasMcp,
`expected hotspots to include an MCP source; got: ${result.hotspots.map(h => h.source).join(', ')}`);
});
it('total_estimated_tokens exceeds the minimal sonnet-era baseline', async () => {
// sonnet-era has no .mcp.json — the activeConfig MCP entries from this
// fixture should push its total above sonnet-era's even when both fixtures
// share the user's ambient cascade/plugin state.
const baseline = await runScanner('opus-47/sonnet-era');
assert.ok(result.total_estimated_tokens > baseline.total_estimated_tokens,
`expected ${result.total_estimated_tokens} > ${baseline.total_estimated_tokens}`);
});
});
describe('TOK scanner — hotspots contract', () => {
let result;
beforeEach(async () => {
result = await runScanner('marketplace-large');
});
it('every finding has a non-empty recommendation', () => {
for (const f of result.findings) {
assert.ok(f.recommendation, `finding ${f.id} missing recommendation`);
assert.ok(String(f.recommendation).length > 0, `finding ${f.id} has empty recommendation`);
}
});
it('exposes a hotspots array of length 310', () => {
assert.ok(Array.isArray(result.hotspots), 'expected result.hotspots to be an array');
assert.ok(result.hotspots.length >= 3, `expected ≥3 hotspots, got ${result.hotspots.length}`);
assert.ok(result.hotspots.length <= 10, `expected ≤10 hotspots, got ${result.hotspots.length}`);
});
it('every hotspot exposes source/estimated_tokens/rank/recommendations', () => {
for (const h of result.hotspots) {
assert.ok(typeof h.source === 'string' && h.source.length > 0, 'hotspot.source missing');
assert.equal(typeof h.estimated_tokens, 'number', 'hotspot.estimated_tokens not a number');
assert.equal(typeof h.rank, 'number', 'hotspot.rank not a number');
assert.ok(Array.isArray(h.recommendations), 'hotspot.recommendations not an array');
assert.ok(h.recommendations.length >= 1 && h.recommendations.length <= 3,
`hotspot.recommendations length should be 13, got ${h.recommendations.length}`);
}
});
it('every hotspot.source is unique (v5 F4: no padding)', () => {
const sources = result.hotspots.map(h => h.source);
const unique = new Set(sources);
assert.equal(unique.size, sources.length,
`expected unique sources; got duplicates in: ${sources.join(', ')}`);
});
it('hotspots.length never exceeds HOTSPOTS_MAX (10)', () => {
assert.ok(result.hotspots.length <= 10,
`expected ≤10 hotspots, got ${result.hotspots.length}`);
});
});
describe('TOK scanner — M2 skill description > 500 chars (v5)', () => {
it('flags skill with bloated description (low severity)', async () => {
const result = await runScanner('skill-bloated');
const f = result.findings.find(x => /skill description/i.test(x.title || ''));
assert.ok(f, `expected skill-description finding; got: ${result.findings.map(x => x.title).join(' | ')}`);
assert.equal(f.severity, 'low', `expected low, got ${f.severity}`);
assert.match(f.evidence || '', /bloated/);
});
it('does NOT flag tight description (under 500 chars)', async () => {
const result = await runScanner('skill-tight');
const f = result.findings.find(x => /skill description/i.test(x.title || ''));
assert.equal(f, undefined, `expected no skill-description finding; got: ${f?.title}`);
});
});
describe('TOK scanner — M4 cascade > 10k tokens (v5)', () => {
it('flags CLAUDE.md cascade > 10k tokens with medium severity', async () => {
const result = await runScanner('large-cascade');
const f = result.findings.find(x => /cascade/i.test(x.title || ''));
assert.ok(f, `expected cascade finding; got: ${result.findings.map(x => x.title).join(' | ')}`);
assert.equal(f.severity, 'medium', `expected medium, got ${f.severity}`);
assert.match(f.title, /CLAUDE\.md cascade/i);
});
it('does NOT flag small cascade (< 10k tokens)', async () => {
const result = await runScanner('small-cascade');
const f = result.findings.find(x => /cascade/i.test(x.title || ''));
assert.equal(f, undefined,
`expected no cascade finding for small fixture; got: ${f?.title}`);
});
});
describe('TOK scanner — N1 MCP tool-schema budget (v5 CA-TOK-005)', () => {
// readActiveConfig pulls in ambient ~/.claude.json plugin MCP servers; tests
// filter to the fixture's own server name (budget-srv-<count>) to avoid
// user-state leakage. Findings identified by title (not exact ID) — TOK IDs
// are sequential per scan.
const findFixtureBudget = (result, count) =>
result.findings.find(f =>
/MCP tool-schema budget/i.test(f.title || '') &&
(f.title || '').includes(`budget-srv-${count}`)
);
it('14 tools → no budget finding (under 20-tool floor)', async () => {
const result = await runScanner('mcp-budget/14-tools');
const f = findFixtureBudget(result, 14);
assert.equal(f, undefined,
`expected no budget finding for budget-srv-14 under 20 tools; got: ${f?.title}`);
});
it('25 tools → low severity', async () => {
const result = await runScanner('mcp-budget/25-tools');
const f = findFixtureBudget(result, 25);
assert.ok(f, `expected budget finding for budget-srv-25; got: ${result.findings.map(x => x.title).join(' | ')}`);
assert.equal(f.severity, 'low', `expected low for 25 tools, got ${f.severity}`);
});
it('60 tools → medium severity', async () => {
const result = await runScanner('mcp-budget/60-tools');
const f = findFixtureBudget(result, 60);
assert.ok(f, `expected budget finding for budget-srv-60`);
assert.equal(f.severity, 'medium', `expected medium for 60 tools, got ${f.severity}`);
});
it('120 tools → high severity', async () => {
const result = await runScanner('mcp-budget/120-tools');
const f = findFixtureBudget(result, 120);
assert.ok(f, `expected budget finding for budget-srv-120`);
assert.equal(f.severity, 'high', `expected high for 120 tools, got ${f.severity}`);
});
it('unknown toolCount → low severity with "unknown" in evidence', async () => {
const result = await runScanner('mcp-budget/unknown-tools');
const f = findFixtureBudget(result, 'unknown');
assert.ok(f, `expected budget finding for budget-srv-unknown`);
assert.equal(f.severity, 'low', `expected low for unknown toolCount, got ${f.severity}`);
assert.match(String(f.evidence || ''), /unknown/i,
`expected "unknown" in evidence, got: ${f.evidence}`);
});
it('finding ID matches CA-TOK-NNN format', async () => {
const result = await runScanner('mcp-budget/120-tools');
const f = findFixtureBudget(result, 120);
assert.ok(f);
assert.match(f.id, /^CA-TOK-\d{3}$/);
});
it('finding evidence carries calibration_note', async () => {
const result = await runScanner('mcp-budget/60-tools');
const f = findFixtureBudget(result, 60);
assert.ok(f);
assert.match(String(f.evidence || ''), /severity reflects estimated tokens\/turn/i);
});
});
describe('TOK scanner — F7 severity recalibration (v5)', () => {
// Findings identified by title pattern, not finding ID — TOK IDs are
// sequential per scan run, not semantic per pattern (output.mjs:31).
const SEVERITY_TABLE = [
{ fixture: 'opus-47/cache-breaking', pattern: 'A', titleMatch: /cache-breaking volatile/i, expected: 'high' },
{ fixture: 'opus-47/redundant-tools', pattern: 'B', titleMatch: /redundant permission/i, expected: 'medium' },
{ fixture: 'opus-47/deep-imports', pattern: 'C', titleMatch: /deep @import chain/i, expected: 'low' },
];
for (const { fixture, pattern, titleMatch, expected } of SEVERITY_TABLE) {
it(`Pattern ${pattern} (${fixture}) has severity ${expected}`, async () => {
const result = await runScanner(fixture);
const f = result.findings.find(x => titleMatch.test(x.title || ''));
assert.ok(f, `expected a finding matching ${titleMatch} in ${fixture}; got: ${result.findings.map(x => x.title).join(' | ')}`);
assert.equal(f.severity, expected, `expected ${expected}, got ${f.severity}`);
});
it(`Pattern ${pattern} (${fixture}) carries calibration_note evidence`, async () => {
const result = await runScanner(fixture);
const f = result.findings.find(x => titleMatch.test(x.title || ''));
assert.ok(f, `expected a finding matching ${titleMatch} in ${fixture}`);
const evidence = String(f.evidence || '');
assert.ok(/severity reflects estimated tokens\/turn/i.test(evidence),
`expected calibration_note phrase in evidence, got: ${evidence}`);
});
}
});