feat(review): the critic's own security key SECURITY_RISK (decision 167)

lib/review/rule-catalogue.mjs gains SECURITY_RISK (BLOCKER, category
security, memory route rule): Voyage's own security key for a finding by
its own reviewers that SECURITY_INJECTION does not cover.
SECURITY_REVIEW_VULNERABILITY is described as the engines' key only.
plan-critic and scope-guardian name SECURITY_INJECTION or SECURITY_RISK
and no longer the engines' key.

The count moves from 17 to 18 in every pin: rule-catalogue.test,
claude-md-scope [CAT] (which now also refuses a stale 17), doc-consistency,
review-prose-pins, docs/architecture.md and the two reviewer agents.
critic-severity-rules pins three security keys; its "every security key
named" check leaves out the engines' key, and a new pin says the critic
never names it.

Existing tests changed for the planned rule: the four count pins above
(17 -> 18), and critic-severity-rules' two-key test (now three keys).
tests/lib/engine-findings.test.mjs: a comment naming the "(17 keys)" pin
now says 18.

Part 2, step 2.3 of the E plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-08 08:55:34 +01:00
commit 5cb2918baa
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
12 changed files with 67 additions and 31 deletions

View file

@ -42,19 +42,19 @@ test('[CAT] the catalogue holds CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE at MINOR
);
});
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);
test('[CAT] the catalogue has 18 keys and docs/architecture.md and both reviewer agents say 18-key', () => {
assert.equal(RULE_CATALOGUE.length, 18);
assert.equal(RULE_KEYS.size, 18);
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');
assert.match(header, /\b18 rule keys\b/, 'the catalogue header counts 18 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\)/);
assert.match(arch, /rule catalogue \(18 keys\)/);
assert.match(arch, /the 18-key rule catalogue/);
assert.doesNotMatch(arch, /\b(12|15|17)-key rule catalogue|rule catalogue \((12|15|17) 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`);
assert.match(text, /the 18-key catalogue/, `${agent} says 18-key`);
assert.doesNotMatch(text, /\b(12|15|17)-key catalogue/, `${agent} no longer says 17-key`);
}
});