Skip to main content

ADR-0100: Officer override of the composite risk recommendation

Date: 2026-07-06 Status: Accepted Deciders: Adrian (Soft4U), Claude Opus 4.8

Decision context:

  • Latency: one INSERT + one audit row on the officer-interactive decision surface; the downward path adds one pending_decision_approvals INSERT + a PENDING_SECOND_APPROVAL state move (the existing four-eyes cost). Not request-throughput-bound.
  • Dependency surface: no new packages. New RLS table case_risk_overrides (migration 085); reuses MakerCheckerService (ADR-0070), assert_distinct_approver, the deterministic escalator floors (eba_risk_matrix), the ADR-0089 rank helpers, #12's ≥50-char rationale validator, and the _enrich_* merge seam in get_case.
  • Debuggability: an override is a queryable append-only row + paired immutable audit_events (risk_override_applied / risk_override_approved), carrying the original assessed level and the officer's reasoning — a full EU AI Act Art. 12/14 trail, not a mutated field.
  • Reversibility: additive — new table, one endpoint, one merge seam, one reconcile_display_risk guard. Reverting stops honoring overlays; the assessed verdict is untouched underneath. ~5-file change.
  • Blast radius: additive on the decision surface + the get_case read merge + reconcile_display_risk. No change to the EBA computation, the ADR-0089 entity ratchet, or the monitoring cadence (which conservatively stays at the assessed tier).
  • Alternative considered: write the override straight through to entity_baselines.latest_risk_tier — rejected (entity-scoped, would retro-affect other cases sharing the identity, and the ratchet/display-reconcile would re-raise anything below the assessed floor next run).

Context

The composite risk verdict is computed by the EBA matrix (compute_eba_risk, deterministic 0–100 score + level, with criminal/sanctions/payment-account escalator floors) and derived to the SDD/CDD/EDD tier. An officer can override the MCC classification and the automation tier, but there is no path to override the composite risk tier/recommendation with a documented justification — the AIAssessmentCard "I disagree" button is a no-op that records nothing. EU AI Act Art. 14 requires human oversight including the ability to override the AI's output, and the Belgian AML law places the risk-assessment decision on the officer, not the system. This is issue #15. The design must reconcile with three existing invariants: the ADR-0089 one-way entity ratchet (a persisted floor never silently lowers), reconcile_display_risk (the display never shows softer than the floored EBA verdict), and the deterministic escalator floors (a criminal investigation or true sanctions designation is a hard floor).

Decision

An audited, direction-gated, floor-guarded case-scoped override overlay.

  1. case_risk_overrides append-only RLS table (migration 085): the from/to level+tier, direction, rationale, officer_id, status, approved_by.
  2. POST /cases/{workflow_id}/risk-override (CASE_DECIDE), rationale ≥50 (reuse #12's validator; 422 else).
  3. Floor guard (fail-closed): an override may never set a level below the deterministic EBA escalator floor or the persisted material-findings floor → 409. A downward override cannot wash out a deterministic signal (the never-suppress cardinal rule).
  4. Direction gate: upward / lateral-stricter → immediate, single-officer, audited. Downward → four-eyes (reuse MakerCheckerService; a distinct second approver; the existing override_risk_divergence predicate slot) → PENDING_SECOND_APPROVAL; only a distinct approver activates it.
  5. Effective-tier overlay consumed at the case-facing read sites via _enrich_risk_override in get_case; reconcile_display_risk respects an active approved downward override.
  6. Monitoring cadence stays at the assessed tier (fail-safe) until the ADR-0089 audited baseline downgrade lowers the entity floor on its own gate — an override never silently lowers the entity-scoped baseline.

Consequences

Positive

  • Closes the EU AI Act Art. 14 human-oversight gap — the officer can override the AI risk verdict with a retained, audited justification, and the dead-end "I disagree" becomes a real control.
  • A scrutiny-reducing override gets the codebase's maximal-traceability treatment: four-eyes + ≥50-char rationale + escalator-floor guard + fail-safe monitoring — never a silent softening.
  • The assessed EBA verdict is preserved underneath (from_* retained); the overlay is fully reversible and auditable, satisfying EU AI Act Art. 12.

Negative

  • Two risk representations now coexist per case (assessed vs effective-overridden); every case-facing read must go through the merge seam or it will show the pre-override verdict — a standing coupling (mitigated by centralising on get_case's _enrich_*).
  • Monitoring cadence and the case-facing verdict can diverge until baseline reconciliation (cadence stays stricter) — deliberate fail-safe, but a temporary inconsistency an officer may notice.
  • A downward override adds officer workload (a second approver) exactly when an officer wants to reduce attention to a case — deliberate friction, but friction.

Neutral

  • The upward override is immediate because raising is always safe; only the downward path is gated.
  • v1 does not propagate an approved downward override into the entity baseline cadence (deferred to the ADR-0089 audited downgrade); the effect is more-monitoring-than-asked, which is fail-safe.

Alternatives Considered

Alternative 1: write the override through to entity_baselines.latest_risk_tier

  • Directly lower the persisted entity floor on override.
  • Why rejected: the baseline is entity-scoped (registration_number + country); a write-through would retro-affect other cases sharing the identity, and reconcile_display_risk + the ADR-0089 ratchet re-raise any value written below the assessed floor on the next run — so it is neither case-correct nor durable. The audited approve_baseline_downgrade remains the single authority for lowering the entity floor.

Alternative 2: allow a downward override immediately (single-officer), like the MCC override

  • Mirror submit_mcc_decision's immediate single-officer write for risk too.
  • Why rejected: a risk downgrade is a scrutiny-reducing action on the load-bearing compliance verdict; every other such action in the system is four-eyes-gated, and letting one officer silently soften the risk verdict is exactly the oversight failure Art. 14 and the never-suppress rule guard against. Upward is immediate; downward is not.