'array', 'failures' => 'array', 'processed_items' => 'integer', 'total_items' => 'integer', 'succeeded' => 'integer', 'failed' => 'integer', 'skipped' => 'integer', ]; public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function auditLog(): BelongsTo { return $this->belongsTo(AuditLog::class); } public function runType(): string { return "{$this->resource}.{$this->action}"; } public function statusBucket(): string { $status = $this->status; if ($status === 'pending') { return 'queued'; } if ($status === 'running') { return 'running'; } $succeededCount = (int) ($this->succeeded ?? 0); $failedCount = (int) ($this->failed ?? 0); $failureEntries = $this->failures ?? []; $hasNonSkippedFailure = false; foreach ($failureEntries as $entry) { if (! is_array($entry)) { continue; } if (($entry['type'] ?? 'failed') !== 'skipped') { $hasNonSkippedFailure = true; break; } } $hasFailures = $failedCount > 0 || $hasNonSkippedFailure; if ($succeededCount > 0 && $hasFailures) { return 'partially succeeded'; } if ($succeededCount === 0 && $hasFailures) { return 'failed'; } return match ($status) { 'completed', 'completed_with_errors' => 'succeeded', 'failed', 'aborted' => 'failed', default => 'failed', }; } }