docs(review): /trekreview chooses its security engine (E 3.2)

Phase 4.7 of /trekreview runs security-engine.mjs choose first. Claude
Security runs through its own skill (scan changes --base {before_sha}
--effort high); its cost question goes to the operator, and "No", a
question nobody can answer or a missing Workflow tool records it as not
run, never a fallback to /security-review. /security-review runs only
when choose picked it. Its report is converted by security-engine.mjs
convert, never transcribed; below_threshold is declined as
engine:below-threshold. With no engine the expected reviewer
security-engine never reports, so the verdict gate exits 2 and the
summary says "security: not measured" and why, never 0. --quick keeps no
security engine. The Engine summary line, the template's ran line, the
hard rules (the engine's report folder, scan changes only) and the
allowed-tools (Workflow) follow.

Engine prose follows in commands/trekexecute.md, docs/command-modes.md,
docs/architecture.md, CLAUDE.md, README.md, SECURITY.md and Handover 6:
Claude Security recommended, proprietary so called by name and never a
manifest dependency; without it /security-review; with neither "not
measured".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-08 13:39:44 +01:00
commit d97e9b5b19
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
10 changed files with 279 additions and 49 deletions

View file

@ -460,12 +460,12 @@ Optional but standard sections: `## Findings (BLOCKER)`, `## Findings (MAJOR)`,
Since 1.1 (ordre 5):
- `## Remediation Steps` — required at `review_version` ≥ 1.1 when there is a BLOCKER: one `### Step R<n>` per BLOCKER carrying `Source finding: <id>` (strict error `REVIEW_MISSING_REMEDIATION_STEP`, soft warning). Steps are appended and numbered, never reworded across iterations (Spec Kit `/converge`).
- `## Declined to Judge` — required at `review_version` ≥ 1.1 (strict error, soft warning; 1.0 reviews exempt): every finding set aside without a ruling, with its reason (including engine findings outside the review scope, `engine:out-of-scope`), plus `Trimmed: N`. **No finding is struck for length**: only MINOR/SUGGESTION text is trimmed, and counted (`trimmed` in the JSON block); a long BLOCKER/MAJOR keeps every character.
- Findings from Claude Code's own `/code-review` and `/security-review` enter under the catalogue keys `CODE_REVIEW_DEFECT` (MAJOR), `CODE_REVIEW_IMPROVEMENT` (MINOR: a `/code-review` finding whose category is simplification, efficiency, reuse, style or readability) and `SECURITY_REVIEW_VULNERABILITY` (BLOCKER), or a narrower key, with `reviewer` `claude-code-review` / `claude-security-review`.
- Working files of one review run, in the project folder (not part of `review.md`): the engine transcriptions `.review-engine-{code-review|security-review}.json` (`/trekreview` Phase 4.7) and, beside them, the converted payloads `.review-engine-payload-{engine}.json` (the `engine-findings.mjs` output, `{reviewer, findings, out_of_scope}`). `review-finalize.mjs` reads the payloads to record which engines ran and to put back, at its own severity, an engine finding at MAJOR or BLOCKER that no finding of `review.md` carries (the standard-path comparison); the verdict gate reads the payloads too, to compare them with `review.md`: an engine finding at MAJOR or BLOCKER missing from it is exit 2 (`review-not-measured: engine finding not in review`). `review-rounds.mjs prepare` deletes the payloads at the start of each round.
- `## Declined to Judge` — required at `review_version` ≥ 1.1 (strict error, soft warning; 1.0 reviews exempt): every finding set aside without a ruling, with its reason (including engine findings outside the review scope, `engine:out-of-scope`, and Claude Security findings below confidence `high`, `engine:below-threshold`), plus `Trimmed: N`. **No finding is struck for length**: only MINOR/SUGGESTION text is trimmed, and counted (`trimmed` in the JSON block); a long BLOCKER/MAJOR keeps every character.
- Findings from the engines — Claude Code's own `/code-review` and one security engine, chosen by `lib/review/security-engine.mjs choose` (Claude Security where it is installed and the range has committed changes, else `/security-review`) — enter under the catalogue keys `CODE_REVIEW_DEFECT` (MAJOR), `CODE_REVIEW_IMPROVEMENT` (MINOR: a `/code-review` finding whose category is simplification, efficiency, reuse, style or readability) and `SECURITY_REVIEW_VULNERABILITY` (BLOCKER), or a narrower key, with `reviewer` `claude-code-review` / `claude-security` (engine `claude-security`) / `claude-security-review`. A Claude Security finding counts only at confidence `high` (decision 219); the rest stand under `## Declined to Judge` as `engine:below-threshold`, an unverified reason: not counted, it forbids `ALLOW`. With no security engine (`choose` gives `no-engine`, `no-origin` or `empty-diff`, or the chosen engine did not run) the expected reviewer `security-engine` never reports: `missing-reviewer:security-engine`, verdict gate exit 2 — security not measured, never 0.
- Working files of one review run, in the project folder (not part of `review.md`): the engine transcriptions `.review-engine-{code-review|security-review}.json` (`/trekreview` Phase 4.7) and, beside them, the converted payloads `.review-engine-payload-{engine}.json` (the `engine-findings.mjs` output, `{reviewer, findings, out_of_scope}`). Claude Security's is never transcribed: `.review-engine-payload-claude-security.json` is the `security-engine.mjs convert` output from its own report, `{reviewer, findings, out_of_scope, below_threshold, report}`; `below_threshold` is never compared or put back. `review-finalize.mjs` reads the payloads to record which engines ran and to put back, at its own severity, an engine finding at MAJOR or BLOCKER that no finding of `review.md` carries (the standard-path comparison); the verdict gate reads the payloads too, to compare them with `review.md`: an engine finding at MAJOR or BLOCKER missing from it is exit 2 (`review-not-measured: engine finding not in review`). `review-rounds.mjs prepare` deletes the payloads at the start of each round.
Since 1.2 (every field and section optional; a malformed one present is a strict error, a soft warning):
- Trailing JSON block, additive: per finding `pre_existing` (boolean: the range did not add or change the cited line and `scope_sha_start` is bound to the fork point from main (the fork point itself, or a later commit reached through process files only); otherwise it is never true), `carried_from` (the previous round's finding id) and `instruction_ref` (the repo path of the instruction file a CLAUDE.md-key finding cites); top level `pre_existing_counts` (`{SEVERITY: n}`), `resolved` (the previous round's finding ids no longer raised) and `ran` (`{engines: [code-review|security-review], reviewers: [string]}`, written with `finalized: true`). Codes `REVIEW_PRE_EXISTING_MALFORMED`, `REVIEW_ROUND_MALFORMED`, `REVIEW_POLICY_MALFORMED`.
- Trailing JSON block, additive: per finding `pre_existing` (boolean: the range did not add or change the cited line and `scope_sha_start` is bound to the fork point from main (the fork point itself, or a later commit reached through process files only); otherwise it is never true), `carried_from` (the previous round's finding id) and `instruction_ref` (the repo path of the instruction file a CLAUDE.md-key finding cites); top level `pre_existing_counts` (`{SEVERITY: n}`), `resolved` (the previous round's finding ids no longer raised) and `ran` (`{engines: [code-review|security-review|claude-security], reviewers: [string]}`, written with `finalized: true`). Codes `REVIEW_PRE_EXISTING_MALFORMED`, `REVIEW_ROUND_MALFORMED`, `REVIEW_POLICY_MALFORMED`.
- `## Pre-existing` — the titles and counts of the findings marked `pre_existing`; written by `review-finalize.mjs`.
- `## Resolved since round <n>` — the previous round's findings this round no longer raises; written by `review-finalize.mjs`.
- `## Answers` — the operator's answer to each counting MAJOR, written by `/trekreview` Phase 8.8 after `## Remediation Steps` and before the trailing JSON block: a pipe table with the header exactly `| Finding | Answer | Reason | Record | Carried from |`; per row a 40-hex finding id (optionally `#n`), `accepted` or `fixed`, a reason (non-empty when `accepted`), the 64-hex hash of the K2 `review` record, and `-` or `review-round-<run12>-<n>.md#<id>`; at most one row per finding. A MAJOR answered `fixed` needs its `### Step R<n>` with `Source finding: <id>` under `## Remediation Steps`. Code `REVIEW_ANSWERS_MALFORMED`.
@ -474,13 +474,13 @@ Since 1.2 (every field and section optional; a malformed one present is a strict
**The brief-conflict mark (fields `engine`, `contradicts_brief`, `brief_lowered_from`).**
Each finding may carry three more fields, in its body field list and in the trailing JSON block:
`engine` (`code-review` | `security-review`, on a finding that came from an engine),
`engine` (`code-review` | `security-review` | `claude-security`, on a finding that came from an engine),
`contradicts_brief` (the brief's Non-Goal or stated choice the finding's recommended action contradicts, verbatim)
and `brief_lowered_from` (`MAJOR`, written only by the one lowering).
The mark is a field on a kept finding, never a `suppressed_reason`.
Only an engine finding is ever marked; a reviewer-agent finding (no `engine`) and a finding whose `rule_key` is not in the catalogue never are.
The one legal lowering: a `/code-review` MAJOR-tier finding at severity MAJOR drops to MINOR, with `brief_lowered_from: MAJOR`;
a BLOCKER-tier, a MINOR-tier and any `/security-review` finding keep their severity and only carry the mark.
a BLOCKER-tier, a MINOR-tier and any finding from a security engine (`/security-review`, Claude Security) keep their severity and only carry the mark.
On an ALLOW or WARN review the verdict gate exits 2 on every finding `illegalBriefLowering` (`lib/review/coordinator-contract.mjs`) names:
any other `brief_lowered_from`, a mark on a reviewer-agent finding, a marked finding below its tier, a marked finding with an unknown key,
and an engine finding below its tier, marked or not (a BLOCK review exits 1 as before).