/** * Session #51 — knowledge-refresh must write the register it actually read. * * The command reads the register through `knowledge-refresh-cli.mjs`, whose `REGISTER_PATH` * is anchored to the CLI module itself — i.e. `${CLAUDE_PLUGIN_ROOT}/knowledge/ * best-practices.json`. For any installed plugin that is the marketplace cache; measured * live during this chunk: * * registerPath: …/.claude/plugins/cache/ktg-plugin-marketplace/config-audit/5.13.0/ * knowledge/best-practices.json * * Step 6 then said: Edit `knowledge/best-practices.json` — an UNANCHORED relative path, * which resolves against whatever repo the user happens to be sitting in. Three consequences, * none of them visible at the time: * * 1. For a normal user, that path does not exist in their repo at all, so the approved * change either fails or drops a stray file into their project. * 2. In the plugin's own checkout it resolves to the working tree, so the command reads * one file and writes a different one — the refresh appears to do nothing, because the * CLI keeps reporting the cached copy's dates. * 3. Step 6.3's validation gate runs the plugin's own test file, which loads the register * via the same anchored `REGISTER_PATH`. So the gate validates the copy that was NOT * edited and passes no matter what was written — a guard that cannot see the file it * guards. * * The two register copies were byte-identical on the day this was found, which is exactly * why the defect was invisible to a casual dogfood run. */ import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFile } from 'node:fs/promises'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const FILE = resolve(__dirname, '..', '..', 'commands', 'knowledge-refresh.md'); /** * Scoped to lines that tell the model to MODIFY the register. Descriptive prose ("this * command keeps knowledge/best-practices.json current") and the closing `git commit` * suggestion name the file without acting on it, and a git path is correctly repo-relative. * The defect was specifically an unanchored path in an imperative write step. */ const WRITE_VERB = /\b(edit|write|append|overwrite|save)\b/i; function unanchoredWriteTargets(content) { const out = []; content.split('\n').forEach((line, i) => { if (!WRITE_VERB.test(line)) return; for (const m of line.matchAll(/(\S*)knowledge\/best-practices\.json/g)) { if (!m[1].endsWith('${CLAUDE_PLUGIN_ROOT}/')) out.push(`${i + 1}: ${line.trim()}`); } }); return out; } test('the guard still catches the original defect', () => { const original = '1. Edit `knowledge/best-practices.json` — bump `source.verified`, update the `claim`/'; assert.equal( unanchoredWriteTargets(original).length, 1, 'A narrowed guard must still fail on the text it was written for.', ); }); test('every register path in knowledge-refresh.md is anchored to the plugin root', async () => { const content = await readFile(FILE, 'utf-8'); const unanchored = unanchoredWriteTargets(content); assert.deepEqual( unanchored, [], 'A bare `knowledge/best-practices.json` resolves against the user\'s current repo, not\n' + 'against the register the CLI actually read. Anchor every reference to\n' + '${CLAUDE_PLUGIN_ROOT}/ so the file that is read, written, and validated is one file:\n ' + unanchored.join('\n '), ); }); test('the write step says where the register really lives', async () => { const content = await readFile(FILE, 'utf-8'); assert.match( content, /marketplace|plugin cache|replaced on upgrade|plugin's own checkout/i, 'Step 6 must state that the register is part of the installed plugin, so an approved\n' + 'edit to a marketplace-installed copy is discarded by the next plugin upgrade and the\n' + 'durable change belongs in the plugin\'s own checkout. Silently editing a cache\n' + 'directory is the kind of write that looks successful and evaporates.', ); });