--- --- # Step 3: Triage ## RULES - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}` - Be precise. When uncertain between categories, prefer the more conservative classification. ## INSTRUCTIONS 1. **Normalize** findings into a common format. Expected input formats: - Adversarial (Blind Hunter): markdown list of descriptions - Edge Case Hunter: JSON array with `location`, `trigger_condition`, `guard_snippet`, `potential_consequence` fields - Acceptance Auditor: markdown list with title, AC/constraint reference, and evidence If a layer's output does not match its expected format, attempt best-effort parsing. Note any parsing issues for the user. Convert all to a unified list where each finding has: - `id` -- sequential integer - `source` -- `blind`, `edge`, `auditor`, `static` (auto-mode prefilter), or merged sources (e.g., `blind+edge`) - `title` -- one-line summary - `detail` -- full description - `location` -- file and line reference (if available) 2. **Deduplicate.** If two or more findings describe the same issue, merge them into one: - Use the most specific finding as the base (prefer edge-case JSON with location over adversarial prose). - Append any unique detail, reasoning, or location references from the other finding(s) into the surviving `detail` field. - Set `source` to the merged sources (e.g., `blind+edge`). **Prior-cycle ledger check** (`{auto_mode}` only): if `{spec_file}` contains `#### Review Ledger` entries from earlier review cycles, treat them as already adjudicated. A new finding matching a previously dismissed entry (same location, same substance) is `dismiss` with reason "previously dismissed — see ledger" unless it brings genuinely new evidence. A finding matching a previously patched entry must be checked against the current code before re-raising — the patch may already cover it. 3. **Verify against the code** (`{auto_mode}` only). For each surviving finding (except `static` ones — those are tool output), check it against the actual code before classifying. You have project access; the hunters that produced these findings mostly did not. A finding contradicted by the surrounding code — the case is already guarded, the function behaves differently than the finding assumes, the "missing" handling exists elsewhere — becomes `dismiss` with the contradiction recorded as its reason. Do not classify a finding you have not verified. 4. **Classify** each finding into exactly one bucket: - **decision_needed** -- There is an ambiguous choice that requires human input. The code cannot be correctly patched without knowing the user's intent. Only possible if `{review_mode}` = `"full"`. - **patch** -- Code issue that is fixable without human input. The correct fix is unambiguous. - **defer** -- Pre-existing issue not caused by the current change. Real but not actionable now. - **dismiss** -- Noise, false positive, or handled elsewhere. If `{review_mode}` = `"no-spec"` and a finding would otherwise be `decision_needed`, reclassify it as `patch` (if the fix is unambiguous) or `defer` (if not). If `{auto_mode}` and a finding would otherwise be `decision_needed`: reclassify as `patch` only when the fix is genuinely unambiguous; otherwise reclassify as `defer` with reason "auto-mode: needs human decision" AND record it in the result escalations — severity `CRITICAL` if it concerns correctness or security of the new code, else `PREFERENCE` (see `../automation-mode.md` rule 5). If `{auto_mode}` and a finding's root cause is a defect in the spec itself (the code faithfully implements something the spec got wrong), handle it per `../automation-mode.md` rule 6: if the root cause is inside the spec's `` block, classify as `defer` and record a `CRITICAL` (`type: spec-defect`) escalation — never patch around frozen, human-owned intent; if the root cause is outside the frozen block, classify as `patch` (correct the code to the evidently right behavior), mark it for the step-04 `## Spec Change Log` append, and record a `PREFERENCE` escalation. 5. **Drop** all `dismiss` findings. Record the dismiss count for the summary. (`{auto_mode}`: do NOT drop — set each dismissed finding aside, keeping its title, location, and one-line dismissal reason; step-04 writes them to the Review Ledger so later cycles do not re-litigate them.) 6. If `{failed_layers}` is non-empty, report which layers failed before announcing results. If zero findings remain after dropping dismissed AND `{failed_layers}` is non-empty, warn the user that the review may be incomplete rather than announcing a clean review. 7. If zero findings remain after triage (all rejected or none raised): state "✅ Clean review — all layers passed." (Step 3 already warned if any review layers failed via `{failed_layers}`.) ## NEXT Read fully and follow `./step-04-present.md`