/** * lens-prefilter tests — the deterministic, recall-oriented half of the v5.7 * optimization-lens hybrid motor (Chunk 2b). * * The pre-filter is intentionally CHEAP and recall-oriented: it surfaces * candidate lines for the three prose-judgment mechanism-fit classes that the * deterministic OPT scanner (Chunk 2a) deliberately skips. The opus * optimization-lens-agent is the PRECISION gate downstream — so these tests * assert the pre-filter (a) detects the obvious signals and tags them with the * right register id / lensCheck / mechanism, (b) reports correct 1-based line * numbers, and (c) does not fire inside fenced code blocks. They do NOT demand * zero false positives — that is the agent's job, by design. */ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { prefilterClaudeMd, LENS_DETECTORS } from '../../scanners/lib/lens-prefilter.mjs'; const byCheck = (candidates, lensCheck) => candidates.filter((c) => c.lensCheck === lensCheck); describe('lens-prefilter — detector table', () => { it('exposes the three prose-judgment detectors keyed by register id + lensCheck', () => { const map = new Map(LENS_DETECTORS.map((d) => [d.lensCheck, d])); assert.deepEqual(map.get('claude-md-lifecycle-phrasing')?.registerId, 'BP-MECH-001'); assert.deepEqual(map.get('claude-md-lifecycle-phrasing')?.mechanism, 'hook'); assert.deepEqual(map.get('unscoped-path-specific-instruction')?.registerId, 'BP-MECH-002'); assert.deepEqual(map.get('unscoped-path-specific-instruction')?.mechanism, 'rule'); assert.deepEqual(map.get('never-instruction')?.registerId, 'BP-MECH-004'); assert.deepEqual(map.get('never-instruction')?.mechanism, 'permission'); }); }); describe('lens-prefilter — lifecycle phrasing (BP-MECH-001 → hook)', () => { it('flags "After every commit, run the linter."', () => { const c = byCheck(prefilterClaudeMd('After every commit, run the linter.'), 'claude-md-lifecycle-phrasing'); assert.equal(c.length, 1); assert.equal(c[0].registerId, 'BP-MECH-001'); assert.equal(c[0].mechanism, 'hook'); assert.equal(c[0].line, 1); assert.match(c[0].text, /After every commit/); }); it('flags several lifecycle markers (before each, every time, whenever)', () => { const text = [ 'Before each push, run the test suite.', 'Every time you edit a config file, re-validate it.', 'Whenever you finish a task, update STATE.md.', ].join('\n'); assert.equal(byCheck(prefilterClaudeMd(text), 'claude-md-lifecycle-phrasing').length, 3); }); it('does not flag a plain fact with no lifecycle phrasing', () => { assert.equal( byCheck(prefilterClaudeMd('This project uses TypeScript and pnpm.'), 'claude-md-lifecycle-phrasing').length, 0, ); }); }); describe('lens-prefilter — never-instruction (BP-MECH-004 → permission)', () => { it('flags "Never commit secrets to the repository."', () => { const c = byCheck(prefilterClaudeMd('Never commit secrets to the repository.'), 'never-instruction'); assert.equal(c.length, 1); assert.equal(c[0].registerId, 'BP-MECH-004'); assert.equal(c[0].mechanism, 'permission'); }); it('flags a bulleted "never" directive', () => { const c = byCheck(prefilterClaudeMd('- Never push directly to main.'), 'never-instruction'); assert.equal(c.length, 1); }); }); describe('lens-prefilter — unscoped path-specific instruction (BP-MECH-002 → rule)', () => { it('flags an instruction referencing a glob', () => { const c = byCheck(prefilterClaudeMd('Always format src/**/*.ts with prettier.'), 'unscoped-path-specific-instruction'); assert.equal(c.length, 1); assert.equal(c[0].registerId, 'BP-MECH-002'); assert.equal(c[0].mechanism, 'rule'); }); it('flags an instruction referencing a concrete filename', () => { const c = byCheck(prefilterClaudeMd('Run migrations from db/schema.sql before tests.'), 'unscoped-path-specific-instruction'); assert.equal(c.length, 1); }); it('does not flag a bare path reference with no instruction verb', () => { assert.equal( byCheck(prefilterClaudeMd('See docs/architecture.md for the overview.'), 'unscoped-path-specific-instruction').length, 0, ); }); }); describe('lens-prefilter — structural rules', () => { it('reports correct 1-based line numbers', () => { const text = ['# Title', '', 'Some facts here.', 'Never delete the .env file.'].join('\n'); const c = byCheck(prefilterClaudeMd(text), 'never-instruction'); assert.equal(c.length, 1); assert.equal(c[0].line, 4); }); it('ignores signals inside fenced code blocks', () => { const text = [ 'Real prose: never run this in production.', '```bash', '# after every deploy, never skip the smoke test', 'echo "every time"', '```', 'More prose after the fence.', ].join('\n'); const all = prefilterClaudeMd(text); // only the first prose line's "never" should match; nothing inside the fence assert.ok(all.every((c) => c.line === 1), `expected only line 1; got lines ${all.map((c) => c.line).join(',')}`); }); it('returns an empty array for a clean facts-only document', () => { const text = [ '# Project', 'A web API written in Go. Data lives in Postgres.', 'The team deploys via GitHub Actions.', ].join('\n'); assert.deepEqual(prefilterClaudeMd(text), []); }); it('a single line can yield candidates for two distinct detectors', () => { // "never" + a concrete path → both the prohibition and the path-scope angle. const c = prefilterClaudeMd('Never edit src/config.ts by hand.'); const checks = new Set(c.map((x) => x.lensCheck)); assert.ok(checks.has('never-instruction')); assert.ok(checks.has('unscoped-path-specific-instruction')); }); });