5.3 KiB
API Contracts: 111 — Findings Workflow V2 + SLA
Date: 2026-02-24
Branch: 111-findings-workflow-sla
Overview
This feature does not introduce new external REST endpoints. User interaction is through Filament/Livewire actions on the Findings Resource and Alert Rules configuration. The only new cross-module “contract” is the sla_due alert event produced during scheduled alert evaluation and consumed by the Alerts dispatch pipeline.
1. Alert Event Contract: sla_due
Producer: App\Jobs\Alerts\EvaluateAlertsJob
Event Type: AlertRule::EVENT_SLA_DUE (sla_due)
Event Cardinality: At most 1 event per tenant per evaluation window (when newly-overdue findings exist)
Eligibility (Per Tenant)
An event is produced when a tenant has one or more newly-overdue open findings since the previous evaluation window:
status IN (new, triaged, in_progress, reopened)due_at <= now()due_at > windowStart
Terminal statuses (resolved, closed, risk_accepted) never contribute to overdue evaluation.
Event Payload Shape
{
"event_type": "sla_due",
"tenant_id": 123,
"severity": "high",
"fingerprint_key": "sla_due:tenant:123",
"title": "SLA overdue findings detected",
"body": "Tenant Contoso has 5 overdue open findings (critical: 1, high: 2, medium: 2, low: 0).",
"metadata": {
"overdue_total": 5,
"overdue_by_severity": {
"critical": 1,
"high": 2,
"medium": 2,
"low": 0
}
}
}
Severity Semantics
severity is the maximum severity among overdue open findings for that tenant at evaluation time.
Rationale: alert rules can use minimum_severity; a critical-overdue case can bypass a “high-only” rule.
2. Filament Findings Resource: Workflow Actions Contract
All workflow actions:
- enforce tenant membership as deny-as-not-found (404) for non-members
- enforce capability checks (403 for members lacking capability)
- write an audit log entry with before/after and any reason fields
List Defaults
Default list shows:
- all finding types (no drift-only default)
- open statuses only:
new,triaged,in_progress,reopened
Quick filters:
- Open
- Overdue (
due_at < now()and open statuses) - High severity (high + critical)
- My assigned (
assignee_user_id = current user)
Row Actions (More menu)
| Action | Allowed From Status | To Status | Capability | Confirmation | Notes |
|---|---|---|---|---|---|
| Triage | new, reopened |
triaged |
TENANT_FINDINGS_TRIAGE (or legacy TENANT_FINDINGS_ACKNOWLEDGE alias) |
No | Sets triaged_at |
| Start progress | triaged |
in_progress |
TENANT_FINDINGS_TRIAGE (or legacy alias) |
No | Sets in_progress_at |
| Assign | open statuses | unchanged | TENANT_FINDINGS_ASSIGN |
No | Sets assignee_user_id and optional owner_user_id (picker limited to tenant members) |
| Resolve | open statuses | resolved |
TENANT_FINDINGS_RESOLVE |
Yes | Requires resolved_reason; sets resolved_at |
| Close | any status | closed |
TENANT_FINDINGS_CLOSE |
Yes | Requires closed_reason; sets closed_at + closed_by_user_id |
| Risk accept | any status | risk_accepted |
TENANT_FINDINGS_RISK_ACCEPT |
Yes | Requires reason (stored as closed_reason); sets closed_at + closed_by_user_id |
| Reopen | resolved, closed, risk_accepted |
reopened |
TENANT_FINDINGS_TRIAGE (or legacy alias) |
Yes | Manual reopen only. Automatic reopen is allowed only from resolved during detection. |
Bulk Actions
Bulk actions are all-or-nothing (if any record is unauthorized for the current tenant context, the action is disabled):
| Action | Capability | Confirmation | Notes |
|---|---|---|---|
| Bulk triage | TENANT_FINDINGS_TRIAGE (or legacy alias) |
Yes (typed confirm for large selections) | Moves `new |
| Bulk assign | TENANT_FINDINGS_ASSIGN |
Yes (typed confirm for large selections) | Assignee/owner pickers limited to tenant members |
| Bulk resolve | TENANT_FINDINGS_RESOLVE |
Yes | Reason required |
| Bulk close | TENANT_FINDINGS_CLOSE |
Yes | Reason required |
| Bulk risk accept | TENANT_FINDINGS_RISK_ACCEPT |
Yes | Reason required |
3. Backfill/Consolidation Operation Contract
Backfill is a tenant-context operation that upgrades legacy findings to v2 lifecycle fields and consolidates drift duplicates. It MUST be OperationRun-backed and use OPS-UX feedback surfaces (queued toast, progress surfaces, initiator-only completion notification).
OperationRun type: findings.lifecycle.backfill (label registered in OperationCatalog)
Idempotency: One active run per tenant (deduped by OperationRun identity)
Summary counts use canonical numeric keys only (e.g., total, processed, updated, failed, skipped).
4. Audit Contract (Tenant Scope)
Every workflow mutation writes a tenant audit record (via App\Services\Intune\AuditLogger) with:
actionstring (e.g.,finding.triaged,finding.resolved,finding.closed,finding.risk_accepted,finding.reopened,findings.lifecycle.backfill.started)metadataincluding:finding_id,before_status,after_status, and any reason fields or assignment deltas
Audit payloads must remain sanitized and must not include secrets/tokens.