feat(tokens): stale plugin-cache disk-cleanup finding, honestly labelled Dead config (v5.9 B3b)
The B3a cache filter already exposes discovery.staleCacheVersions; surface them
as a finding so the user knows the superseded plugin versions are safe to delete.
Honesty (Verifiseringsplikt): the finding loads on ZERO turns, so it must NOT
read as a per-turn token cost. TOK normally humanizes to "Wasted tokens"; a new
per-finding category override ('plugin-cache-hygiene' -> "Dead config") plus a
dedicated humanizer translation ("Old plugin versions are sitting on disk (safe
to delete) ... cost zero tokens per turn ... housekeeping, not a performance
problem") keep the prose accurate instead of the generic "using more space"
default. evidence carries the explicit "zero live-context impact" note.
- token-hotspots: Pattern H emits CA-TOK (low) from discovery.staleCacheVersions
when stale versions exist (`--global`); silent otherwise.
- humanizer: CATEGORY_TO_IMPACT lets a finding's category override the
scanner-default impact label (raw `category` field unchanged -> --json/--raw
byte-stable). humanizer-data: honest static translation for the finding.
- Tests: finding fires/severity/category, lists stale keys + zero-impact note,
silent when none; humanizer override -> Dead config; honest translation locked.
- Docs: tokens command render note (disk-cleanup, not a token problem) +
--no-exclude-cache flag; README + CLAUDE.md rows (7 patterns, cache-aware).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a371832688
commit
ba9f82f952
8 changed files with 124 additions and 6 deletions
|
|
@ -354,3 +354,44 @@ describe('TOK scanner — F7 severity recalibration (v5)', () => {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ── Pattern H: stale plugin-cache versions (v5.9 B3) ──────────────────────
|
||||
describe('TOK scanner — H stale plugin-cache versions (v5.9 B3)', () => {
|
||||
const staleDiscovery = {
|
||||
files: [],
|
||||
staleCacheVersions: [
|
||||
{ key: 'mkt/voyage/5.1.1', fileCount: 35, estimatedBytes: 416237 },
|
||||
{ key: 'mkt/config-audit/5.6.0', fileCount: 149, estimatedBytes: 301330 },
|
||||
],
|
||||
};
|
||||
|
||||
it('emits a low-severity stale-cache finding when stale versions exist', async () => {
|
||||
resetCounter();
|
||||
const result = await withHermeticHome(() => scan('/tmp/x', staleDiscovery));
|
||||
const f = result.findings.find(x => /stale plugin-cache/i.test(x.title || ''));
|
||||
assert.ok(f, 'expected a stale plugin-cache finding');
|
||||
assert.equal(f.severity, 'low');
|
||||
assert.equal(f.category, 'plugin-cache-hygiene');
|
||||
});
|
||||
|
||||
it('states zero live-context impact and lists the stale keys', async () => {
|
||||
resetCounter();
|
||||
const result = await withHermeticHome(() => scan('/tmp/x', staleDiscovery));
|
||||
const f = result.findings.find(x => /stale plugin-cache/i.test(x.title || ''));
|
||||
assert.match(f.evidence, /zero live-context impact/i);
|
||||
assert.match(f.evidence, /mkt\/voyage\/5\.1\.1/);
|
||||
assert.match(f.evidence, /mkt\/config-audit\/5\.6\.0/);
|
||||
});
|
||||
|
||||
it('does NOT fire when there are no stale versions', async () => {
|
||||
resetCounter();
|
||||
const result = await withHermeticHome(() => scan('/tmp/x', { files: [], staleCacheVersions: [] }));
|
||||
assert.ok(!result.findings.some(x => /stale plugin-cache/i.test(x.title || '')));
|
||||
});
|
||||
|
||||
it('does NOT fire when discovery omits staleCacheVersions entirely', async () => {
|
||||
resetCounter();
|
||||
const result = await withHermeticHome(() => scan('/tmp/x', { files: [] }));
|
||||
assert.ok(!result.findings.some(x => /stale plugin-cache/i.test(x.title || '')));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue