TenantAtlas/specs/230-findings-notification-convergence/data-model.md
ahmido 742d65f0d9
Some checks failed
Main Confidence / confidence (push) Failing after 51s
feat: converge findings notification presentation (#265)
## Summary
- converge finding, queued, and completed database notifications on one shared `OperationUxPresenter` presentation contract
- preserve existing finding and operation deep-link authorities while standardizing title, body, status/icon treatment, and single primary action
- add focused notification, findings, and guard coverage plus the full feature 230 spec artifacts

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Notifications/SharedDatabaseNotificationContractTest.php tests/Feature/Notifications/OperationRunNotificationTest.php tests/Feature/Notifications/FindingNotificationLinkTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsNotificationEventTest.php tests/Feature/Findings/FindingsNotificationRoutingTest.php tests/Feature/OpsUx/Constitution/LegacyNotificationGuardTest.php`

## Filament / Platform Notes
- Livewire v4.0+ compliance preserved on Filament v5 primitives
- provider registration remains unchanged in `apps/platform/bootstrap/providers.php`
- no globally searchable resource behavior changed in this feature
- no destructive actions were introduced
- asset strategy is unchanged; the existing `cd apps/platform && php artisan filament:assets` deploy step remains sufficient

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #265
2026-04-22 20:26:18 +00:00

190 lines
6.2 KiB
Markdown

# Data Model: Findings Notification Presentation Convergence
## Overview
This feature introduces no new persisted business entity. Existing finding truth, operation-run truth, deep-link helpers, and database-notification rows remain canonical. The new work is a bounded derived presentation layer over those existing records.
## Existing Persistent Entities
### Database Notification (`notifications` table)
**Purpose**: Existing persisted delivery artifact for operator-facing in-app notifications.
**Key fields used by this feature**:
- `id`
- `type`
- `notifiable_type`
- `notifiable_id`
- `data`
- `read_at`
- `created_at`
**Rules relevant to convergence**:
- The feature changes only the derived primary payload shape stored in `data`.
- Existing namespaced metadata such as `finding_event`, `reason_translation`, and `diagnostic_reason_code` remains secondary and consumer-specific.
- No new table or projection is added.
### Finding
**Purpose**: Canonical tenant-scoped truth for finding identity, severity, lifecycle, and notification-event context.
**Key fields used by this feature**:
- `id`
- `tenant_id`
- `workspace_id`
- `severity`
- `status`
- `owner_user_id`
- `assignee_user_id`
- `due_at`
**Rules relevant to convergence**:
- The feature does not change how finding events are generated.
- Finding links continue to resolve against the existing tenant-panel detail route.
- Finding-event metadata remains available for downstream consumers and tests.
### OperationRun
**Purpose**: Canonical truth for operation lifecycle, scope, outcome, and supporting notification context.
**Key fields used by this feature**:
- `id`
- `type`
- `status`
- `outcome`
- `tenant_id`
- `context`
- `summary_counts`
- `failure_summary`
**Rules relevant to convergence**:
- The feature does not change queued or terminal notification emit rules.
- Existing admin-plane, tenantless, and system-plane link resolution remains authoritative.
- Completed-run guidance and reason translation remain derived from current run truth.
### Notifiable Context
**Purpose**: Determines which route family and supporting context a notification may expose.
**Relevant notifiable cases**:
- tenant-scoped operator receiving a finding notification
- workspace operator receiving an admin-plane operation notification
- platform user receiving a system-plane operation notification
**Rules relevant to convergence**:
- Shared presentation must not erase plane-specific destination behavior.
- The shared contract can adapt the action URL by notifiable context, but it cannot widen visibility or flatten authorization semantics.
## Derived Models
### OperatorDatabaseNotificationPresentation
**Purpose**: Shared derived contract for the primary structure rendered in the existing Filament notification drawer.
**Fields**:
- `title`
- `body`
- `status`
- `primaryAction.label`
- `primaryAction.url`
- `primaryAction.target`
- `supportingLines[]`
- `metadata`
**Validation rules**:
- Every in-scope consumer provides exactly one primary action.
- `status` remains the single source for the existing Filament icon treatment; the feature does not introduce a second icon taxonomy.
- `supportingLines` is optional and never replaces `body` or the primary action.
- `metadata` may carry consumer-specific namespaced fields, but the shared primary structure remains stable.
### NotificationPrimaryAction
**Purpose**: Canonical one-action model for secondary context notifications.
**Fields**:
- `label`
- `url`
- `target`
**Allowed targets**:
- `finding_detail`
- `admin_operation_run`
- `tenantless_operation_run`
- `system_operation_run`
**Rules**:
- There is exactly one primary action per in-scope card.
- The action source remains the existing canonical link helper for that domain and plane.
### FindingNotificationPresentationInput
**Purpose**: Consumer-specific derived input used by the shared contract for `FindingEventNotification`.
**Fields**:
- `findingId`
- `tenantId`
- `eventType`
- `title`
- `body`
- `recipientReason`
- `tenantName`
- `severity`
- `fingerprintKey`
- `dueCycleKey`
**Rules**:
- Primary wording remains finding-first and uses `Open finding` as the action label.
- `recipientReason` stays supporting context, not the headline.
### OperationRunNotificationPresentationInput
**Purpose**: Consumer-specific derived input used by the shared contract for queued and terminal run notifications.
**Fields**:
- `runId`
- `operationType`
- `status`
- `outcome`
- `targetPlane`
- `openUrl`
- `openLabel`
- `guidanceLines[]`
- `summaryLine`
- `reasonTranslation`
**Rules**:
- Queued notifications keep their existing queued vocabulary but adopt the shared card structure.
- Completed notifications preserve terminal explanation, summary, and diagnostic fields as supporting context.
- Platform users resolve to the system-panel run detail route; non-platform users keep current admin or tenantless behavior.
## Consumer Matrix
| Consumer | Source truth | Primary action target | Required shared fields | Preserved secondary metadata |
|----------|--------------|-----------------------|------------------------|------------------------------|
| `FindingEventNotification` | `Finding` plus existing event envelope | tenant finding detail | title, body, status with existing icon treatment, `Open finding`, tenant-safe URL | `finding_event` with recipient reason, fingerprint, tenant name, severity |
| `OperationRunQueued` | `OperationRun` queued state | admin or tenantless operation run view | title, body, status with existing icon treatment, open-run label, resolved URL | minimal context derived from current run state only |
| `OperationRunCompleted` | `OperationRun` terminal state | admin, tenantless, or system operation run view | title, body, status with existing icon treatment, open-run label, resolved URL | `reason_translation`, `diagnostic_reason_code`, summary lines, failure guidance |
## Persistence Boundaries
- No new table, enum-backed persistence, or presentation-only cache is introduced.
- The shared notification contract remains derived from existing finding and operation-run truth.
- Existing `notifications.data` remains the only persisted artifact for in-app delivery.
- Existing event semantics from Spec 224 and current operation notification behavior remain unchanged.