Muin is in private beta.Watch the public release announcement —talk to us.
Falaah Falaah AI

Risk Register

Identify, score, and mitigate risks on a 5×5 likelihood and impact matrix, track residual risk against controls, and get AI-suggested mitigations in Muin.

The Risk Register is where your organization catalogs, scores, and tracks compliance risks. Each risk is scored on a 5×5 matrix (likelihood × impact), assigned an owner, linked to the controls that mitigate it, and moved through a mitigation workflow to closure or accepted-risk status.

The Risk Management factor is 20% of your overall compliance score — the second-largest factor after framework completion — so keeping this register current directly moves your score.

The 5×5 Scoring Model

Muin uses a simple and widely-adopted likelihood × impact model with both values on a 1–5 scale.

Likelihood (1–5)

ValueLabelMeaning
1Rare<5% chance in the next 12 months
2Unlikely5–25% chance
3Possible25–50% chance
4Likely50–75% chance
5Almost certain>75% chance

Impact (1–5)

ValueLabelMeaning
1NegligibleMinor inconvenience, no data loss, no material cost
2LowSmall cost, recoverable, no external notification
3ModerateNotable financial/operational impact, customer notification possible
4MajorSignificant financial loss, regulatory notification required, reputational damage
5SevereExistential risk — business continuity threat, major regulatory action

Derived Score and Level

score = likelihood × impact   (range: 1–25)

The score is bucketed into a level:

ScoreLevelTypical action
20–25CriticalImmediate exec escalation + mitigation workflow auto-started
12–19HighMitigation owner assigned, plan due within 30 days
6–11MediumMonitored quarterly, mitigation if cost-effective
2–5LowLogged and tracked, reviewed annually
1MinimalAccepted by default, reviewed annually

Thresholds are defined in RiskService.get_risk_level — see muin.server/src/services/compliance/risk_service.py.

Residual Risk

The inherent score is the score with no controls applied. The residual score is what remains after a control is applied with some effectiveness:

residual = inherent × (1 − control_effectiveness)

For example, an inherent score of 20 (critical) with a control that’s 75% effective becomes a residual score of 5 (low). Control effectiveness is a per-risk value you set — it’s not magic, just a multiplier — so the register lets you model how much of each risk each mitigation actually buys you.

When a linked control’s test status changes (e.g. from tested to in_progress), Muin recalculates the residual scores of all risks linked to that control. This is JG-4 in the plan — residual auto-updates on control test change, so you never have a register showing low-risk for a risk whose control has silently failed a test.


Creating a Risk

On /compliance/risks, click New Risk. The form asks for:

FieldPurpose
TitleShort descriptor, e.g. “Unencrypted customer PII in exports”
DescriptionFull context for the auditor
Categorytechnical, process, people, financial, regulatory, reputational, or other
Likelihood1–5
Impact1–5
Inherent scoreAuto-computed from likelihood × impact (read-only on the form)
OwnerRisk owner (a User) — the accountable person
Statusopen, mitigating, mitigated, accepted, closed
Linked controlsFramework controls that mitigate this risk

Both likelihood and impact are validated to the 1–5 range at the service layer — values outside that range return a 422.


The Heatmap (IS13)

The heatmap view aggregates your register into a 5×5 matrix. Each cell shows the count of risks at that likelihood/impact combination. Click a cell to drill through to the filtered risk list.

              Impact →
          1    2    3    4    5
      ┌────┬────┬────┬────┬────┐
   1  │  3 │  1 │  0 │  0 │  0 │   minimal/low
      ├────┼────┼────┼────┼────┤
   2  │  5 │  8 │  2 │  1 │  0 │   low/medium
      ├────┼────┼────┼────┼────┤
 L  3  │  2 │  4 │  7 │  3 │  1 │   medium/high
 i    ├────┼────┼────┼────┼────┤
 k  4  │  0 │  1 │  2 │  2 │  0 │   high/critical
 e    ├────┼────┼────┼────┼────┤
 l  5  │  0 │  0 │  1 │  0 │  0 │   critical
 i    └────┴────┴────┴────┴────┘
 h
 o
 o
 d

Colors shade by level: green for minimal/low, amber for medium, red for high/critical.

The heatmap pulls from GET /compliance/risks/heatmap which returns one row per cell with the aggregate count. The backend-side matrix generator is RiskService.generate_heatmap_matrix — a simple aggregation that’s fast enough to compute on every page load, no caching.

Why Your Heatmap Cell Counts Zero

If a cell you think should have risks shows zero, check three things:

  1. The risks have status other than closed — closed risks are excluded from the heatmap by default
  2. The likelihood and impact values are both in the 1–5 range — values outside that range aren’t placed on the matrix (see generate_heatmap_matrix bounds check)
  3. The filter bar above the heatmap isn’t set to a category or owner that excludes the expected risks

Mitigation Workflow

When a risk’s score crosses the high threshold (≥12) and mitigation_status is NOT_STARTED, Muin automatically:

  1. Fires a risk.high_without_mitigation notification to the risk owner
  2. Starts a compliance.risk.mitigation_approval WorkflowInstance (Plan 272k INT-WORKFLOWS)
  3. Creates a HumanTask for the owner to acknowledge and propose mitigation

The workflow walks the owner through: assess root cause → propose mitigation → assign target date → link controls → submit for review → approval → close.

Once the workflow completes and the residual score drops below the high threshold, the alert clears automatically — no manual dismiss.

Accepting a Risk

Sometimes mitigation is uneconomic — the risk is real but the cost of controls exceeds the cost of the residual. Accept the risk:

  1. On risk detail, click Accept Risk
  2. Fill in the acceptance justification (required, stored for audit)
  3. The risk transitions to accepted status
  4. A HumanTask is created for the compliance owner to approve the acceptance

Accepted risks still show in the heatmap (so auditors see them) but they don’t trigger the mitigation workflow or notifications.


Linking Risks to Controls (JG-4)

On risk detail, the Linked Controls section lets you attach one or more framework controls. Each link stores:

  • The control’s ID
  • An effectiveness value (0.0–1.0) — how much of the risk this control mitigates

When any linked control’s test status changes, Muin recomputes the risk’s residual score using the linked control’s effectiveness. This ensures:

  • If a control that was mitigating a risk fails a test → the residual score goes back up → the heatmap lights up red
  • If a control gets newly certified → the residual drops → the risk may fall off the high-priority list

This is JG-4 in Plan 272 — the residual-updates-on-control-test link that keeps the register honest.


AI-Assisted Mitigation (JG-5)

The Suggest Mitigation button on any risk detail invokes Bedrock Claude with the risk’s title, description, category, likelihood, impact, and the tenant’s active framework controls. It returns 3–5 concrete mitigation strategies tailored to the risk.

What the AI Does Well

  • Translates a plain-English risk description into control-themed mitigations
  • References the specific framework controls the tenant already has (so suggestions map to existing work, not new work)
  • Flags immediate-escalation when the risk is at or above score ≥ 15 — “Immediate management escalation required” is always the first suggestion for severe risks

What the AI Doesn’t Do

  • Create the mitigation for you — the output is a suggestion list, not a committed plan
  • Invent controls you don’t have — the citation fence rejects suggestions referencing controls not in your tenant
  • Replace the risk owner’s judgment — the owner still chooses which suggestions to accept, adjusts effectiveness values, and signs the mitigation plan

There’s also a rule-based fallback (RiskService.suggest_mitigation static method) that returns category-appropriate templates when the LLM is slow, rate-limited, or returns an unusable response. The UI shows a source badge indicating LLM vs rule-based so you always know which you’re reading.


Risk Summary (/compliance/risks/summary)

The summary endpoint returns aggregate counts by level and status — used by the sparklines on the /compliance Today tab and the mitigation-status donut chart on the risk list page. Handy for quick exec view without drilling through the heatmap.


FAQs

What’s residual risk?

Residual is the risk that remains after controls are applied. Formula: residual = inherent × (1 − control_effectiveness). A control that’s 75% effective reduces the risk by 75%. Residual moves automatically when the linked control’s test status changes (JG-4), so your register reflects reality instead of the score you set six months ago.

Why does my heatmap cell count zero?

Three usual reasons: (1) the risks in that cell are closed (closed risks are excluded from the heatmap), (2) the likelihood or impact value is outside the 1–5 range and therefore not placed on the matrix, (3) a filter is active that excludes those risks. Check each in that order.

How do I close a risk?

Move through: openmitigatingmitigated (residual has dropped below the threshold you committed to) → closed. Alternatively, accepted is a valid terminal status for risks where mitigation isn’t economic — requires an acceptance justification and compliance owner approval. Closed and accepted risks both remain visible for audit but don’t trigger notifications.

What if the AI mitigation suggestion is wrong?

The suggestion list is never auto-committed — you review and pick what fits. If you disagree with every suggestion, ignore them. The rule-based fallback returns generic category-appropriate ideas if that’s more useful than LLM output. The source badge tells you which you’re reading, and the risk owner is always the accountable decision-maker.

Can a mitigation workflow be restarted?

Yes — if a risk was mitigated and then a control test failure pushes the residual back into the high-threshold zone, the high-without-mitigation predicate fires again and a new workflow instance is started. Prior workflow history is preserved for audit.