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:
parent
bec4500b8f
commit
5cb2918baa
12 changed files with 67 additions and 31 deletions
|
|
@ -33,7 +33,7 @@ You will receive a prompt containing:
|
||||||
/trekreview triage gate. Respect `skip` decisions; do NOT flag
|
/trekreview triage gate. Respect `skip` decisions; do NOT flag
|
||||||
skipped files unless the skip itself is wrong (then emit
|
skipped files unless the skip itself is wrong (then emit
|
||||||
`COVERAGE_SILENT_SKIP`).
|
`COVERAGE_SILENT_SKIP`).
|
||||||
- **Rule catalogue** — the 17-key catalogue in
|
- **Rule catalogue** — the 18-key catalogue in
|
||||||
`lib/review/rule-catalogue.mjs`. You may only emit findings whose
|
`lib/review/rule-catalogue.mjs`. You may only emit findings whose
|
||||||
`rule_key` is in this set.
|
`rule_key` is in this set.
|
||||||
- **Success-criteria check results** — a table produced by /trekreview
|
- **Success-criteria check results** — a table produced by /trekreview
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ You will receive a prompt containing:
|
||||||
context only. The brief is not your contract — it is the conformance
|
context only. The brief is not your contract — it is the conformance
|
||||||
reviewer's contract. You evaluate code correctness regardless of
|
reviewer's contract. You evaluate code correctness regardless of
|
||||||
what the brief promised.
|
what the brief promised.
|
||||||
- **Rule catalogue** — the 17-key catalogue in
|
- **Rule catalogue** — the 18-key catalogue in
|
||||||
`lib/review/rule-catalogue.mjs`. You may only emit findings whose
|
`lib/review/rule-catalogue.mjs`. You may only emit findings whose
|
||||||
`rule_key` is in this set.
|
`rule_key` is in this set.
|
||||||
- **Instruction files** — the repo's tracked instruction files for this
|
- **Instruction files** — the repo's tracked instruction files for this
|
||||||
|
|
|
||||||
|
|
@ -290,8 +290,8 @@ implementation catches nothing, so the finding stays major. Give
|
||||||
`lowered_from` and `caught_by` when you lower a finding this way.
|
`lowered_from` and `caught_by` when you lower a finding this way.
|
||||||
|
|
||||||
A security finding uses the catalogue's security `rule_key`
|
A security finding uses the catalogue's security `rule_key`
|
||||||
(`SECURITY_INJECTION`, or `SECURITY_REVIEW_VULNERABILITY` when no narrower key
|
(`SECURITY_INJECTION`, or `SECURITY_RISK` when no narrower key fits); the
|
||||||
fits).
|
engines' key is never yours.
|
||||||
|
|
||||||
Your rating decides what happens next: `/trekplan` records your findings with
|
Your rating decides what happens next: `/trekplan` records your findings with
|
||||||
`plan-gate.mjs --record`, and the plan gate stops the plan while any BLOCKER
|
`plan-gate.mjs --record`, and the plan gate stops the plan while any BLOCKER
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ implementation catches nothing, so the finding stays major. Give
|
||||||
`lowered_from` and `caught_by` when you lower a finding this way.
|
`lowered_from` and `caught_by` when you lower a finding this way.
|
||||||
|
|
||||||
A security finding uses the catalogue's security `rule_key`
|
A security finding uses the catalogue's security `rule_key`
|
||||||
(`SECURITY_INJECTION`, or `SECURITY_REVIEW_VULNERABILITY` when no narrower key
|
(`SECURITY_INJECTION`, or `SECURITY_RISK` when no narrower key fits); the
|
||||||
fits).
|
engines' key is never yours.
|
||||||
|
|
||||||
Your findings go through `plan-gate.mjs --record` with plan-critic's; an open
|
Your findings go through `plan-gate.mjs --record` with plan-critic's; an open
|
||||||
blocker or major stops the plan. `severity` is exactly `blocker`, `major` or `minor`;
|
blocker or major stops the plan. `severity` is exactly `blocker`, `major` or `minor`;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Imported from `CLAUDE.md` via pointer.
|
||||||
|
|
||||||
- `lib/util/{frontmatter,result,atomic-write,autonomy-gate}.mjs` — shared YAML-frontmatter parser + Result helpers + `atomicWriteJson(path, obj)` for tmp+rename writes + `--gates` flag parser (v3.4.0)
|
- `lib/util/{frontmatter,result,atomic-write,autonomy-gate}.mjs` — shared YAML-frontmatter parser + Result helpers + `atomicWriteJson(path, obj)` for tmp+rename writes + `--gates` flag parser (v3.4.0)
|
||||||
- `lib/parsers/{plan-schema,manifest-yaml,arg-parser,jaccard,finding-id}.mjs` — pure parsers (no I/O), unit-tested. `manifest-yaml` extended in v3.4.0 with additive `skip_commit_check` + `memory_write` flags (forward-compat: unknown keys ignored)
|
- `lib/parsers/{plan-schema,manifest-yaml,arg-parser,jaccard,finding-id}.mjs` — pure parsers (no I/O), unit-tested. `manifest-yaml` extended in v3.4.0 with additive `skip_commit_check` + `memory_write` flags (forward-compat: unknown keys ignored)
|
||||||
- `lib/review/{rule-catalogue,plan-review-dedup}.mjs` — version-pinned rule catalogue (17 keys) + Phase 9 inline dedup helpers (v3.4.0)
|
- `lib/review/{rule-catalogue,plan-review-dedup}.mjs` — version-pinned rule catalogue (18 keys) + Phase 9 inline dedup helpers (v3.4.0)
|
||||||
- `lib/review/{verdict-gate,engine-findings,recurrence}.mjs` — the review gate (K7: 0 ALLOW, or WARN with every counting MAJOR answered · 1 BLOCK · 2 never green), Claude Code `/code-review` + `/security-review` findings → reviewer payload, recurring finding classes → operator-approved project-memory lines (review_version 1.2); `lib/review/fill-finding-ids.mjs` computes the coordinator's `@@ID_<n>@@` placeholders over `(file, line, rule_key)`; `lib/validators/brief-review-stamp.mjs` lets `/trekplan` skip a brief review `/trekbrief` already ran on the same text
|
- `lib/review/{verdict-gate,engine-findings,recurrence}.mjs` — the review gate (K7: 0 ALLOW, or WARN with every counting MAJOR answered · 1 BLOCK · 2 never green), Claude Code `/code-review` + `/security-review` findings → reviewer payload, recurring finding classes → operator-approved project-memory lines (review_version 1.2); `lib/review/fill-finding-ids.mjs` computes the coordinator's `@@ID_<n>@@` placeholders over `(file, line, rule_key)`; `lib/validators/brief-review-stamp.mjs` lets `/trekplan` skip a brief review `/trekbrief` already ran on the same text
|
||||||
- `lib/validators/{brief,research,plan,progress,session-state}-validator.mjs` — schema validators with CLI shims (`node lib/validators/X.mjs --json <path>`)
|
- `lib/validators/{brief,research,plan,progress,session-state}-validator.mjs` — schema validators with CLI shims (`node lib/validators/X.mjs --json <path>`)
|
||||||
- `lib/validators/architecture-discovery.mjs` — drift-WARN external-contract discovery for `architecture/overview.md`
|
- `lib/validators/architecture-discovery.mjs` — drift-WARN external-contract discovery for `architecture/overview.md`
|
||||||
|
|
@ -46,7 +46,7 @@ Doc-consistency test at `tests/lib/doc-consistency.test.mjs` pins agent-table co
|
||||||
|
|
||||||
**Continue:** `/trekcontinue` reads `{dir}/.session-state.local.json` (Handover 7), validates schema-v1 via `session-state-validator`, narrates a 3-line summary (project / next-session-label / brief-path), and immediately begins executing the next session. Auto-discovers active project state files under `.claude/projects/*/.session-state.local.json` if no explicit `<project-dir>` argument. Operator-invoked only — never auto-loaded via SessionStart. The `/trekendsession` helper is the informal-flow producer: writes the same state file for ad-hoc multi-session handovers that don't run through `/trekexecute`.
|
**Continue:** `/trekcontinue` reads `{dir}/.session-state.local.json` (Handover 7), validates schema-v1 via `session-state-validator`, narrates a 3-line summary (project / next-session-label / brief-path), and immediately begins executing the next session. Auto-discovers active project state files under `.claude/projects/*/.session-state.local.json` if no explicit `<project-dir>` argument. Operator-invoked only — never auto-loaded via SessionStart. The `/trekendsession` helper is the informal-flow producer: writes the same state file for ad-hoc multi-session handovers that don't run through `/trekexecute`.
|
||||||
|
|
||||||
**Delegate the engine, keep the policy (cross-cutting; audited S32, 2026-06-20).** Every swarm and interview in the pipeline rides a *native* Claude Code primitive and re-implements no engine of its own: parallel sub-agent fan-out is a single-message multi-`Agent`-call dispatch (research swarm, exploration swarm, reviewer swarm), and interview turn-taking is `AskUserQuestion` (brief Phase 3, the research interview, the trekplan research-status gate, the trekreview scope gate). Voyage's value is the **policy** layered on top — never the scheduler, concurrency loop, or menu loop underneath: typed agent roles + effort defaults + codebase-size scaling (exploration); 4-angle decomposition (docs/community/security/contrarian) + per-source schemas + triangulation (research); the 17-key rule catalogue + no-cross-feed isolation + deterministic dedup/verdict (review); the section-driven completeness loop + framing gate (brief). Concretely: the brief's Phase-3 "selection rule" picks *which* question to ask (policy); `AskUserQuestion` does the asking (engine). The V01/V07/V08/V11/V24 balance-analysis audit confirmed all five already delegate natively — no engine re-implementation found; the doc-consistency test (`tests/lib/doc-consistency.test.mjs` § S32) pins the native-delegation prose so a hand-rolled engine cannot creep back in.
|
**Delegate the engine, keep the policy (cross-cutting; audited S32, 2026-06-20).** Every swarm and interview in the pipeline rides a *native* Claude Code primitive and re-implements no engine of its own: parallel sub-agent fan-out is a single-message multi-`Agent`-call dispatch (research swarm, exploration swarm, reviewer swarm), and interview turn-taking is `AskUserQuestion` (brief Phase 3, the research interview, the trekplan research-status gate, the trekreview scope gate). Voyage's value is the **policy** layered on top — never the scheduler, concurrency loop, or menu loop underneath: typed agent roles + effort defaults + codebase-size scaling (exploration); 4-angle decomposition (docs/community/security/contrarian) + per-source schemas + triangulation (research); the 18-key rule catalogue + no-cross-feed isolation + deterministic dedup/verdict (review); the section-driven completeness loop + framing gate (brief). Concretely: the brief's Phase-3 "selection rule" picks *which* question to ask (policy); `AskUserQuestion` does the asking (engine). The V01/V07/V08/V11/V24 balance-analysis audit confirmed all five already delegate natively — no engine re-implementation found; the doc-consistency test (`tests/lib/doc-consistency.test.mjs` § S32) pins the native-delegation prose so a hand-rolled engine cannot creep back in.
|
||||||
|
|
||||||
**Operator-UX guarantee (since v5.0.2):** `/trekbrief`, `/trekplan`, and `/trekreview` MUST always emit (a) a plain `file://<abs path>` URL AND (b) a copy-pasteable `open file://<abs path>` command in the final report block. The file:// URL must use an ABSOLUTE path (not relative or `~/`-prefixed) so terminals with cmd+click support (Ghostty, iTerm2, modern Terminal.app) can resolve it without shell interpretation. This is a non-negotiable operator-UX contract — the doc-consistency test pins both forms in all three commands' final report blocks.
|
**Operator-UX guarantee (since v5.0.2):** `/trekbrief`, `/trekplan`, and `/trekreview` MUST always emit (a) a plain `file://<abs path>` URL AND (b) a copy-pasteable `open file://<abs path>` command in the final report block. The file:// URL must use an ABSOLUTE path (not relative or `~/`-prefixed) so terminals with cmd+click support (Ghostty, iTerm2, modern Terminal.app) can resolve it without shell interpretation. This is a non-negotiable operator-UX contract — the doc-consistency test pins both forms in all three commands' final report blocks.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// lib/review/rule-catalogue.mjs
|
// lib/review/rule-catalogue.mjs
|
||||||
// Canonical rule catalogue for /trekreview.
|
// Canonical rule catalogue for /trekreview.
|
||||||
//
|
//
|
||||||
// 17 rule keys, 4-tier severity (matches brief contract). Decision 98 added
|
// 18 rule keys, 4-tier severity (matches brief contract). Decision 98 added
|
||||||
// CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE (both MINOR): a change that breaks, or
|
// CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE (both MINOR): a change that breaks, or
|
||||||
// makes untrue, a line of the target repo's tracked instruction files.
|
// makes untrue, a line of the target repo's tracked instruction files.
|
||||||
// llm-security 5-tier alignment is a v1.1 candidate.
|
// llm-security 5-tier alignment is a v1.1 candidate.
|
||||||
|
|
@ -14,6 +14,13 @@
|
||||||
// style, readability): the second voyage-lekerepo trial (2026-10-08) saw a pure
|
// style, readability): the second voyage-lekerepo trial (2026-10-08) saw a pure
|
||||||
// simplification proposal enter as MAJOR and drive the verdict.
|
// simplification proposal enter as MAJOR and drive the verdict.
|
||||||
//
|
//
|
||||||
|
// E 2.3 (decision 167) added SECURITY_RISK (BLOCKER): Voyage's own security
|
||||||
|
// key, for a security finding by its own reviewers (plan-critic,
|
||||||
|
// scope-guardian, the code reviewer's part for command texts) that
|
||||||
|
// SECURITY_INJECTION does not cover. SECURITY_REVIEW_VULNERABILITY is the
|
||||||
|
// engines' key only: Voyage's reviewers never use it, and a finding under it
|
||||||
|
// is never Voyage's own (lib/review/threat-model.mjs).
|
||||||
|
//
|
||||||
// `memory_route` decides where a finding class that recurs across two
|
// `memory_route` decides where a finding class that recurs across two
|
||||||
// review.md files is proposed as project memory (P5, lib/review/recurrence.mjs):
|
// review.md files is proposed as project memory (P5, lib/review/recurrence.mjs):
|
||||||
// 'check' - mechanical: a deterministic check can catch it;
|
// 'check' - mechanical: a deterministic check can catch it;
|
||||||
|
|
@ -137,9 +144,16 @@ export const RULE_CATALOGUE = Object.freeze([
|
||||||
rule_key: 'SECURITY_REVIEW_VULNERABILITY',
|
rule_key: 'SECURITY_REVIEW_VULNERABILITY',
|
||||||
severity: 'BLOCKER',
|
severity: 'BLOCKER',
|
||||||
category: 'security',
|
category: 'security',
|
||||||
description: "A vulnerability reported by Claude Code's /security-review that no narrower rule_key covers.",
|
description: "A vulnerability reported by a security engine (Claude Code's /security-review) that no narrower rule_key covers. The engines' key only: Voyage's own reviewers use SECURITY_RISK.",
|
||||||
memory_route: null,
|
memory_route: null,
|
||||||
}),
|
}),
|
||||||
|
Object.freeze({
|
||||||
|
rule_key: 'SECURITY_RISK',
|
||||||
|
severity: 'BLOCKER',
|
||||||
|
category: 'security',
|
||||||
|
description: "Voyage's own security finding (a concrete attack path, high confidence) that SECURITY_INJECTION does not cover.",
|
||||||
|
memory_route: 'rule',
|
||||||
|
}),
|
||||||
// memory_route null: a lesson about the instruction files would feed back into them.
|
// memory_route null: a lesson about the instruction files would feed back into them.
|
||||||
Object.freeze({
|
Object.freeze({
|
||||||
rule_key: 'CLAUDE_MD_DEVIATION',
|
rule_key: 'CLAUDE_MD_DEVIATION',
|
||||||
|
|
|
||||||
|
|
@ -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', () => {
|
test('[CAT] the catalogue has 18 keys and docs/architecture.md and both reviewer agents say 18-key', () => {
|
||||||
assert.equal(RULE_CATALOGUE.length, 17);
|
assert.equal(RULE_CATALOGUE.length, 18);
|
||||||
assert.equal(RULE_KEYS.size, 17);
|
assert.equal(RULE_KEYS.size, 18);
|
||||||
const header = read('lib/review/rule-catalogue.mjs').split('\n').slice(0, 10).join('\n');
|
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');
|
const arch = read('docs/architecture.md');
|
||||||
assert.match(arch, /rule catalogue \(17 keys\)/);
|
assert.match(arch, /rule catalogue \(18 keys\)/);
|
||||||
assert.match(arch, /the 17-key rule catalogue/);
|
assert.match(arch, /the 18-key rule catalogue/);
|
||||||
assert.doesNotMatch(arch, /\b(12|15)-key rule catalogue|rule catalogue \((12|15) keys\)/);
|
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']) {
|
for (const agent of ['agents/code-correctness-reviewer.md', 'agents/brief-conformance-reviewer.md']) {
|
||||||
const text = read(agent);
|
const text = read(agent);
|
||||||
assert.match(text, /the 17-key catalogue/, `${agent} says 17-key`);
|
assert.match(text, /the 18-key catalogue/, `${agent} says 18-key`);
|
||||||
assert.doesNotMatch(text, /\b(12|15)-key catalogue/, `${agent} no longer says 12-key`);
|
assert.doesNotMatch(text, /\b(12|15|17)-key catalogue/, `${agent} no longer says 17-key`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
// The two plan reviewers (plan-critic, scope-guardian) must state rule B word for
|
// The two plan reviewers (plan-critic, scope-guardian) must state rule B word for
|
||||||
// word as lib/review/critic-diff.mjs exports it, keep the 2026-10-08 rule that a
|
// word as lib/review/critic-diff.mjs exports it, keep the 2026-10-08 rule that a
|
||||||
// test which cannot fail on a wrong implementation is major, tag security
|
// test which cannot fail on a wrong implementation is major, tag security
|
||||||
// findings with the catalogue's security keys, and carry the round-2 fields
|
// findings with Voyage's own security keys (never the engines' key, decision
|
||||||
|
// 167, E 2.3), and carry the round-2 fields
|
||||||
// (`previous`, `line_end`, `lowered_from`, `caught_by`) in their json template.
|
// (`previous`, `line_end`, `lowered_from`, `caught_by`) in their json template.
|
||||||
// The name is the brief's.
|
// The name is the brief's.
|
||||||
|
|
||||||
|
|
@ -30,9 +31,12 @@ function jsonTemplate(text, rel) {
|
||||||
return JSON.parse(m[1]);
|
return JSON.parse(m[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('the catalogue has exactly two security keys: SECURITY_INJECTION and SECURITY_REVIEW_VULNERABILITY', () => {
|
// The engines' key (/security-review, Claude Security): never the critic's (decision 167).
|
||||||
|
const ENGINES_KEY = 'SECURITY_REVIEW_VULNERABILITY';
|
||||||
|
|
||||||
|
test('the catalogue has exactly three security keys: SECURITY_INJECTION, SECURITY_REVIEW_VULNERABILITY and SECURITY_RISK', () => {
|
||||||
const keys = RULE_CATALOGUE.filter(r => r.category === 'security').map(r => r.rule_key).sort();
|
const keys = RULE_CATALOGUE.filter(r => r.category === 'security').map(r => r.rule_key).sort();
|
||||||
assert.deepEqual(keys, ['SECURITY_INJECTION', 'SECURITY_REVIEW_VULNERABILITY']);
|
assert.deepEqual(keys, ['SECURITY_INJECTION', 'SECURITY_REVIEW_VULNERABILITY', 'SECURITY_RISK']);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const rel of AGENTS) {
|
for (const rel of AGENTS) {
|
||||||
|
|
@ -56,11 +60,15 @@ for (const rel of AGENTS) {
|
||||||
|
|
||||||
test(`${rel}: a security finding uses the catalogue's security rule_key`, () => {
|
test(`${rel}: a security finding uses the catalogue's security rule_key`, () => {
|
||||||
const t = norm(read(rel));
|
const t = norm(read(rel));
|
||||||
const keys = RULE_CATALOGUE.filter(r => r.category === 'security').map(r => r.rule_key);
|
const keys = RULE_CATALOGUE.filter(r => r.category === 'security' && r.rule_key !== ENGINES_KEY).map(r => r.rule_key);
|
||||||
for (const k of keys) assert.ok(t.includes(`\`${k}\``), `${rel} must name \`${k}\``);
|
for (const k of keys) assert.ok(t.includes(`\`${k}\``), `${rel} must name \`${k}\``);
|
||||||
assert.ok(t.includes("A security finding uses the catalogue's security `rule_key`"));
|
assert.ok(t.includes("A security finding uses the catalogue's security `rule_key`"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(`${rel}: the critic never names the engines' key (decision 167)`, () => {
|
||||||
|
assert.ok(!read(rel).includes(ENGINES_KEY), `${rel} must not name ${ENGINES_KEY}: it is the engines' key only`);
|
||||||
|
});
|
||||||
|
|
||||||
test(`${rel}: [STP] rule_key names the class, never the instance (the stop rule's class, decision 217)`, () => {
|
test(`${rel}: [STP] rule_key names the class, never the instance (the stop rule's class, decision 217)`, () => {
|
||||||
const t = norm(read(rel));
|
const t = norm(read(rel));
|
||||||
assert.ok(t.includes('`rule_key` names the class, never the instance'), `${rel} must say rule_key names the class, never the instance`);
|
assert.ok(t.includes('`rule_key` names the class, never the instance'), `${rel} must say rule_key names the class, never the instance`);
|
||||||
|
|
|
||||||
|
|
@ -240,12 +240,12 @@ test('HANDOVER-CONTRACTS.md Handover 7 § Lifecycle names --cleanup and produced
|
||||||
// file" (commands/trekcontinue.md exists, so that loop already asserts the
|
// file" (commands/trekcontinue.md exists, so that loop already asserts the
|
||||||
// /trekcontinue reference). Single-command existence pin → redundant.
|
// /trekcontinue reference). Single-command existence pin → redundant.
|
||||||
|
|
||||||
test('rule-catalogue has exactly 17 entries', async () => {
|
test('rule-catalogue has exactly 18 entries', async () => {
|
||||||
const mod = await import('../../lib/review/rule-catalogue.mjs');
|
const mod = await import('../../lib/review/rule-catalogue.mjs');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
mod.RULE_CATALOGUE.length,
|
mod.RULE_CATALOGUE.length,
|
||||||
17,
|
18,
|
||||||
'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)',
|
'lib/review/rule-catalogue.mjs RULE_CATALOGUE size invariant: must be 18 (12 v1.0 baseline + 2 engine keys, ordre 5, + CODE_REVIEW_IMPROVEMENT, lekerepo trial 2, + CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE, decision 98, + SECURITY_RISK, E 2.3)',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ test('engine-findings — /trekreview asks the transcription for the engine\'s f
|
||||||
assert.match(p, /never\s+write\s+one\s+the\s+engine\s+did\s+not\s+give/);
|
assert.match(p, /never\s+write\s+one\s+the\s+engine\s+did\s+not\s+give/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// The docs/architecture.md "rule catalogue (17 keys)" pin of this test lives in
|
// The docs/architecture.md "rule catalogue (18 keys)" pin of this test lives in
|
||||||
// tests/lib/claude-md-scope.test.mjs ([CAT]).
|
// tests/lib/claude-md-scope.test.mjs ([CAT]).
|
||||||
test('engine-findings — the docs: the engine rule-key count and the Declined-to-Judge rule match the code', () => {
|
test('engine-findings — the docs: the engine rule-key count and the Declined-to-Judge rule match the code', () => {
|
||||||
const read = (p) => readFileSync(join(ROOT, p), 'utf8');
|
const read = (p) => readFileSync(join(ROOT, p), 'utf8');
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ function subsection(text, heading) {
|
||||||
return text.slice(start, next === -1 ? undefined : start + heading.length + next);
|
return text.slice(start, next === -1 ? undefined : start + heading.length + next);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('[CMD] agents/code-correctness-reviewer.md names the instruction-file dimension, the 8-dimension heading and 17-key', () => {
|
test('[CMD] agents/code-correctness-reviewer.md names the instruction-file dimension, the 8-dimension heading and 18-key', () => {
|
||||||
const text = read(CC_AGENT);
|
const text = read(CC_AGENT);
|
||||||
const fm = /^---\n([\s\S]*?)\n---\n/.exec(text)?.[1] ?? '';
|
const fm = /^---\n([\s\S]*?)\n---\n/.exec(text)?.[1] ?? '';
|
||||||
assert.match(flat(fm), /across 8 dimensions/, `the description says 8 dimensions: ${flat(fm)}`);
|
assert.match(flat(fm), /across 8 dimensions/, `the description says 8 dimensions: ${flat(fm)}`);
|
||||||
|
|
@ -303,7 +303,7 @@ test('[CMD] agents/code-correctness-reviewer.md names the instruction-file dimen
|
||||||
const rows = text.split('\n').filter((l) => l.startsWith('| Instruction files |'));
|
const rows = text.split('\n').filter((l) => l.startsWith('| Instruction files |'));
|
||||||
assert.deepEqual(rows, ['| Instruction files | CLAUDE_MD_DEVIATION / CLAUDE_MD_STALE | {N} |'], 'the per-dimension table has the instruction-file row');
|
assert.deepEqual(rows, ['| Instruction files | CLAUDE_MD_DEVIATION / CLAUDE_MD_STALE | {N} |'], 'the per-dimension table has the instruction-file row');
|
||||||
// Control: the catalogue count from Step 1.
|
// Control: the catalogue count from Step 1.
|
||||||
assert.ok(text.includes('the 17-key catalogue'), 'Control: the agent says the 17-key catalogue');
|
assert.ok(text.includes('the 18-key catalogue'), 'Control: the agent says the 18-key catalogue');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[CMD] every text that counts the code-correctness dimensions says 8, and /trekreview Phase 5 passes the instruction set', () => {
|
test('[CMD] every text that counts the code-correctness dimensions says 8, and /trekreview Phase 5 passes the instruction set', () => {
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,23 @@ test('RULE_CATALOGUE — all category values within enum', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('RULE_KEYS.size === RULE_CATALOGUE.length (== 17) — pinned by doc-consistency', () => {
|
test('RULE_KEYS.size === RULE_CATALOGUE.length (== 18) — pinned by doc-consistency', () => {
|
||||||
assert.equal(RULE_KEYS.size, RULE_CATALOGUE.length);
|
assert.equal(RULE_KEYS.size, RULE_CATALOGUE.length);
|
||||||
assert.equal(RULE_CATALOGUE.length, 17);
|
assert.equal(RULE_CATALOGUE.length, 18);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SECURITY_RISK is Voyage\'s own security key; SECURITY_REVIEW_VULNERABILITY is the engines\' key only (E 2.3, decision 167)', () => {
|
||||||
|
const own = getRule('SECURITY_RISK');
|
||||||
|
assert.ok(own, 'SECURITY_RISK is in the catalogue');
|
||||||
|
assert.equal(own.severity, 'BLOCKER');
|
||||||
|
assert.equal(own.category, 'security');
|
||||||
|
assert.equal(own.memory_route, 'rule');
|
||||||
|
assert.match(own.description, /Voyage's own/);
|
||||||
|
const engines = getRule('SECURITY_REVIEW_VULNERABILITY');
|
||||||
|
assert.equal(engines.severity, 'BLOCKER');
|
||||||
|
assert.equal(engines.category, 'security');
|
||||||
|
assert.match(engines.description, /engines' key only/);
|
||||||
|
assert.match(engines.description, /SECURITY_RISK/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE are MINOR', () => {
|
test('CLAUDE_MD_DEVIATION and CLAUDE_MD_STALE are MINOR', () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue