fix(scanners): the fix engine downgraded a near-miss on xhigh to high (C2)
`fix-engine.mjs` carried its own copy of the valid `effortLevel` list, and that copy had gone stale on `xhigh` (CC 2.1.154's top Opus tier) while `settings-validator.mjs` had all five. The nearest-match "fix" therefore corrected `xhig` — and `XHIGH` — to `high`: the tool silently changed the tier the user asked for, in the one code path whose whole job is to write the corrected value back to disk. Fixed by sharing the validator's table instead of aligning the copy, so the two cannot drift again. Guard asserts the blanket invariant — a one-character near-miss on EVERY valid tier corrects back to that same tier — rather than pinning the one level that happened to be missing; verified red against the original stale array. Suite 1488/0. Frozen v5.0.0 snapshots untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014P6Rh59Mtj4uYrdYMCYZJE
This commit is contained in:
parent
c76dc537ce
commit
3d6ddb273c
3 changed files with 52 additions and 4 deletions
|
|
@ -10,6 +10,7 @@ import { discoverConfigFiles } from '../../scanners/lib/file-discovery.mjs';
|
|||
import { runAllScanners } from '../../scanners/scan-orchestrator.mjs';
|
||||
import { planFixes, applyFixes, verifyFixes, FIX_TYPES } from '../../scanners/fix-engine.mjs';
|
||||
import { parseJson, parseFrontmatter } from '../../scanners/lib/yaml-parser.mjs';
|
||||
import { VALID_EFFORT_LEVELS } from '../../scanners/settings-validator.mjs';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
const FIXTURES = resolve(__dirname, '../fixtures');
|
||||
|
|
@ -271,6 +272,49 @@ describe('applyFixes on tmp copy', () => {
|
|||
assert.ok(newStat.isFile());
|
||||
});
|
||||
|
||||
// C2 — the nearest-match table for effortLevel dropped `xhigh` (CC 2.1.154),
|
||||
// so the engine "corrected" a near-miss on the top Opus tier down to `high`:
|
||||
// a silent downgrade of the very setting the user asked for. Behaviour-level
|
||||
// (scan → plan → apply), because the helper itself is not exported.
|
||||
async function fixEffortLevel(dir, raw) {
|
||||
const settingsPath = join(dir, '.claude', 'settings.json');
|
||||
const before = parseJson(await readFile(settingsPath, 'utf-8'));
|
||||
before.effortLevel = raw;
|
||||
await writeFile(settingsPath, `${JSON.stringify(before, null, 2)}\n`, 'utf-8');
|
||||
|
||||
resetCounter();
|
||||
const env = await runAllScanners(dir);
|
||||
const { fixes } = planFixes(env);
|
||||
const effortFix = fixes.filter(f => f.key === 'effortLevel');
|
||||
assert.strictEqual(effortFix.length, 1, `"${raw}" must be planned as exactly one effortLevel fix`);
|
||||
await applyFixes(effortFix, { dryRun: false, backupDir: dir });
|
||||
|
||||
return parseJson(await readFile(settingsPath, 'utf-8')).effortLevel;
|
||||
}
|
||||
|
||||
// Blanket invariant, not just the level that happened to be missing: for
|
||||
// EVERY valid tier, a one-character near-miss must correct back to that same
|
||||
// tier. A test pinned to `xhig` alone would go green again if a future edit
|
||||
// dropped a different level from the table.
|
||||
it('corrects a near-miss on every valid tier back to that tier (C2)', async () => {
|
||||
for (const level of VALID_EFFORT_LEVELS) {
|
||||
const nearMiss = level.slice(0, -1);
|
||||
assert.strictEqual(
|
||||
await fixEffortLevel(tmpDir, nearMiss),
|
||||
level,
|
||||
`"${nearMiss}" must correct to "${level}" — any other target silently changes the tier the user asked for`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('corrects a case variant of "xhigh" to xhigh, not high (C2)', async () => {
|
||||
assert.strictEqual(
|
||||
await fixEffortLevel(tmpDir, 'XHIGH'),
|
||||
'xhigh',
|
||||
'the user named a real tier in the wrong case; the fix must keep the tier',
|
||||
);
|
||||
});
|
||||
|
||||
it('validates JSON output after fix', async () => {
|
||||
const { fixes } = planFixes(envelope);
|
||||
const jsonFixes = fixes.filter(f => f.file.endsWith('.json'));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue