# Data Model (Spec 086)

This feature consolidates execution tracking into `operation_runs` while keeping legacy run tables as read-only history.

## Entities

### 1) OperationRun (canonical)

**Table:** `operation_runs`

**Purpose:** Single source of truth for execution tracking: status/progress, results (counts), failures, provenance/context.

**Fields (current):**
- `id`
- `workspace_id` (FK, required)
- `tenant_id` (FK, nullable)
- `user_id` (FK, nullable)
- `initiator_name` (string)
- `type` (string; see OperationRunType registry)
- `status` (string; queued|running|completed)
- `outcome` (string; pending|succeeded|partially_succeeded|failed|blocked…)
- `run_identity_hash` (string; deterministic hash for idempotency)
- `summary_counts` (json/array; normalized counts + key metadata)
- `failure_summary` (json/array; structured failures, sanitized)
- `context` (json/array; provenance + inputs + target scope)
- `started_at`, `completed_at`, `created_at`, `updated_at`

**Indexes / constraints (current):**
- `(workspace_id, type, created_at)` and `(workspace_id, created_at)`
- `(tenant_id, type, created_at)` and `(tenant_id, created_at)`
- Partial unique indexes for active runs:
  - tenant-scoped: unique `(tenant_id, run_identity_hash)` where `tenant_id IS NOT NULL` and `status IN ('queued','running')`
  - workspace-scoped: unique `(workspace_id, run_identity_hash)` where `tenant_id IS NULL` and `status IN ('queued','running')`

**Context contract (current patterns):**
The `context` JSON is used for “related links” and display. Existing keys include (non-exhaustive):
- `provider_connection_id`
- `backup_schedule_id`
- `backup_schedule_run_id`
- `backup_set_id`
- `policy_id`
- `restore_run_id`
- `target_scope` (nested object)
- `selection` and `idempotency` objects for bulk operations

**Required additions for Spec 086 (planned):**
- New `type` values:
  - `backup_schedule.scheduled`
  - `directory_role_definitions.sync`
- Scheduled backup context keys:
  - `backup_schedule_id`
  - `scheduled_for` (UTC timestamp/minute)
  - Optional `backup_schedule_run_id` if the legacy table remains for history during transition

### 2) InventorySyncRun (legacy)

**Table:** `inventory_sync_runs`

**Purpose:** Historical record (read-only) for pre-cutover tracking.

**Key fields:**
- `tenant_id`
- `selection_hash`
- `selection_payload` (nullable)
- status + timestamps + counters

**Planned optional mapping:**
- Add nullable `operation_run_id` FK to enable deterministic redirect to canonical viewer when present. No backfill required.

### 3) EntraGroupSyncRun (legacy)

**Table:** `entra_group_sync_runs`

**Purpose:** Historical record (read-only) for pre-cutover group sync tracking.

**Key fields:**
- `tenant_id`
- `selection_key`, `slot_key`
- status + error fields + counters

**Planned optional mapping:**
- Add nullable `operation_run_id` FK to enable deterministic redirect when present.

### 4) BackupScheduleRun (legacy)

**Table:** `backup_schedule_runs`

**Purpose:** Historical record of backup schedule executions.

**Planned behavior change:**
- Distinguish scheduled fires vs manual/retry at the OperationRun level by introducing `backup_schedule.scheduled` type.

**Planned optional mapping:**
- Add nullable `operation_run_id` FK to enable deterministic redirect when present.

### 5) RestoreRun (domain)

**Table:** `restore_runs`

**Purpose:** Domain workflow record (requested items, dry-run, preview/results). Execution tracking and “View run” uses `operation_runs`.

**Current linkage approach:**
- Canonical runs store `restore_run_id` in `operation_runs.context` (used by `OperationRunLinks::related`).

## Enumerations / Registries

### OperationRunType

**Location:** `app/Support/OperationRunType.php`

**Planned additions:**
- `BackupScheduleScheduled = 'backup_schedule.scheduled'`
- `DirectoryRoleDefinitionsSync = 'directory_role_definitions.sync'`

### OperationCatalog

**Location:** `app/Support/OperationCatalog.php`

**Planned additions:**
- Human label for `backup_schedule.scheduled`
- Human label for `directory_role_definitions.sync`
- Optional expected durations (if known)

## State transitions

### OperationRun

- `queued` → `running` → `completed`
- `outcome` starts as `pending`, transitions to one of: `succeeded`, `partially_succeeded`, `failed`, `blocked`.

The canonical update surface is `OperationRunService` (`dispatchOrFail`, `updateRun`, `appendFailures`, `incrementSummaryCounts`, etc.).
