Quality only, no behaviour change (four cleanup reviewers, one implementer;
npm test 2480/0 on the working tree):
- threat-model.mjs splitBacked(): the one known_outside backing check of
plan-gate (record and bare gate), verdict-gate and review-finalize, with
one why-text; the engines' security keys derived from ENGINES; isSecurity.
- coordinator-contract: "not a security engine" read from ENGINES (a string
engine only), not the code-review literal.
- review-rounds owns trailingBlock, findingsBlockSha, isMajor, isBlocker;
review-answers imports them (review-rounds still imports nothing from it).
prepare keeps the archive bytes it read instead of reading the previous
archive again.
- engine-compare exports rowIn and payloadEngine; finalize uses them.
- plan-gate: one accepted_open builder; the stop-rule check hoisted; one
top-level lookup for --record --brief.
- security-engine: notFullShas(), runCli's usage flag, readScopeList shared
with engine-findings; findReport and reviewRoundSummary module-private;
two dead defaults gone.
- tests: shared plan-gate harness and threat-model brief helpers; the [CNV]
table cases call convertReport in-process, one CLI case per exit path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/review/security-engine.mjs (decisions 175, 196, 219):
- choose: Claude Security when available and base..head has committed
changes; else /security-review when origin/HEAD resolves and
origin/HEAD...HEAD is non-empty; else no engine (no-engine, no-origin,
empty-diff), which is "not measured", never 0. A git failure or bad
usage is exit 2, never a reason.
- convert: reads the newest CLAUDE-SECURITY-<ts>/ report whose stamp is a
changes scan of exactly base..head; confidence high becomes engine
findings through toReviewerPayload, the rest go to below_threshold and
never into findings. An unverified scan, missing researcher counts,
fewer researchers returned than dispatched, an empty diff or scope, or
a malformed record is exit 2.
- NO_ENGINE_REVIEWER: with no engine chosen the expected reviewer
security-engine never reports, so the verdict gate exits 2.
ENGINES gains claude-security (its own reviewer and brief_ref, the
engines' key) with a per-engine label for the written action; it is a
security engine in isSecurityFinding, RAN_ENGINES, the coordinator
contract (out-of-scope and brief-conflict rules) and the coordinator
prose; engine:below-threshold is an unverified reason fed like
engineOutOfScope. Pins of the changed coordinator prose follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New lib/review/threat-model.mjs, the one reader both gates use:
readThreatModel(briefText) -> {present, inside, outside, problems} through
brief-validator's parseThreatModel; usable(briefPath, repo) = present, well
formed, and the intent approval current by the two checks /trekplan Phase 1
runs (checkIntentApprovalContent, then verifyLog kind intent with the
hash's sha256: stripped and the subject normalized), else not usable with
the reason in `why`; backedLine(finding, tm) = the Outside: text the
finding's threat_model_ref equals exactly, else null. Only a security
finding not from an engine can be outside (218): a finding with an
`engine`, or carrying the engines' key SECURITY_REVIEW_VULNERABILITY, is
never backed.
Part 2, step 2.2 of the E plan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>