Implements spec 111 (Findings workflow + SLA) and fixes Workspace findings SLA settings UX/validation. Key changes: - Findings workflow service + SLA policy and alerting. - Workspace settings: allow partial SLA overrides without auto-filling unset severities in the UI; effective values still resolve via defaults. - New migrations, jobs, command, UI/resource updates, and comprehensive test coverage. Tests: - `vendor/bin/sail artisan test --compact` (1779 passed, 8 skipped). Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #135
99 lines
4.0 KiB
Markdown
99 lines
4.0 KiB
Markdown
# Data Model: 111 — Findings Workflow V2 + SLA
|
|
|
|
**Date**: 2026-02-24
|
|
**Branch**: `111-findings-workflow-sla`
|
|
|
|
---
|
|
|
|
## Modified Entities
|
|
|
|
### 1. `findings` Table (Lifecycle + SLA + Recurrence)
|
|
|
|
This feature evolves `findings` from v1 (`new|acknowledged|resolved`) to a v2 workflow and adds lifecycle metadata and SLA fields.
|
|
|
|
#### New Columns
|
|
|
|
| Column | Type | Constraints | Notes |
|
|
|--------|------|-------------|-------|
|
|
| `first_seen_at` | `timestampTz` | nullable initially; NOT NULL after backfill | Set on first observation; backfill from `created_at` where possible |
|
|
| `last_seen_at` | `timestampTz` | nullable initially; NOT NULL after backfill | Updated on every observation (including terminal findings) |
|
|
| `times_seen` | `integer` | default `0`, NOT NULL (after backfill enforce) | Incremented on every observation |
|
|
| `sla_days` | `smallint` | nullable | SLA policy value applied when `due_at` was set/reset |
|
|
| `due_at` | `timestampTz` | nullable | Only “open” findings participate in SLA due evaluation |
|
|
| `owner_user_id` | `bigint` | FK → users, nullable | Retained even if user is no longer a tenant member |
|
|
| `assignee_user_id` | `bigint` | FK → users, nullable | Retained even if user is no longer a tenant member |
|
|
| `triaged_at` | `timestampTz` | nullable | Set on `new|reopened → triaged` |
|
|
| `in_progress_at` | `timestampTz` | nullable | Set on `triaged → in_progress` |
|
|
| `reopened_at` | `timestampTz` | nullable | Set when transitioning into `reopened` (manual or automatic) |
|
|
| `closed_at` | `timestampTz` | nullable | Used for both `closed` and `risk_accepted` terminal outcomes |
|
|
| `closed_by_user_id` | `bigint` | FK → users, nullable | Actor for `closed` / `risk_accepted` |
|
|
| `closed_reason` | `string` | nullable | Reason required for `closed` and `risk_accepted` |
|
|
| `recurrence_key` | `string(64)` | nullable; indexed | Stable identity for drift recurrence (v2) |
|
|
|
|
#### Existing Columns Used/Extended
|
|
|
|
| Column | Notes |
|
|
|--------|------|
|
|
| `status` | Extended v2 statuses (see below). Legacy `acknowledged` is mapped to v2 `triaged` in the UI and migrated during backfill. |
|
|
| `resolved_at` / `resolved_reason` | Remains the terminal “resolved” record with reason. |
|
|
| `acknowledged_at` / `acknowledged_by_user_id` | Retained for historical reference; `acknowledged` status is legacy. |
|
|
| `fingerprint` | Remains unique per tenant. For canonical drift rows going forward, the fingerprint is stable (aligned to recurrence identity). |
|
|
|
|
#### Status Values (Canonical)
|
|
|
|
Open statuses:
|
|
- `new`
|
|
- `triaged`
|
|
- `in_progress`
|
|
- `reopened`
|
|
|
|
Terminal statuses:
|
|
- `resolved`
|
|
- `closed`
|
|
- `risk_accepted`
|
|
|
|
Legacy status (migration window):
|
|
- `acknowledged` (treated as `triaged` in v2 surfaces)
|
|
|
|
#### Indexes
|
|
|
|
New/updated indexes to support list filters and alert evaluation:
|
|
|
|
| Index | Type | Purpose |
|
|
|------|------|---------|
|
|
| `(tenant_id, status, due_at)` | btree | Open/overdue filtering in tenant UI |
|
|
| `(tenant_id, assignee_user_id)` | btree | “My assigned” filter |
|
|
| `(tenant_id, recurrence_key)` | btree | Drift recurrence lookups and consolidation |
|
|
| `(workspace_id, status, due_at)` | btree | Workspace-scoped SLA due producer query |
|
|
|
|
Existing index `(tenant_id, status)` remains valid.
|
|
|
|
---
|
|
|
|
## Configuration Keys (No Schema Change)
|
|
|
|
### Settings: Findings SLA policy
|
|
|
|
Add a SettingsRegistry entry (workspace-resolvable):
|
|
- `findings.sla_days` (JSON object): severity → days
|
|
- Default:
|
|
- critical: 3
|
|
- high: 7
|
|
- medium: 14
|
|
- low: 30
|
|
|
|
Stored in existing `workspace_settings` / `tenant_settings` tables; no new tables required.
|
|
|
|
---
|
|
|
|
## State Machine (High-Level)
|
|
|
|
```
|
|
new ──triage──> triaged ──start──> in_progress ──resolve──> resolved
|
|
└───────────────────────────────close/risk_accept──────────────> closed|risk_accepted
|
|
|
|
resolved ──(auto or manual)──> reopened ──triage──> triaged ...
|
|
closed|risk_accepted ──(manual only)──> reopened
|
|
```
|
|
|