# Data Model — Operations Auto-Refresh Pass

## Overview
This feature does not add new tables or columns. It reuses existing persisted state to decide whether two UI surfaces should continue polling.

## Entities

### OperationRun
- **Purpose**: Canonical record for queued, running, and completed operational work shown in Monitoring → Operations.
- **Relevant fields**:
  - `id`
  - `workspace_id`
  - `tenant_id` (nullable for tenantless/canonical monitoring)
  - `type`
  - `status`
  - `outcome`
  - `context`
  - `summary_counts`
  - `failure_summary`
  - `created_at`
  - `started_at`
  - `completed_at`
- **Relationships**:
  - Belongs to `Workspace`
  - Belongs to `Tenant` (optional)
  - Belongs to initiating `User` (optional)
- **Polling rule**:
  - Poll while UX-normalized status is `queued` or `running`
  - Stop polling for normalized terminal states such as `succeeded`, `partial`, or `failed`
- **Derived constraints**:
  - Polling on run-detail surfaces must continue to respect hidden-tab and mounted-action guards
  - No direct lifecycle mutation is added; `OperationRunService` remains the only lifecycle transition owner

### ReviewPack
- **Purpose**: Persisted output of tenant review pack generation for a tenant-scoped admin surface.
- **Relevant fields**:
  - `id`
  - `workspace_id`
  - `tenant_id`
  - `operation_run_id`
  - `initiated_by_user_id`
  - `status`
  - `options`
  - `summary`
  - `generated_at`
  - `expires_at`
  - `file_path`
  - `file_disk`
  - `file_size`
  - `created_at`
  - `updated_at`
- **Relationships**:
  - Belongs to `Workspace`
  - Belongs to `Tenant`
  - Belongs to `OperationRun`
  - Belongs to initiating `User`
- **Polling rule**:
  - Poll while `status` is `queued` or `generating`
  - Stop polling when `status` is `ready`, `failed`, or `expired`
  - Do not poll when no review pack exists for the tenant

## Derived View State

### Run detail polling state
- **Source**: `OperationRun` + `RunDetailPolling::interval()`
- **Output**: `1s`, `5s`, `10s`, or `null`
- **Rules**:
  - `null` when the run is terminal
  - `null` when the tab is hidden
  - `null` when Filament action modals are mounted

### Review pack card polling state
- **Source**: latest `ReviewPack` for the current tenant
- **Output**: `10s` or `null`
- **Rules**:
  - `10s` when the latest pack is `queued` or `generating`
  - `null` otherwise

## State Transitions

### OperationRun lifecycle relevant to polling
1. `queued` → active polling enabled
2. `running` → active polling enabled
3. `completed` + terminal outcome (`succeeded`, `failed`, `partially_succeeded`) → polling disabled
4. Any unrecognized or missing status normalization → treated as non-active for polling decisions

### ReviewPack lifecycle relevant to polling
1. `queued` → polling enabled
2. `generating` → polling enabled
3. `ready` → polling disabled
4. `failed` → polling disabled
5. `expired` → polling disabled

## Validation Rules
- Polling must never start for unauthorized users; existing page and widget access checks remain authoritative.
- Polling must stop automatically once terminal state is reached.
- Polling decisions must be derived from persisted state only; no external API call is required during render.
- Terminal-state rendering must remain stable and must not trigger repeated refresh loops.
