Dogfooding `campaign` + `knowledge-refresh` against a throwaway ledger. Seven
defects, all found by running the commands as written and measuring, not by
reading them.
The headline pair only existed together. `knowledge-refresh` built
`STALE_AFTER="--stale-after 30"` and expanded it unquoted, trusting the shell to
split it in two. bash does; zsh — the macOS default, and what the Bash tool runs
here — does not. The CLI got one argv entry, matched no flag, and because it had
no unknown-flag branch, silently kept the 90-day default and reported "✓ All 14
register entries were re-verified within the last 90 days": a true-sounding
sentence about a threshold the user had just overridden. Fixing either half alone
leaves a silent wrong answer or a loud one; both are fixed, and a guard now
rejects any template that packs a flag and its value into one variable.
`knowledge-refresh` also read one register and wrote another: step 6 named an
unanchored `knowledge/best-practices.json` while the CLI reads
`${CLAUDE_PLUGIN_ROOT}/…`, which for an installed plugin is the cache. The
validation gate then ran the cached test against the cached register — green no
matter what was written. The two copies were byte-identical that day, which is
exactly why it was invisible.
`campaign` vouched for repos it could not read. `add /finnes/ikke` returned
`added` + exit 0; `refresh-tokens` then put the phantom in `swept[]` with a
0-token delta and left `skipped[]` empty, so the machine-wide bill claimed
coverage of three repos on a machine with two. Paths stay tracked — an unmounted
volume is a legitimate absence — but are reported as `addedUnverified`, and the
command names them.
Two class sweeps, both measured rather than assumed. `posture` was the single
scanner (1 of 14) whose fatal catch exited 1, which ux-rules defines as a normal
WARNING grade — a crash indistinguishable from a result. And all 13 payload
writers failed on a `--output-file` whose parent did not exist, which on a fresh
machine turned `campaign`'s first run into "the ledger may be corrupt"; they now
share `scanners/lib/write-output.mjs`.
Predicted breadth was too wide for the first time in five sessions: 6 of 8 CLIs
predicted to lack unknown-flag rejection, 4 measured. `drift` and `fix` already
reject them, via a construct the grep did not recognise — a grep matches an
implementation, the invariant is a behaviour. The sweep was rewritten to run each
CLI with a bogus flag and read the exit code.
Suite 1453 → 1469/0. Frozen snapshots untouched. `optimize-lens-cli` and
`token-hotspots-cli` share the unknown-flag defect and are deferred to the v5.14
argument-handling chunk with their positional-swallow arm; the count is recorded
in the guard rather than rounded down to zero.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012NHWjN8EnoxSqRvMTLK2NE
235 lines
8.5 KiB
JavaScript
235 lines
8.5 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* optimize-lens CLI — feeds the v5.7 optimization lens (CA-OPT) `/config-audit
|
|
* optimize` command. It produces the two halves of the hybrid motor as one JSON
|
|
* payload:
|
|
*
|
|
* 1. `deterministic` — the OPT scanner's high-precision findings (CA-OPT-001:
|
|
* a long numbered procedure in CLAUDE.md → skill). Already part of the
|
|
* orchestrated audit; surfaced here so /optimize is a complete view.
|
|
* 2. `candidates` — recall-oriented prose-judgment candidates from the
|
|
* lens-prefilter (lifecycle → hook, unscoped path-specific → rule, "never"
|
|
* → permission), each stamped with the CONFIRMED register entry it might fit
|
|
* (claim / recommendation / source / severity). The opus
|
|
* optimization-lens-agent is the precision gate over these.
|
|
*
|
|
* Only CONFIRMED register entries are attached (Verifiseringsplikt); a candidate
|
|
* whose register rule is missing or unconfirmed is dropped, so the agent never
|
|
* sees an unverifiable recommendation.
|
|
*
|
|
* Usage:
|
|
* node optimize-lens-cli.mjs [path] [--output-file <path>] [--global]
|
|
*
|
|
* Exit codes: 0=ok, 3=unrecoverable error. Zero external dependencies.
|
|
*/
|
|
|
|
import { resolve, sep } from 'node:path';
|
|
import { readFile, stat } from 'node:fs/promises';
|
|
import { writeOutputFile } from './lib/write-output.mjs';
|
|
import { discoverConfigFiles } from './lib/file-discovery.mjs';
|
|
import { resetCounter } from './lib/output.mjs';
|
|
import { parseFrontmatter } from './lib/yaml-parser.mjs';
|
|
import { loadRegister, getEntry } from './lib/best-practices-register.mjs';
|
|
import { prefilterClaudeMd, LENS_DETECTORS } from './lib/lens-prefilter.mjs';
|
|
import { subtractionCandidates, SUBTRACT_DETECTORS } from './lib/subtraction-prefilter.mjs';
|
|
import { scan as optScan } from './optimization-lens-scanner.mjs';
|
|
|
|
// Files under `.claude/plugins/` are shipped by an installed plugin — vendored
|
|
// CLAUDE.md plus its bundled tests/fixtures and examples. They are not the user's
|
|
// authored config, so a mechanism-fit suggestion against them is not actionable
|
|
// (the user can't edit a file the plugin overwrites on update). Excluded from the
|
|
// lens regardless of active/stale version. (M-BUG-11; mirrors the M-BUG-2 rule
|
|
// that keeps plugin-bundled config out of the conflict detector.)
|
|
const PLUGIN_TREE_MARKER = `.claude${sep}plugins${sep}`;
|
|
const isPluginBundled = (file) => (file.absPath || '').includes(PLUGIN_TREE_MARKER);
|
|
|
|
/** Confirmed register entry for `id`, or null. */
|
|
function confirmedEntry(register, id) {
|
|
const e = getEntry(register, id);
|
|
return e && e.confidence === 'confirmed' ? e : null;
|
|
}
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
let targetPath = '.';
|
|
let outputFile = null;
|
|
let includeGlobal = false;
|
|
let subtract = false;
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (args[i] === '--global') includeGlobal = true;
|
|
else if (args[i] === '--subtract') subtract = true;
|
|
else if (args[i] === '--output-file' && args[i + 1]) outputFile = args[++i];
|
|
else if (!args[i].startsWith('-')) targetPath = args[i];
|
|
}
|
|
|
|
const absPath = resolve(targetPath);
|
|
try {
|
|
const s = await stat(absPath);
|
|
if (!s.isDirectory()) {
|
|
process.stderr.write(`Error: ${absPath} is not a directory\n`);
|
|
process.exitCode = 3;
|
|
return;
|
|
}
|
|
} catch {
|
|
process.stderr.write(`Error: path does not exist: ${absPath}\n`);
|
|
process.exitCode = 3;
|
|
return;
|
|
}
|
|
|
|
// Load the register once; tolerate its absence (deterministic half still runs).
|
|
let register = null;
|
|
try {
|
|
register = loadRegister();
|
|
} catch {
|
|
register = null;
|
|
}
|
|
|
|
resetCounter();
|
|
const rawDiscovery = await discoverConfigFiles(absPath, { includeGlobal });
|
|
// Scope the lens to the user's authored config: drop plugin-bundled files for
|
|
// BOTH halves of the motor (the OPT scanner reads discovery.files directly).
|
|
const discovery = {
|
|
...rawDiscovery,
|
|
files: (rawDiscovery.files || []).filter((f) => !isPluginBundled(f)),
|
|
};
|
|
|
|
// ── Deterministic half: the OPT scanner (CA-OPT-001) ──
|
|
const opt = await optScan(absPath, discovery);
|
|
|
|
// ── Recall half: prose-judgment candidates from the pre-filter ──
|
|
const claudeMdFiles = (discovery.files || []).filter((f) => f.type === 'claude-md');
|
|
const candidates = [];
|
|
// Opt-in only: the subtraction axis asks a different question and must not
|
|
// fire on a plain `/config-audit optimize` run (brief §7 q3).
|
|
const subtractCands = [];
|
|
const subtractEntry = subtract && register ? confirmedEntry(register, 'BP-SUB-001') : null;
|
|
|
|
for (const file of claudeMdFiles) {
|
|
let content;
|
|
try {
|
|
content = await readFile(file.absPath, 'utf-8');
|
|
} catch {
|
|
continue;
|
|
}
|
|
const parsed = parseFrontmatter(content);
|
|
const body = parsed.body || content;
|
|
const bodyStartLine = parsed.bodyStartLine || 1;
|
|
|
|
if (subtractEntry) {
|
|
for (const cand of subtractionCandidates(body)) {
|
|
subtractCands.push({
|
|
file: file.absPath,
|
|
line: bodyStartLine - 1 + cand.startLine,
|
|
endLine: bodyStartLine - 1 + cand.endLine,
|
|
lineCount: cand.lineCount,
|
|
lensCheck: cand.lensCheck,
|
|
mechanism: cand.mechanism,
|
|
signalText: cand.text,
|
|
register: {
|
|
id: subtractEntry.id,
|
|
claim: subtractEntry.claim,
|
|
recommendation: subtractEntry.recommendation || null,
|
|
severity: subtractEntry.severity || 'low',
|
|
source: subtractEntry.source,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
for (const cand of prefilterClaudeMd(body)) {
|
|
const entry = register ? confirmedEntry(register, cand.registerId) : null;
|
|
if (!entry) continue; // never surface an unverifiable recommendation
|
|
candidates.push({
|
|
// Absolute path: unique + readable. relPath collides across scopes
|
|
// (a repo-root `CLAUDE.md` and the user-global `~/.claude/CLAUDE.md`
|
|
// both relPath to `CLAUDE.md`), which would send the agent's Read() to
|
|
// the wrong file. (M-BUG-11)
|
|
file: file.absPath,
|
|
line: bodyStartLine - 1 + cand.line,
|
|
lensCheck: cand.lensCheck,
|
|
mechanism: cand.mechanism,
|
|
signalText: cand.text,
|
|
register: {
|
|
id: entry.id,
|
|
claim: entry.claim,
|
|
recommendation: entry.recommendation || null,
|
|
severity: entry.severity || 'low',
|
|
source: entry.source,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
// The CONFIRMED prose-judgment entries, so the agent has full provenance even
|
|
// for a detector class that produced no candidates this run.
|
|
const registerEntries = register
|
|
? LENS_DETECTORS.map((d) => confirmedEntry(register, d.registerId))
|
|
.filter(Boolean)
|
|
.map((e) => ({
|
|
id: e.id,
|
|
lensCheck: e.lensCheck,
|
|
claim: e.claim,
|
|
recommendation: e.recommendation || null,
|
|
mechanism: e.mechanism || null,
|
|
severity: e.severity || 'low',
|
|
source: e.source,
|
|
}))
|
|
: [];
|
|
|
|
const payload = {
|
|
status: 'ok',
|
|
target: absPath,
|
|
deterministic: opt.findings || [],
|
|
candidates,
|
|
register: registerEntries,
|
|
counts: {
|
|
deterministic: (opt.findings || []).length,
|
|
candidates: candidates.length,
|
|
byLensCheck: candidates.reduce((acc, c) => {
|
|
acc[c.lensCheck] = (acc[c.lensCheck] || 0) + 1;
|
|
return acc;
|
|
}, {}),
|
|
},
|
|
};
|
|
|
|
// Additive ONLY under --subtract: a plain run's payload must stay byte-identical.
|
|
if (subtract) {
|
|
payload.subtract = {
|
|
enabled: true,
|
|
candidates: subtractCands,
|
|
register: subtractEntry
|
|
? [
|
|
{
|
|
id: subtractEntry.id,
|
|
lensCheck: subtractEntry.lensCheck,
|
|
claim: subtractEntry.claim,
|
|
recommendation: subtractEntry.recommendation || null,
|
|
mechanism: subtractEntry.mechanism || null,
|
|
severity: subtractEntry.severity || 'low',
|
|
source: subtractEntry.source,
|
|
},
|
|
]
|
|
: [],
|
|
detectors: SUBTRACT_DETECTORS.map((d) => ({ ...d })),
|
|
};
|
|
payload.counts.subtractCandidates = subtractCands.length;
|
|
}
|
|
|
|
const json = JSON.stringify(payload, null, 2);
|
|
if (outputFile) {
|
|
await writeOutputFile(outputFile, json, 'utf-8');
|
|
}
|
|
if (!outputFile) {
|
|
process.stdout.write(json + '\n');
|
|
}
|
|
}
|
|
|
|
const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname);
|
|
if (isDirectRun) {
|
|
main().catch((err) => {
|
|
process.stderr.write(`Fatal: ${err.message}\n`);
|
|
process.exitCode = 3;
|
|
});
|
|
}
|