## Summary - add a first-class finding exception domain with request, approval, rejection, renewal, and revocation lifecycle support - add tenant-scoped exception register, finding governance surfaces, and a canonical workspace approval queue in Filament - add audit, badge, evidence, and review-pack integrations plus focused Pest coverage for workflow, authorization, and governance validity ## Validation - vendor/bin/sail bin pint --dirty --format agent - CI=1 vendor/bin/sail artisan test --compact - manual integrated-browser smoke test for the request-exception happy path, tenant register visibility, and canonical queue visibility ## Notes - Filament implementation remains on v5 with Livewire v4-compatible surfaces - canonical queue lives in the admin panel; provider registration stays in bootstrap/providers.php - finding exceptions stay out of global search in this rollout Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #184
5.5 KiB
5.5 KiB
Data Model: Finding Risk Acceptance Lifecycle
1. FindingException
- Purpose: Tenant-owned governance aggregate that represents the current accepted-risk exception state for one finding.
- Ownership: Tenant-owned (
workspace_id+tenant_idNOT NULL). - Fields:
idworkspace_idtenant_idfinding_idstatusenum:pending,active,expiring,expired,rejected,revoked,supersededrequested_by_user_idowner_user_idapproved_by_user_idnullablecurrent_decision_idnullablerequest_reasontextapproval_reasontext nullablerejection_reasontext nullablerevocation_reasontext nullablerequested_atapproved_atnullablerejected_atnullablerevoked_atnullableeffective_fromnullableexpires_atnullablereview_due_atnullableevidence_summaryJSONB nullablecurrent_validity_stateenum:valid,expiring,expired,revoked,rejected,missing_supportcreated_at,updated_at
- Relationships:
- belongs to
Finding - belongs to
Tenant - belongs to
Workspace - belongs to requester
User - belongs to owner
User - belongs to approver
User - has many
FindingExceptionDecision - has many
FindingExceptionEvidenceReference
- belongs to
- Validation / invariants:
workspace_id,tenant_id, andfinding_idare always required.finding_idmust reference a finding in the same workspace and tenant.- At most one current valid active exception may govern one finding at a time.
approved_by_user_idmust differ fromrequested_by_user_idin v1.expires_atmust be aftereffective_fromwhen both are present.
2. FindingExceptionDecision
- Purpose: Append-only historical record of every exception lifecycle decision.
- Ownership: Tenant-owned (
workspace_id+tenant_idNOT NULL). - Fields:
idworkspace_idtenant_idfinding_exception_iddecision_typeenum:requested,approved,rejected,renewal_requested,renewed,revokedactor_user_idreasontext nullableeffective_fromnullableexpires_atnullablemetadataJSONB nullabledecided_atcreated_at,updated_at
- Relationships:
- belongs to
FindingException - belongs to actor
User
- belongs to
- Validation / invariants:
- Decision rows are append-only after creation.
- Decision type must be compatible with the parent exception's lifecycle state.
- Renewal decisions must not erase prior approval or rejection records.
3. FindingExceptionEvidenceReference
- Purpose: Structured pointer to evidence used to justify or review the exception.
- Ownership: Tenant-owned (
workspace_id+tenant_idNOT NULL). - Fields:
idworkspace_idtenant_idfinding_exception_idsource_typestringsource_idstring nullablesource_fingerprintstring nullablelabelstringsummary_payloadJSONB nullablemeasured_atnullablecreated_at,updated_at
- Relationships:
- belongs to
FindingException
- belongs to
- Validation / invariants:
- References must stay intelligible even if the live source artifact later expires or is removed from active views.
summary_payloadis bounded, sanitized, and not a raw payload dump.
4. Finding Risk Governance Projection
- Purpose: Derived truth used by finding detail, tenant exception lists, canonical queues, and downstream evidence/reporting consumers.
- Derived from:
Finding.statusFindingException.status- exception validity window (
effective_from,expires_at) - current exception evidence support state
- Values:
ungovernedpending_exceptionvalid_exceptionexpiring_exceptionexpired_exceptionrevoked_exceptionrejected_exceptionrisk_accepted_without_valid_exception
- Invariant:
- Downstream consumers must use this projection, not finding status alone, when determining whether accepted risk is currently governed.
State Transitions
FindingException
pending->activeon approvalpending->rejectedon rejectionactive->expiringwhen within reminder thresholdactive|expiring->expiredwhenexpires_atpassesactive|expiring->revokedon explicit revokeactive|expiring|expired->supersededwhen a renewal produces a newer governing decision under the same aggregate semantics
FindingExceptionDecision
requestedalways occurs firstapprovedorrejectedresolves a pending requestrenewal_requestedmay occur fromactive,expiring, orexpiredrenewedextends a current or lapsed exception through a new decisionrevokedends current validity explicitly
Indexing and Query Needs
- Composite indexes on
(workspace_id, tenant_id, status)for tenant register filtering. - Composite indexes on
(workspace_id, status, review_due_at)for canonical queue and expiring views. - Unique partial index to prevent more than one current valid active exception per finding.
- Composite index on
(finding_id, tenant_id)for finding-detail resolution.
Relationship to Existing Domain Records
Findingremains the system of record for the detected issue and status workflow.FindingWorkflowServiceremains the only allowed path for changing finding status.AuditLogremains the immutable historical event stream for every lifecycle mutation.EvidenceSnapshotand related artifacts remain separate systems of record referenced by exception evidence links.