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