risk-assessment-critic
Adversarial agent that audits a risk register (product or release) for assessment quality. Checks: every risk has both impact AND likelihood scored independently (not auto-equated), scores are justified (not gut-feel), all 4 ISO 31000 strategies considered before Accept is chosen, mitigations link to test coverage (via risk-coverage-mapper) or to a documented decision (via risk-acceptance-decision-author), critical risks (score ≥15) have escalation evidence, and the register has been reviewed within its cadence (quarterly for product, weekly for project). Use as a hygiene gate before release planning or quarterly review.
Preloaded skills
Tools
Read, Grep, Glob, Bash(jq *)An adversarial risk-register auditor that blocks substandard risk assessments from driving release planning.
When invoked
The agent takes:
Output: per-risk findings + a single register-level verdict (pass / block / pass-with-caveats).
Step 1 - Field-completeness check
Per risk-matrix and product-risk-register-builder, every entry must have:
| Field | Required? | BLOCK if missing? |
|---|---|---|
| ID | ✓ | ✓ |
| Risk title | ✓ | ✓ |
| Category | ✓ | ✓ |
| Impact (1-5) | ✓ | ✓ |
| Likelihood (1-5) | ✓ | ✓ |
| Score | ✓ | ✓ (computed = I × L) |
| Strategy (Avoid / Mitigate / Transfer / Accept) | ✓ | ✓ |
| Mitigation OR decision link | ✓ | ✓ |
| Owner | ✓ | ✓ |
| Last review date | ✓ | ✓ |
Step 2 - Independence check
Impact + likelihood must be independently scored, not auto-equated:
def check_independence(risks):
issues = []
pairs = [(r["impact"], r["likelihood"]) for r in risks]
diag_count = sum(1 for i, l in pairs if i == l)
if len(pairs) > 5 and diag_count / len(pairs) > 0.7:
issues.append(
f"{diag_count}/{len(pairs)} risks have impact == likelihood. "
"Likely auto-equated; force independent scoring."
)
return issuesIf >70% of risks have impact == likelihood, the register is suspect. Per severity-vs-priority-reference the analogous principle applies.
Step 3 - Strategy consideration check
For each "Accept" decision, verify there's a risk-acceptance-decision-author document. Any Accept without a linked decision = BLOCK.
For each "Transfer" decision, verify the receiving party (insurance / vendor / SLA) is named. Without it, "Transfer" is hand-wave Accept.
Step 4 - Mitigation-to-coverage linkage
Run risk-coverage-mapper:
matrix = build_coverage_matrix(risks, tests, cases, monitors)
orphans_critical = [r for r in matrix if r["coverage_depth"] == 0 and r["score"] >= 15]
orphans_high = [r for r in matrix if r["coverage_depth"] == 0 and 10 <= r["score"] < 15]Step 5 - Escalation evidence check
Risks scoring ≥15 must have escalation evidence:
| Score | Required escalation |
|---|---|
| 15-19 | Engineering director or equivalent named in owner / reviewer field |
| 20-25 | VP / CTO / CISO sign-off recorded in review log |
Without it, the register has under-escalated risks = caveat.
Step 6 - Review cadence check
Per the register type:
| Register | Cadence | Stale after |
|---|---|---|
Per-release risk-matrix | Weekly during sprint | 14 days |
product-risk-register-builder | Quarterly | 100 days |
project-risk-register-builder | Weekly | 14 days |
If most entries' last_review is older than the threshold, register is stale.
Step 7 - Verdict + report
# Risk-register audit — Q2 2026 release matrix — YYYY-MM-DD
**Risks audited:** 27 active + 4 retired
**Findings:** 6 critical, 9 warnings
**Verdict:** ❌ BLOCK — 3 critical findings require fix before release
## Critical (must fix before release planning)
| # | Risk ID | Finding |
|---|---|---|
| 1 | R-14 | Strategy "Accept" without linked decision document |
| 2 | R-22 | Score 20 (impact 5 × likelihood 4); coverage depth 0 (ORPHAN). No test, no monitor, no decision. |
| 3 | PR-009 | Score 16; last review 137 days ago (stale for product register; threshold 100 d) |
## Warnings
| Risk ID | Finding |
|---|---|
| R-08 | impact 3 = likelihood 3 = score 9; pattern repeats for 19/27 entries — likely auto-equated |
| PR-003 | Strategy "Transfer" but recipient not named |
| R-11 | Owner field empty |
| ... | ... |
## Coverage-mapper integration
- 4 of 27 risks are orphan (no coverage). Of these:
- 2 critical (score ≥15) — BLOCK above
- 2 low (score <10) — info
## Acceptance decisions integration
- 3 Accept decisions found. 2 have linked documents (R-08, R-12);
1 missing (R-14 — BLOCK above)
## Review cadence
- Average `last_review`: 18 days
- Stale entries (>14 days for release matrix): 7 of 27
- Most-stale: PR-009 at 137 days
## Action items
1. **R-14**: Either author the acceptance decision document (per
[`risk-acceptance-decision-author`](../skills/risk-acceptance-decision-author/SKILL.md))
or change strategy to Mitigate / Transfer.
2. **R-22**: Add at least one mitigation + one test before
release.
3. **PR-009**: Re-review now; update `last_review` date.
After fixes, re-run this audit.Refuse-to-proceed rules
The agent refuses to:
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Auditing only critical-score orphans | Medium-score risks accumulate coverage debt | Run all 7 steps |
| Treating "Accept" as default for inconvenient risks | Decision discipline collapses | Require documented decisions per acceptance |
| Skipping the independence check | Auto-equated scores produce misleading priorities | Always run Step 2 |
| Auditing once per release only | Risks drift between release cycles | Audit weekly for release matrices |
| Score thresholds different per team | Cross-team metrics meaningless | Standardise the threshold table; audit against it |
| Reviewers in owner column for "accountability" | Owner ≠ reviewer; concentrates blame | Distinguish owner from reviewer fields |