# Phase 1 Data Model: Ops-UX Constitution Rollout (v1.3.0 Alignment) (055) **Date**: 2026-01-18 This feature is a migration: it standardizes how existing `operation_runs` records are presented via three UX surfaces. ## Entities ### 1) OperationRun (existing) **Source**: `operation_runs` table **Fields (relevant to this feature)** - `id` (int) - `tenant_id` (int) - `user_id` (int|null) — initiator user - `initiator_name` (string) - `type` (string) — operation type identifier - `status` (string) — active: `queued|running`, terminal: `completed` - `outcome` (string) — `pending|succeeded|partially_succeeded|failed|cancelled` (existing vocabulary) - `started_at` (timestamp|null) - `completed_at` (timestamp|null) - `summary_counts` (jsonb) — canonical “metrics” source for this rollout - `failure_summary` (jsonb) — array of failures with sanitized messages - `context` (jsonb) — structured context used for related links ### Status / Outcome (UX-canonical) The Ops-UX surfaces (toast/widget/db notifications) use the canonical statuses: - active: `queued` | `running` - terminal: `succeeded` | `partial` | `failed` ### Legacy / compatibility mapping (if older records exist) Some existing records may use legacy fields/values (for example `status=completed` with an `outcome`). These MUST be normalized for UX rendering as follows: Normalization rules: - `status=completed` AND `outcome=succeeded` -> `terminal=succeeded` - `status=completed` AND `outcome=partially_succeeded` -> `terminal=partial` - `status=failed` OR `outcome=failed` -> `terminal=failed` Notes: - The Monitoring UI MUST remain usable if legacy values exist. - Normalization is a presentation concern for Ops-UX; storage may remain unchanged during rollout. ### 2) OperationCatalog (new/standardized) **Purpose**: single source of truth for operation labels. **Fields** - `operation_type` (string) → `label` (string) **Rules** - Any code-produced operation type must be registered (CI guard). - Unknown types from historical data render as `Unknown operation`. ### 3) OperationRunUrl (new helper) **Purpose**: canonical URL generator for “View run”. **Rule**: all “View run” links must route to Monitoring → Operations → Run Detail. ### 4) OperationUxPresenter (new presenter/builder) **Purpose**: centralized presentation for all three surfaces. **Responsibilities** - Toast copy for queued intent - Progress widget row presentation (label, status text, optional progress) - Terminal DB notification title/body/status and optional summary rendering ## Validation rules ### Summary counts (`operation_runs.summary_counts`) - Must be a flat object/dictionary. - Allowed keys only: - `total, processed, succeeded, failed, skipped, created, updated, deleted, items, tenants` - Values must be numeric and normalized to integers. - Invalid keys/values are ignored; if nothing valid remains, no summary is rendered. ### Failure messages - Short, sanitized, no secrets/tokens, no payload dumps. - Used only for terminal failure notification body as optional suffix. ## Relationships - `OperationRun` belongs to `Tenant` - `OperationRun` belongs to `User` (initiator) (nullable) ## Derived fields for presentation - `OperationLabel` = `OperationCatalog::label(type)` (or `Unknown operation`) - `UxStatus` = `Queued|Running|Completed|Partial|Failed` (derived) - `ProgressPercent` (optional) = `processed / total` only when both exist and are valid