fix(acr): file-discovery skips backups/ dirs — never live config (M-BUG-8)
A directory named `backups` holds backup COPIES, not live config, so walking it during a config audit produces stale findings. config-audit's own session backups (~/.claude/config-audit/backups/<ts>/files/.../CLAUDE.md) were the canonical case: a ~/.claude-scope audit walked 36 frozen config copies as if live, polluting CPS (C3) and HKV/RUL (C6) results. Same non-live-noise family as M-BUG-2. - Add `backups` to SKIP_DIRS (broad, name-based — consistent with vendor/dist/ .cache): the rule is general, a backups/ dir is never live config. TDD: 2 failing tests (no config under backups/ discovered + backups/ counted as skipped) -> green; a third asserts live config beside the backups tree is still discovered. Full suite 1310/0 (+3). Byte-stable: no fixture is named `backups` and `backups` appears in zero frozen snapshots, so v5.0.0 + SC-5 + default-output outputs are unchanged. Dogfooding ~/.claude: files under /backups/ drop from 36 -> 0; 717 live config files retained. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EnUvKEqyEa1m9gy6Aqhdqq
This commit is contained in:
parent
346dfac6fa
commit
bfd577aeee
2 changed files with 52 additions and 1 deletions
|
|
@ -11,6 +11,11 @@ const SKIP_DIRS = new Set([
|
||||||
'node_modules', '.git', 'dist', 'build', 'coverage', '__pycache__',
|
'node_modules', '.git', 'dist', 'build', 'coverage', '__pycache__',
|
||||||
'.next', '.nuxt', '.output', '.cache', '.turbo', '.parcel-cache',
|
'.next', '.nuxt', '.output', '.cache', '.turbo', '.parcel-cache',
|
||||||
'vendor', 'venv', '.venv', '.tox',
|
'vendor', 'venv', '.venv', '.tox',
|
||||||
|
// A `backups` dir holds backup COPIES, not live config — auditing it as if
|
||||||
|
// live produces stale findings. config-audit's own session backups
|
||||||
|
// (~/.claude/config-audit/backups/<ts>/files/.../CLAUDE.md) are the canonical
|
||||||
|
// case (M-BUG-8), but the rule is general: backups are never live config.
|
||||||
|
'backups',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Path marker for the plugin install cache (~/.claude/plugins/cache).
|
// Path marker for the plugin install cache (~/.claude/plugins/cache).
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, it, before, after } from 'node:test';
|
import { describe, it, before, after } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { join } from 'node:path';
|
import { join, sep } from 'node:path';
|
||||||
import { mkdir, writeFile, rm, stat } from 'node:fs/promises';
|
import { mkdir, writeFile, rm, stat } from 'node:fs/promises';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import {
|
import {
|
||||||
|
|
@ -509,3 +509,49 @@ describe('discoverConfigFiles — cache-aware filtering', () => {
|
||||||
assert.ok(!keys.has(STALE1) && !keys.has(STALE2), 'stale dropped via Multi');
|
assert.ok(!keys.has(STALE1) && !keys.has(STALE2), 'stale dropped via Multi');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────────────────────
|
||||||
|
// Group 8: backups/ skip (M-BUG-8) — a directory named `backups`
|
||||||
|
// holds backup COPIES, not live config. config-audit's own session
|
||||||
|
// backups (~/.claude/config-audit/backups/<ts>/files/plugins/*/CLAUDE.md)
|
||||||
|
// were walked as if live, producing stale findings on a ~/.claude audit.
|
||||||
|
// ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('discoverConfigFiles — skips backups/ (M-BUG-8)', () => {
|
||||||
|
let dir;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
dir = tempDir('backups');
|
||||||
|
// A live CLAUDE.md at the root (must still be discovered).
|
||||||
|
await mkdir(dir, { recursive: true });
|
||||||
|
await writeFile(join(dir, 'CLAUDE.md'), '# Live config');
|
||||||
|
// config-audit's own backup tree — frozen copies, NOT live config.
|
||||||
|
const backupClaude = join(dir, 'config-audit', 'backups', '20260518_103233', 'files', 'plugins', 'voyage');
|
||||||
|
await mkdir(backupClaude, { recursive: true });
|
||||||
|
await writeFile(join(backupClaude, 'CLAUDE.md'), '# Frozen backup copy');
|
||||||
|
const backupSettings = join(dir, 'config-audit', 'backups', '20260518_103233', 'files', '.claude');
|
||||||
|
await mkdir(backupSettings, { recursive: true });
|
||||||
|
await writeFile(join(backupSettings, 'settings.json'), '{}');
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await rm(dir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discovers the live CLAUDE.md', async () => {
|
||||||
|
const { files } = await discoverConfigFiles(dir);
|
||||||
|
const live = files.find(f => f.relPath === 'CLAUDE.md');
|
||||||
|
assert.ok(live, 'live CLAUDE.md at root should be discovered');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does NOT discover config files under a backups/ directory', async () => {
|
||||||
|
const { files } = await discoverConfigFiles(dir);
|
||||||
|
const inBackups = files.filter(f => f.absPath.includes(`backups${sep}`));
|
||||||
|
assert.equal(inBackups.length, 0, 'no files under backups/ should be discovered');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('counts backups/ as a skipped directory', async () => {
|
||||||
|
const { skipped } = await discoverConfigFiles(dir);
|
||||||
|
assert.ok(skipped >= 1, 'backups/ should be counted as skipped');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue