69 lines
5.8 KiB
Markdown
69 lines
5.8 KiB
Markdown
# Research: Findings Notifications & Escalation v1
|
||
|
||
## Decision 1: Reuse the existing alert event registry and alert-rule surfaces
|
||
|
||
**Decision**: Add the four finding event constants to `AlertRule` and expose them through `AlertRuleResource::eventTypeOptions()` and `AlertRuleResource::eventTypeLabel()`.
|
||
|
||
**Rationale**: Workspace alert rules, destinations, deliveries, cooldowns, quiet hours, and delivery-history viewing already exist and are the approved external-copy path. Extending that registry keeps rule configuration and delivery viewing in one place and avoids a second event catalog.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Create a findings-specific alert configuration surface. Rejected because the spec explicitly requires reuse of the existing alert system and forbids a second preference or notification center.
|
||
- Pass new event strings ad hoc without updating the central registry. Rejected because alert-rule selectors, delivery filters, and labels would drift immediately.
|
||
|
||
## Decision 2: Use existing Filament database notifications for direct personal delivery
|
||
|
||
**Decision**: Deliver direct operator notifications through Laravel database notifications with Filament payloads stored in the existing `notifications` table.
|
||
|
||
**Rationale**: `AdminPanelProvider` already enables `databaseNotifications()`, and the existing `OperationRunQueued` and `OperationRunCompleted` notification classes show the repository’s established pattern for title, body, and action-link payloads. This satisfies the requirement for in-app personal notifications without creating a new page or asset surface.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Build a custom Livewire inbox or findings-notification page. Rejected because it introduces a second notification surface and broader UI scope than the spec allows.
|
||
- Deliver only external copies through email or Teams. Rejected because the spec requires direct in-app notifications first and treats external channels as optional copies controlled by alert rules.
|
||
|
||
## Decision 3: Emit assignment and automatic-reopen events from `FindingWorkflowService` after commit
|
||
|
||
**Decision**: Hook `findings.assigned` and `findings.reopened` at the workflow-service boundary after `mutateAndAudit(...)` returns a refreshed `Finding` record.
|
||
|
||
**Rationale**: `FindingWorkflowService` already owns assignment, reopen, due-date reset, authorization, and audit semantics. Emitting after commit avoids dispatching notification side effects for rolled-back writes and keeps event truth attached to the seam that actually changes responsibility or lifecycle.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Emit from controllers, Filament actions, or pages. Rejected because some finding changes are system-driven and those surfaces do not own the canonical mutation truth.
|
||
- Emit inside the transaction. Rejected because direct or external delivery could start before the write commits.
|
||
- Notify on manual `reopen(...)` in the same pass. Rejected for v1 because the spec is limited to automatic reopen notification and keeping manual reopen silent reduces scope.
|
||
|
||
## Decision 4: Keep due-soon and overdue evaluation inside `EvaluateAlertsJob`
|
||
|
||
**Decision**: Extend `EvaluateAlertsJob` with finding due-soon and overdue candidate scans and route each candidate through the finding-notification delivery seam.
|
||
|
||
**Rationale**: The job already evaluates workspace-scoped alert events on a scheduled cadence, carries evaluation-window semantics, and runs inside the existing `alerts.evaluate` operational path. Reusing it avoids a second scheduler, command, or run family.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Create a dedicated due-notification job or Artisan command. Rejected because it duplicates scheduling, workspace scoping, and operational observability for no functional gain.
|
||
- Trigger due reminders from the UI. Rejected because reminder delivery must not depend on an operator visiting a page.
|
||
|
||
## Decision 5: Use notification-payload metadata for direct-delivery dedupe
|
||
|
||
**Decision**: Store a `fingerprint_key` and event metadata in the existing database notification `data` JSONB payload and query the existing `notifications` table to suppress duplicate direct deliveries.
|
||
|
||
**Rationale**: The spec forbids new persistence while still requiring one notification per due cycle and fingerprint-aware delivery semantics. Existing notification rows are already persisted delivery artifacts, so they are the narrowest place to record direct-delivery fingerprints without adding a new table.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Create a new `FindingNotificationDelivery` table. Rejected because it adds persistence and lifecycle ownership that the spec explicitly avoids.
|
||
- Skip direct dedupe entirely. Rejected because due-soon and overdue would repeat on every scheduled evaluation pass.
|
||
|
||
## Decision 6: Reuse tenant finding detail links and current entitlement checks
|
||
|
||
**Decision**: Build notification actions against the existing tenant-panel finding detail route and suppress send-time direct delivery if the selected recipient no longer has current tenant membership plus findings-view capability.
|
||
|
||
**Rationale**: The spec requires the notification to explain why the user is seeing it and send them to the existing finding detail route while preserving current `404` and `403` semantics. Rechecking entitlement at send time prevents stale assignments or ownership from generating a misleading in-app notification.
|
||
|
||
**Alternatives considered**:
|
||
|
||
- Link to the My Findings inbox instead of the single finding detail. Rejected because the spec calls for direct follow-up on the existing finding detail and the inbox adds avoidable navigation indirection.
|
||
- Add a new notification-detail page. Rejected because it duplicates finding detail and creates a second follow-up surface.
|
||
- Skip send-time entitlement revalidation. Rejected because tenant membership and capability state can change between assignment and delivery. |