create(); $connection = ProviderConnection::factory()->verifiedHealthy()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', ]); $run = spec367TerminalRun($tenant, [ 'type' => 'provider.connection.check', 'outcome' => OperationRunOutcome::Blocked->value, 'context' => [ 'provider' => 'microsoft', 'provider_connection_id' => (int) $connection->getKey(), ], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState) ->and($result->requiresCurrentFollowUp())->toBeFalse() ->and($result->resolvingModelType)->toBe('provider_connection') ->and(OperationRun::query()->whereKey($run->getKey())->terminalFollowUp()->exists())->toBeTrue() ->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse(); }); it('supersedes old provider blockers with later same-scope successful checks in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', ]); $oldRun = spec367TerminalRun($tenant, [ 'type' => 'provider.connection.check', 'outcome' => OperationRunOutcome::Failed->value, 'completed_at' => now()->subHours(2), 'created_at' => now()->subHours(2), 'context' => [ 'provider' => 'microsoft', 'provider_connection_id' => (int) $connection->getKey(), ], ]); $laterRun = spec367TerminalRun($tenant, [ 'type' => 'provider.connection.check', 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subHour(), 'created_at' => now()->subHour(), 'context' => [ 'provider' => 'microsoft', 'provider_connection_id' => (int) $connection->getKey(), ], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::SupersededByLaterSuccess) ->and($result->supersedingRunId)->toBe((int) $laterRun->getKey()) ->and($result->requiresCurrentFollowUp())->toBeFalse(); }); it('does not supersede repeatable runs from a different scope or correlation in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $otherTenant = ManagedEnvironment::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, ]); $oldRun = spec367TerminalRun($tenant, [ 'type' => 'inventory.sync', 'outcome' => OperationRunOutcome::Failed->value, 'completed_at' => now()->subHours(2), 'created_at' => now()->subHours(2), 'context' => ['selection_hash' => 'selected-family-a'], ]); spec367TerminalRun($otherTenant, [ 'type' => 'inventory.sync', 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subHour(), 'created_at' => now()->subHour(), 'context' => ['selection_hash' => 'selected-family-a'], ]); spec367TerminalRun($tenant, [ 'type' => 'inventory.sync', 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subMinutes(30), 'created_at' => now()->subMinutes(30), 'context' => ['selection_hash' => 'selected-family-b'], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable) ->and($result->requiresCurrentFollowUp())->toBeTrue(); }); it('supersedes repeatable runs only with later same-scope success in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $oldRun = spec367TerminalRun($tenant, [ 'type' => 'inventory.sync', 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'completed_at' => now()->subHours(2), 'created_at' => now()->subHours(2), 'context' => ['selection_hash' => 'selected-family-a'], ]); $laterRun = spec367TerminalRun($tenant, [ 'type' => 'inventory.sync', 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subHour(), 'created_at' => now()->subHour(), 'context' => ['selection_hash' => 'selected-family-a'], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::SupersededByLaterSuccess) ->and($result->supersedingRunId)->toBe((int) $laterRun->getKey()) ->and(OperationRun::query()->whereKey($oldRun->getKey())->currentTerminalFollowUp()->exists())->toBeFalse(); }); it('uses repository artifact proof to resolve terminal backup update history in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $backupSet = BackupSet::factory()->for($tenant)->recentCompleted()->create([ 'workspace_id' => (int) $tenant->workspace_id, ]); $run = spec367TerminalRun($tenant, [ 'type' => 'backup_set.update', 'outcome' => OperationRunOutcome::Blocked->value, 'context' => [ 'backup_set_id' => (int) $backupSet->getKey(), ], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState) ->and($result->resolvingModelType)->toBe('backup_set') ->and($result->resolvingModelId)->toBe((int) $backupSet->getKey()) ->and($result->requiresCurrentFollowUp())->toBeFalse(); }); it('uses ready management PDF artifact proof to resolve terminal management report generation history in Spec412', function (): void { Storage::fake('exports'); $tenant = ManagedEnvironment::factory()->create(); $pack = ReviewPack::factory()->ready()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), ]); $pdfBytes = spec367ManagementReportPdfBytes('Spec367 ready management report'); $filePath = sprintf('management-reports/spec367/%d-ready.pdf', (int) $pack->getKey()); Storage::disk('exports')->put($filePath, $pdfBytes); $run = spec367TerminalRun($tenant, [ 'type' => 'report.management.generate', 'outcome' => OperationRunOutcome::Failed->value, 'context' => [ 'source_review_pack_id' => (int) $pack->getKey(), 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, ], ]); $report = StoredReport::factory()->managementReportPdf()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'source_review_pack_id' => (int) $pack->getKey(), 'operation_run_id' => null, 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'file_path' => $filePath, 'file_size' => strlen($pdfBytes), 'sha256' => hash('sha256', $pdfBytes), ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState) ->and($result->resolvingModelType)->toBe('stored_report') ->and($result->resolvingModelId)->toBe((int) $report->getKey()) ->and($result->requiresCurrentFollowUp())->toBeFalse() ->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse(); }); it('keeps terminal management report generation actionable when ready metadata points to a missing PDF in Spec412', function (): void { Storage::fake('exports'); $tenant = ManagedEnvironment::factory()->create(); $pack = ReviewPack::factory()->ready()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), ]); $pdfBytes = spec367ManagementReportPdfBytes('Spec367 missing management report'); $run = spec367TerminalRun($tenant, [ 'type' => 'report.management.generate', 'outcome' => OperationRunOutcome::Failed->value, 'context' => [ 'source_review_pack_id' => (int) $pack->getKey(), 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, ], ]); StoredReport::factory()->managementReportPdf()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'source_review_pack_id' => (int) $pack->getKey(), 'operation_run_id' => null, 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'file_path' => sprintf('management-reports/spec367/%d-missing.pdf', (int) $pack->getKey()), 'file_size' => strlen($pdfBytes), 'sha256' => hash('sha256', $pdfBytes), ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable) ->and($result->requiresCurrentFollowUp())->toBeTrue() ->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeTrue(); }); it('keeps terminal management report generation actionable when the ready PDF belongs to another tenant scope in Spec412', function (): void { Storage::fake('exports'); $tenant = ManagedEnvironment::factory()->create(); $otherTenant = ManagedEnvironment::factory()->create(); $pack = ReviewPack::factory()->ready()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), ]); $pdfBytes = spec367ManagementReportPdfBytes('Spec367 cross tenant management report'); $filePath = sprintf('management-reports/spec367/%d-cross-tenant.pdf', (int) $pack->getKey()); Storage::disk('exports')->put($filePath, $pdfBytes); $run = spec367TerminalRun($tenant, [ 'type' => 'report.management.generate', 'outcome' => OperationRunOutcome::Failed->value, 'context' => [ 'source_review_pack_id' => (int) $pack->getKey(), 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF, ], ]); StoredReport::factory()->managementReportPdf()->create([ 'workspace_id' => (int) $otherTenant->workspace_id, 'managed_environment_id' => (int) $otherTenant->getKey(), 'source_review_pack_id' => (int) $pack->getKey(), 'operation_run_id' => null, 'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE, 'file_path' => $filePath, 'file_size' => strlen($pdfBytes), 'sha256' => hash('sha256', $pdfBytes), ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable) ->and($result->requiresCurrentFollowUp())->toBeTrue() ->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeTrue(); }); it('keeps high-risk terminal operations in manual review even when later runs succeed in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $oldRun = spec367TerminalRun($tenant, [ 'type' => 'restore.execute', 'outcome' => OperationRunOutcome::Failed->value, 'completed_at' => now()->subHours(2), 'created_at' => now()->subHours(2), ]); spec367TerminalRun($tenant, [ 'type' => 'restore.execute', 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now()->subHour(), 'created_at' => now()->subHour(), ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::RequiresManualReview) ->and($result->requiresCurrentFollowUp())->toBeTrue() ->and(OperationRun::query()->whereKey($oldRun->getKey())->currentTerminalFollowUp()->exists())->toBeTrue(); }); it('keeps reconciled successful terminal history out of current follow-up in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $run = spec367TerminalRun($tenant, [ 'type' => 'restore.execute', 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'reconciliation' => [ 'reconciled_at' => now()->toIso8601String(), 'decision' => 'completed', 'source' => 'restore_run_observer', ], ], ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::NotTerminal) ->and($result->requiresCurrentFollowUp())->toBeFalse() ->and(OperationRun::query()->whereKey($run->getKey())->terminalFollowUp()->exists())->toBeTrue() ->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse(); }); it('fails closed for unknown terminal operation types in Spec367', function (): void { $tenant = ManagedEnvironment::factory()->create(); $run = spec367TerminalRun($tenant, [ 'type' => 'unknown.operation', 'outcome' => OperationRunOutcome::Failed->value, ]); $result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh()); expect($result->status)->toBe(OperationRunActionabilityStatus::RequiresManualReview) ->and($result->reasonCode)->toBe('unknown_operation_type') ->and($result->requiresCurrentFollowUp())->toBeTrue(); }); /** * @param array $attributes */ function spec367TerminalRun(ManagedEnvironment $tenant, array $attributes = []): OperationRun { $completedAt = $attributes['completed_at'] ?? now()->subMinutes(5); return OperationRun::factory()->forTenant($tenant)->create(array_replace([ 'type' => 'policy.sync', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, 'created_at' => $completedAt, 'completed_at' => $completedAt, 'context' => [], ], $attributes)); } function spec367ManagementReportPdfBytes(string $label): string { return "%PDF-1.7\n1 0 obj\n<< /Type /Catalog /Title ({$label}) >>\nendobj\nstartxref\n0\n%%EOF"; }