# Service Interface: Operation Runs ## `App\Services\OperationRunService` ### `ensureRun` Idempotently creates or retrieves an active run. ```php public function ensureRun( Tenant $tenant, string $type, array $inputs, ?User $initiator = null ): OperationRun ``` - **Logic**: 1. Compute `hash = sha256(tenant_id + type + sorted_json(inputs))`. 2. Try finding active run (`queued` or `running`) with this hash. 3. If found, return it. 4. If not found, create new `queued` run. 5. Return run. 6. If an existing active run is returned (dedupe), the initiator (`user_id`, `initiator_name`) MUST NOT be replaced. - **Dispatch failure**: - If queue dispatch fails after a run was created, the system MUST NOT leave misleading queued runs; instead complete the run immediately as `failed` (e.g., failure code `queue.dispatch_failed`) and show a clear UI message. ### `updateRun` Updates the status/outcome of a run. ```php public function updateRun( OperationRun $run, string $status, ?string $outcome = null, array $summaryCounts = [], array $failures = [] ): OperationRun ``` ### `failRun` Helper to fail a run immediately. ```php public function failRun(OperationRun $run, Throwable $e): OperationRun ``` ## `App\Jobs\Middleware\TrackOperationRun` Middleware for Jobs to automatically handle `running` -> `completed`/`failed` transitions if bound to a run. ## `App\Listeners\SyncRestoreRunToOperationRun` Listener for `RestoreRun` events to update the shadow `OperationRun`. The adapter row is created/visible only once a restore run reaches `previewed` (or later).