# 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.