feat(tok,acr): v5.6 B2 — load-pattern column in token-hotspots

Annotate every ranked TOK hotspot with the load-pattern triple
(loadPattern/survivesCompaction/derivationConfidence):

- hotspotLoadPattern() maps each discovery `type` → a deriveLoadPattern kind.
  Rules reuse activeConfig.rules for precise `scoped` handling; claude-md maps
  by scope. Two new deriveLoadPattern kinds back the rest: `command`
  (on-demand — body loads on /invoke) and `harness-config` (external —
  settings/keybindings/.mcp.json/hooks.json/plugin.json configure the CLI, not
  the model context, so they cost no per-turn context tokens). Honest split:
  the .mcp.json FILE is external; the MCP server's tool schemas are a separate
  `always` hotspot.

Byte-stability — the opposite of B1's manifest. token-hotspots IS a byte-equal
SC-6/SC-7 CLI, and its hotspots ride inside scan-orchestrator + posture, so the
change touched SIX frozen-v5.0.0 comparisons across five test files. Resolved by
preserving the frozen baselines: a shared tests/helpers/strip-hotspot-load-pattern.mjs
strips the additive triple before each byte-equal compare (proves the original
schema is byte-identical). SC-5 default-output snapshots (scan-orchestrator +
token-hotspots) regenerated — diff reviewed as additive-only.

Tests 1008→1012. Self-audit A/A, scanner count unchanged at 13 (C bumps to 14).
Completes v5.6 B (B1 manifest + B2 token-hotspots).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-06-20 20:21:01 +02:00
commit 778b517e6f
17 changed files with 236 additions and 40 deletions

View file

@ -6,6 +6,7 @@ import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { readFile, writeFile, unlink, mkdir, access } from 'node:fs/promises';
import { hermeticEnv, HERMETIC_HOME } from '../helpers/hermetic-home.mjs';
import { stripHotspotLoadPattern } from '../helpers/strip-hotspot-load-pattern.mjs';
const exec = promisify(execFile);
const __dirname = dirname(fileURLToPath(import.meta.url));
@ -42,7 +43,7 @@ async function runCli(cliPath, args, env = {}) {
function normalizeTokenHotspotsPayload(p) {
const out = JSON.parse(JSON.stringify(p));
out.duration_ms = 0;
return out;
return stripHotspotLoadPattern(out);
}
function normalizeManifestOutput(o) {

View file

@ -6,6 +6,7 @@ import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { readFile, unlink } from 'node:fs/promises';
import { hermeticEnv } from '../helpers/hermetic-home.mjs';
import { stripHotspotLoadPattern } from '../helpers/strip-hotspot-load-pattern.mjs';
const exec = promisify(execFile);
const __dirname = dirname(fileURLToPath(import.meta.url));
@ -38,7 +39,7 @@ function normalizePosture(p) {
}
}
}
return out;
return stripHotspotLoadPattern(out);
}
/** Strip time-varying durations (Xms) so progress lines compare verbatim across runs. */

View file

@ -6,6 +6,7 @@ import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { readFile, unlink } from 'node:fs/promises';
import { hermeticEnv } from '../helpers/hermetic-home.mjs';
import { stripHotspotLoadPattern } from '../helpers/strip-hotspot-load-pattern.mjs';
const exec = promisify(execFile);
const __dirname = dirname(fileURLToPath(import.meta.url));
@ -36,7 +37,7 @@ function normalizeEnvelope(env) {
}
}
}
return out;
return stripHotspotLoadPattern(out);
}
async function runOrchestrator(flags) {

View file

@ -182,6 +182,32 @@ describe('TOK scanner — hotspots contract', () => {
}
});
it('every hotspot carries a load-pattern triple (v5.6 B2)', () => {
const LOAD_PATTERNS = new Set(['always', 'on-demand', 'external', 'unknown']);
const SURVIVES = new Set(['yes', 'no', 'n/a']);
for (const h of result.hotspots) {
assert.ok(LOAD_PATTERNS.has(h.loadPattern),
`hotspot ${h.source} has invalid loadPattern: ${h.loadPattern}`);
assert.ok(SURVIVES.has(h.survivesCompaction),
`hotspot ${h.source} has invalid survivesCompaction: ${h.survivesCompaction}`);
assert.ok(typeof h.derivationConfidence === 'string' && h.derivationConfidence.length > 0,
`hotspot ${h.source} missing derivationConfidence`);
}
});
it('tags a CLAUDE.md hotspot always-loaded and a skill hotspot on-demand (v5.6 B2)', () => {
const claudeMd = result.hotspots.find(h => /CLAUDE\.md/.test(h.source));
if (claudeMd) {
assert.equal(claudeMd.loadPattern, 'always',
`expected CLAUDE.md hotspot always-loaded; got ${claudeMd.loadPattern}`);
}
const skill = result.hotspots.find(h => /SKILL\.md/.test(h.source) || /skills?\//.test(h.source));
if (skill) {
assert.equal(skill.loadPattern, 'on-demand',
`expected skill hotspot on-demand; got ${skill.loadPattern}`);
}
});
it('every hotspot.source is unique (v5 F4: no padding)', () => {
const sources = result.hotspots.map(h => h.source);
const unique = new Set(sources);