# 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**: - `id` - `workspace_id` - `tenant_id` - `status` - `severity` - `due_at` - `sla_days` - `assignee_user_id` - `owner_user_id` - `triaged_at` - `in_progress_at` - `reopened_at` - `resolved_at` - `resolved_reason` - `closed_at` - `closed_reason` - `closed_by_user_id` - `first_seen_at` - `last_seen_at` - `times_seen` - `evidence_jsonb` **Validation rules**: - `tenant_id` must match the active tenant scope for any tenant-context mutation. - `workspace_id` must match the owning tenant workspace. - `status` must remain within the canonical Spec 111 lifecycle state set, with legacy `acknowledged` treated as compatibility input rather than a preferred new write target. - `resolved_reason` is required for resolve operations. - `closed_reason` is required for close and risk-accept operations. - `assignee_user_id` and `owner_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_id` - `tenant_id` - `workspace_id` - `requested_transition` - `actor_kind` (`human` or `system`) - `actor_id` or system-origin identity - `reason_text` when required - `assignee_user_id` and `owner_user_id` when assignment changes - `observation_timestamp` for automation paths that depend on current observation truth **Validation rules**: - Must resolve to one owned `Finding` within 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_id` - `tenant_id` - `actor_id` - `actor_email` - `actor_name` - `action` - `resource_type` - `resource_id` - `status` - `metadata` - `recorded_at` **Required metadata shape for this feature**: - `finding_id` - `before_status` - `after_status` - `before` - `after` - 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 `Finding` later changes or becomes inaccessible. - Must not duplicate history for the same successful covered mutation. ## Relationships - One `Finding` belongs to one `Tenant` and one `Workspace` through tenant ownership. - Many audit log entries may reference one `Finding` over time. - One workflow mutation command targets exactly one `Finding` but may be invoked repeatedly across the lifecycle. ## Consistency Rules - A lifecycle mutation is valid only if both the persisted `Finding` and 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 `acknowledged` values remain readable and migratable, but new writes should converge on canonical statuses.