## Summary - harden finding lifecycle changes behind the canonical `FindingWorkflowService` gateway - route automated resolve and reopen flows through the same audited workflow path - tighten tenant and workspace scope checks on finding actions and audit visibility - add focused spec artifacts, workflow regression coverage, automation coverage, and audit visibility tests - update legacy finding model tests to use the workflow service after direct lifecycle mutators were removed ## Testing - `vendor/bin/sail bin pint --dirty --format agent` - focused findings and audit slices passed during implementation - `vendor/bin/sail artisan test --compact tests/Feature/Models/FindingResolvedTest.php` - full repository suite passed: `2757 passed`, `8 skipped`, `14448 assertions` ## Notes - Livewire v4.0+ compliance preserved - no new Filament assets or panel providers introduced; provider registration remains in `bootstrap/providers.php` - findings stay on existing Filament action surfaces, with destructive actions still confirmation-gated - no global search behavior was changed for findings resources Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #181
5.0 KiB
Data Model: Findings Workflow Enforcement and Audit Backstop
Overview
This feature does not introduce a new primary persistence model. It hardens the existing relationship between tenant-owned Finding records and workspace-owned audit history so that lifecycle truth is enforced consistently across UI and automation paths.
Entities
Finding
Type: Existing tenant-owned aggregate
Storage: findings table
Ownership: workspace_id + tenant_id required via tenant ownership rules
Relevant fields:
idworkspace_idtenant_idstatusseveritydue_atsla_daysassignee_user_idowner_user_idtriaged_atin_progress_atreopened_atresolved_atresolved_reasonclosed_atclosed_reasonclosed_by_user_idfirst_seen_atlast_seen_attimes_seenevidence_jsonb
Validation rules:
tenant_idmust match the active tenant scope for any tenant-context mutation.workspace_idmust match the owning tenant workspace.statusmust remain within the canonical Spec 111 lifecycle state set, with legacyacknowledgedtreated as compatibility input rather than a preferred new write target.resolved_reasonis required for resolve operations.closed_reasonis required for close and risk-accept operations.assignee_user_idandowner_user_id, when present, must reference current tenant members.
State transitions:
| From | To | Trigger | Notes |
|---|---|---|---|
new |
triaged |
Human triage | Canonical replacement for legacy acknowledge semantics |
reopened |
triaged |
Human triage | Same workflow rule as Spec 111 |
acknowledged |
triaged |
Human triage or compatibility normalization | Legacy compatibility path |
triaged |
in_progress |
Human start progress | Existing workflow rule |
acknowledged |
in_progress |
Human start progress | Legacy compatibility path |
new,triaged,in_progress,reopened,acknowledged |
resolved |
Human resolve or system auto-resolve | Reason required; system-origin audit must remain explicit |
new,triaged,in_progress,reopened,acknowledged |
closed |
Human close | Reason required |
new,triaged,in_progress,reopened,acknowledged |
risk_accepted |
Human risk accept | Reason required |
resolved,closed,risk_accepted |
reopened |
Human reopen or recurrence reopen | Recomputes SLA and due date |
Out-of-scope transitions:
- Any direct write that changes lifecycle truth without using the canonical gateway
- Any new active status value beyond the Spec 111 set
Finding Workflow Mutation
Type: Domain command, not a table
Purpose: Encapsulates the requested lifecycle change and the context needed to validate and audit it
Fields:
finding_idtenant_idworkspace_idrequested_transitionactor_kind(humanorsystem)actor_idor system-origin identityreason_textwhen requiredassignee_user_idandowner_user_idwhen assignment changesobservation_timestampfor automation paths that depend on current observation truth
Validation rules:
- Must resolve to one owned
Findingwithin the current tenant scope. - Must satisfy the allowed transition matrix.
- Must carry all required metadata for the requested transition.
- Must reject no-op transitions that do not materially change lifecycle truth.
Audit Log Entry for Findings Workflow
Type: Existing workspace-owned historical record
Storage: audit_logs table
Relevant fields:
workspace_idtenant_idactor_idactor_emailactor_nameactionresource_typeresource_idstatusmetadatarecorded_at
Required metadata shape for this feature:
finding_idbefore_statusafter_statusbeforeafter- transition-specific reason or assignment fields when applicable
- system-origin marker when the mutation was not user-driven
Constraints:
- Must never expose
evidence_jsonb, secrets, or oversized raw payloads. - Must remain intelligible even if the live
Findinglater changes or becomes inaccessible. - Must not duplicate history for the same successful covered mutation.
Relationships
- One
Findingbelongs to oneTenantand oneWorkspacethrough tenant ownership. - Many audit log entries may reference one
Findingover time. - One workflow mutation command targets exactly one
Findingbut may be invoked repeatedly across the lifecycle.
Consistency Rules
- A lifecycle mutation is valid only if both the persisted
Findingand the requested transition still satisfy the gateway rules at execution time. - Human-driven and system-driven lifecycle changes share the same transition rules but differ in actor semantics and audit labeling.
- Assignment changes are part of workflow history and must be auditable, but they do not introduce a separate lifecycle state.
- Legacy
acknowledgedvalues remain readable and migratable, but new writes should converge on canonical statuses.