3.9 KiB
Quickstart: 111 — Findings Workflow V2 + SLA
Date: 2026-02-24
Branch: 111-findings-workflow-sla
Prerequisites
- Sail services running (
vendor/bin/sail up -d) - Database migrated to latest
- At least one workspace with at least one tenant
- Queue worker running for queued jobs (e.g.,
vendor/bin/sail artisan queue:work)
Implementation Phases
Phase 1: Data Layer (Findings v2 Columns + Badges + Settings)
Goal: Extend findings to v2 lifecycle fields and add SLA policy setting.
- Migrations (add v2 columns + indexes; two-phase if enforcing NOT NULL after backfill)
- Update
App\Models\Findingconstants for v2 statuses and severities - BADGE-001: extend Finding status badge mapping to include v2 statuses (and legacy
acknowledgedmapping) - Settings:
- Add
findings.sla_daystoSettingsRegistry - Expose in Workspace Settings UI (JSON textarea), validate via registry rules
- Add
Run: vendor/bin/sail artisan migrate && vendor/bin/sail artisan test --compact --filter=SettingsRegistry
Phase 2: Workflow + RBAC (Server-Side Enforcement + Filament Actions)
Goal: Enforce transition rules, write timestamps, and provide safe UI actions.
- Capabilities:
- Add new
TENANT_FINDINGS_*constants - Keep
TENANT_FINDINGS_ACKNOWLEDGEas triage alias (migration window) - Update
RoleCapabilityMap
- Add new
- Policy/service layer:
- Enforce allowed transitions server-side
- Require reasons for resolve/close/risk accept
- Audit log every mutation (before/after + reason fields)
- Filament
FindingResource:- Remove drift-only default filters
- Default to Open statuses across all finding types
- Add quick filters (Open/Overdue/High severity/My assigned)
- Add row actions + bulk actions per spec (grouped under “More”)
Run: vendor/bin/sail artisan test --compact --filter=FindingWorkflow
Phase 3: Generators (Lifecycle Fields + Drift Recurrence + Stale Resolve)
Goal: Ensure findings lifecycle fields and recurrence behavior are maintained automatically.
- Drift:
- Compute
recurrence_keyand upsert by it - Auto-reopen only from
resolvedintoreopened - Auto-resolve stale drift for a scope when no longer detected (
resolved_reason=no_longer_detected)
- Compute
- Permission posture + Entra roles:
- Preserve existing reopen/auto-resolve behavior
- Add lifecycle fields: first/last seen, times_seen, due_at/sla_days
Run: vendor/bin/sail artisan test --compact --filter=FindingRecurrence
Phase 4: Alerts (SLA Due Producer + UI Re-Enable)
Goal: Make sla_due alert rules functional.
- Add SLA due producer to
EvaluateAlertsJobthat emits one tenant-level event summarizing overdue counts. - Re-enable
sla_duein AlertRuleResource event type options (only after producer exists).
Manual verification:
- Create (or backfill) a finding with
due_atin the past and open status. - Run
vendor/bin/sail artisan tenantpilot:alerts:dispatch --workspace={id} - Confirm an
alert_deliveriesrow is created for matching enabled rules.
Run: vendor/bin/sail artisan test --compact --filter=FindingSlaDue
Phase 5: Backfill/Consolidation (OperationRun-Backed)
Goal: Upgrade legacy findings and consolidate drift duplicates.
- Trigger backfill from tenant context (Filament action and/or an artisan command entrypoint).
- Verify OPS-UX:
- queued toast intent-only
- progress visible in active ops widget + OperationRun detail
- exactly one terminal completion notification (initiator-only)
- Verify data outcomes:
acknowledged → triaged- lifecycle fields populated
- due dates set from backfill time + SLA days for legacy open findings
- drift duplicates consolidated (one canonical open row per recurrence identity)
Run: vendor/bin/sail artisan test --compact --filter=FindingBackfill
Formatting / Hygiene
- Run
vendor/bin/sail bin pint --dirtybefore finalizing.