feat(review): two CLAUDE.md rule keys at MINOR, the catalogue counts 17

This commit is contained in:
Kjell Tore Guttormsen 2026-11-02 13:53:24 +01:00
commit ed67526987
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
9 changed files with 99 additions and 11 deletions

View file

@ -0,0 +1,65 @@
// tests/lib/claude-md-scope.test.mjs
// Fix round 4.5b2 part B, Step 1 (tag CAT): the rule catalogue holds the two
// CLAUDE.md keys at MINOR (decision 98), and every text that counts the keys
// says 17. Each [CAT] case fails at 96fba75 on an assertion.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { RULE_CATALOGUE, RULE_KEYS, getRule } from '../../lib/review/rule-catalogue.mjs';
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
const read = (rel) => {
const p = join(ROOT, rel);
assert.ok(existsSync(p), `${rel} exists`);
return readFileSync(p, 'utf-8');
};
test('[CAT] the catalogue holds CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE at MINOR with memory_route null', () => {
for (const key of ['CLAUDE_MD_DEVIATION', 'CLAUDE_MD_STALE']) {
assert.ok(RULE_KEYS.has(key), `RULE_KEYS holds ${key}`);
const rule = getRule(key);
assert.ok(rule !== null, `getRule(${key}) finds the entry`);
assert.equal(rule.severity, 'MINOR', `${key} is MINOR`);
assert.equal(rule.category, 'conformance', `${key} is conformance`);
assert.equal(rule.memory_route, null, `${key} is never proposed as project memory`);
assert.ok(typeof rule.description === 'string' && rule.description.length > 0, `${key} has a description`);
}
assert.equal(
getRule('CLAUDE_MD_DEVIATION').description,
"A change that breaks a line of the target repo's tracked CLAUDE.md, AGENTS.md or .claude/rules file.",
);
assert.equal(
getRule('CLAUDE_MD_STALE').description,
'A change that makes a line of such an instruction file untrue.',
);
});
test('[CAT] the catalogue has 17 keys and docs/architecture.md and both reviewer agents say 17-key', () => {
assert.equal(RULE_CATALOGUE.length, 17);
assert.equal(RULE_KEYS.size, 17);
const header = read('lib/review/rule-catalogue.mjs').split('\n').slice(0, 10).join('\n');
assert.match(header, /\b17 rule keys\b/, 'the catalogue header counts 17 rule keys');
const arch = read('docs/architecture.md');
assert.match(arch, /rule catalogue \(17 keys\)/);
assert.match(arch, /the 17-key rule catalogue/);
assert.doesNotMatch(arch, /\b(12|15)-key rule catalogue|rule catalogue \((12|15) keys\)/);
for (const agent of ['agents/code-correctness-reviewer.md', 'agents/brief-conformance-reviewer.md']) {
const text = read(agent);
assert.match(text, /the 17-key catalogue/, `${agent} says 17-key`);
assert.doesNotMatch(text, /\b(12|15)-key catalogue/, `${agent} no longer says 12-key`);
}
});
test('[CAT] the armB RULE_TIERS table holds both keys at MINOR', () => {
const src = read('scripts/trekreview-armB.workflow.mjs');
const start = src.indexOf('const RULE_TIERS = {');
assert.ok(start !== -1, 'the script carries a RULE_TIERS literal block');
const block = src.slice(start, src.indexOf('};', start));
const tiers = Object.fromEntries([...block.matchAll(/([A-Z_]+):\s*'([A-Z]+)'/g)].map((m) => [m[1], m[2]]));
assert.equal(tiers.CLAUDE_MD_DEVIATION, 'MINOR');
assert.equal(tiers.CLAUDE_MD_STALE, 'MINOR');
assert.equal(Object.keys(tiers).length, 17);
});

View file

@ -270,12 +270,12 @@ test('HANDOVER-CONTRACTS.md Handover 7 § Lifecycle names --cleanup and produced
// file" (commands/trekcontinue.md exists, so that loop already asserts the
// /trekcontinue reference). Single-command existence pin → redundant.
test('rule-catalogue has exactly 15 entries', async () => {
test('rule-catalogue has exactly 17 entries', async () => {
const mod = await import('../../lib/review/rule-catalogue.mjs');
assert.strictEqual(
mod.RULE_CATALOGUE.length,
15,
'lib/review/rule-catalogue.mjs RULE_CATALOGUE size invariant: must be 15 (12 v1.0 baseline + 2 engine keys, ordre 5, + CODE_REVIEW_IMPROVEMENT, lekerepo trial 2)',
17,
'lib/review/rule-catalogue.mjs RULE_CATALOGUE size invariant: must be 17 (12 v1.0 baseline + 2 engine keys, ordre 5, + CODE_REVIEW_IMPROVEMENT, lekerepo trial 2, + CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE, decision 98)',
);
});

View file

@ -39,9 +39,14 @@ test('RULE_CATALOGUE — all category values within enum', () => {
}
});
test('RULE_KEYS.size === RULE_CATALOGUE.length (== 15) — pinned by doc-consistency', () => {
test('RULE_KEYS.size === RULE_CATALOGUE.length (== 17) — pinned by doc-consistency', () => {
assert.equal(RULE_KEYS.size, RULE_CATALOGUE.length);
assert.equal(RULE_CATALOGUE.length, 15);
assert.equal(RULE_CATALOGUE.length, 17);
});
test('CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE are MINOR', () => {
assert.equal(getRule('CLAUDE_MD_DEVIATION')?.severity, 'MINOR');
assert.equal(getRule('CLAUDE_MD_STALE')?.severity, 'MINOR');
});
test('RULE_CATALOGUE — the two Claude Code engine keys exist at their tiers (ordre 5)', () => {

View file

@ -218,7 +218,7 @@ test('A7 (review): the session-spec template stages before it commits too', () =
});
test('docs (review): the counts and the Declined-to-Judge rule match the code', () => {
assert.match(read('docs/architecture.md'), /rule catalogue \(15 keys\)/);
assert.match(read('docs/architecture.md'), /rule catalogue \(17 keys\)/);
assert.match(read('docs/HANDOVER-CONTRACTS.md'), /three engine rule keys/);
assert.doesNotMatch(read('lib/review/engine-findings.mjs'), /it forbids ALLOW and is listed under/);
assert.match(read('agents/review-coordinator.md'), /every `\[unverified\]` one, and every\s+out-of-scope engine finding/);