fix(review): the workflow review ranks by tier and puts back every engine finding

Operator decision 23 (13.10); review findings ab1b5d9f and bc7f4e23 (workflow side).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-13 14:32:59 +02:00
commit 05f6b67be9
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
5 changed files with 257 additions and 37 deletions

View file

@ -519,8 +519,9 @@ block. Do NOT re-run the reviewers based on the coordinator's output.
## Phase 5–6 via the Workflow substrate (opt-in `--workflow`)
Runs **only** when `workflow_substrate == true`. This is the **NW2 port**: it expresses
the SAME Phase 5–6 pipeline (parallel reviewers → triplet-dedup → coordinator verdict) as
a single Workflow, reusing the NW1 findings schema. The S10 bake-off found it
the same Phase 5–6 pipeline shape (parallel reviewers → triplet-dedup → coordinator verdict)
as a single Workflow, reusing the NW1 findings schema; its verdict floor is stricter than
the prose path's (decision 23, below). The S10 bake-off found it
**fidelity-equivalent** to the prose path — see `docs/T2-bakeoff-results.md` (verdict
**POSITIVE**: verdict-match 1.0, issue-coverage 100%, `(file,rule_key)` jaccard ≥
within-arm, tokens +4.4%). It stays **opt-in**, not the default, because the Workflow tool
@ -545,6 +546,11 @@ Workflow({
A reviewer that returns nothing, or an engine listed in `engineNotRun`,
forbids `ALLOW` on this path too (`missing_reviewers` in the result).
On this path dedup ranks as Pass 1 does (both paths), every engine finding the
coordinator did not return at its exact `(file, line, rule_key)` is put back at its own
severity (operator decision 23), and a returned finding is never below the lower of its
reviewer counterpart's severity and its catalogue tier (floor (b)), so the workflow path
is stricter than the prose path.
Contract (verified in S10 part B — follow exactly):
- **Pass `args` as a JSON object.** The script defensively re-parses a JSON *string*, but