Skip to content

feat(risk): residual-risk acceptance expires — mandatory end date, reminder, automatic re-assessment #811

Description

@alex-dembele

Problem

When a risk owner and their approver accept a residual risk, the acceptance holds forever. Nothing brings it back for review when the context changes: a new vulnerability, a new regulation, a year gone by. ISO/IEC 27005 treats acceptance as a decision to review, and supervisors (DORA, COBAC) expect time-bound acceptances with a named decision-maker. Today an auditor looking at an acceptance from two years ago cannot tell whether it still stands.

What exists (verified 2026-09-25):

  • Accepting is already governed. residual_accepted requires an approved governance request (GuardGovernanceApproval, backend/internal/domain/risk_state.go:278-292).
  • ApprovalWorkflow.ExpiresInHours (internal/domain/governance.go:337) is the deadline to decide, not how long the acceptance stays valid.
  • Risk.ReviewIntervalDays / NextReviewAt (internal/domain/risk.go:312-317) nudge the owner, but leave the state alone.
  • Allowed transitions out of residual_accepted: closed, in_treatment (risk_state.go:63).

Acceptance criteria

  1. Moving to residual_accepted requires an acceptance_expires_at. The default is 12 months and the maximum is set per tenant. The approver, the date and the expiry go on the risk and into the chained audit trail.
  2. A worker notifies the owner and the approver at 30 days and at 7 days before expiry. It stamps before sending, to avoid duplicates (the same pattern as ReminderSentAt in internal/domain/evidence.go).
  3. At expiry the risk leaves residual_accepted through a system transition, with actor system and reason acceptance_expired, and appears in the owner's Action Center. The target state is settled in the PR by tech-lead. The recommendation is a new explicit residual_accepted → assessed transition, because an expired acceptance calls for re-assessment and in_treatment would break GuardActiveMitigation.
  4. Renewing means a new approved governance request. Nobody can simply edit the date.
  5. The register can filter acceptances that expire within N days, and the board report lists them.
  6. Tests: Success, NotFound, Unauthorized, plus worker idempotence (two ticks produce one notification and one transition) and tenant isolation.

Definition of Done

No acceptance in the system is open-ended. An expired acceptance is visible, notified and re-opened with no human action, as the worker's integration test proves.

Depends on: #757 (a real notifier). Without it, criterion 2 can only be proven through the in-app channel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions