create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'policy.sync', 'status' => 'running', 'outcome' => 'pending', 'started_at' => now()->subMinutes(20), 'created_at' => now()->subMinutes(20), ]); $reconciledRun = OperationRun::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'restore.execute', 'status' => 'completed', 'outcome' => 'failed', 'context' => [ 'reason_code' => 'run.adapter_out_of_sync', 'reconciliation' => [ 'reconciled_at' => now()->toIso8601String(), 'reason' => 'run.adapter_out_of_sync', 'reason_code' => 'run.adapter_out_of_sync', 'source' => 'adapter_reconciler', ], ], 'failure_summary' => [[ 'code' => 'run.adapter_out_of_sync', 'reason_code' => 'run.adapter_out_of_sync', 'message' => 'A related restore record reached terminal truth before the operation run was updated.', ]], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::index()) ->assertOk() ->assertSee('Likely stale') ->assertSee('Past the lifecycle window. Review worker health and logs before retrying.') ->assertDontSee('Progress details pending.') ->assertSee('belong in terminal follow-up'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::tenantlessView($reconciledRun)) ->assertOk() ->assertSee('Automatically reconciled') ->assertSee('Still active: No. Automatic reconciliation: Yes.'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::tenantlessView($staleRun)) ->assertOk() ->assertSee('Likely stale operation') ->assertDontSee('No action needed yet. The operation is currently in progress.') ->assertDontSee('Progress details pending.'); }); it('renders lifecycle outcome fallbacks when historical runs are missing stored outcomes', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $staleRun = OperationRun::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'policy.sync', 'status' => 'running', 'outcome' => '', 'started_at' => now()->subMinutes(20), 'created_at' => now()->subMinutes(20), ]); $reconciledRun = OperationRun::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'restore.execute', 'status' => 'completed', 'outcome' => '', 'context' => [ 'reason_code' => 'run.adapter_out_of_sync', 'reconciliation' => [ 'reconciled_at' => now()->toIso8601String(), 'reason' => 'run.adapter_out_of_sync', 'reason_code' => 'run.adapter_out_of_sync', 'source' => 'adapter_reconciler', ], ], 'failure_summary' => [[ 'code' => 'run.adapter_out_of_sync', 'reason_code' => 'run.adapter_out_of_sync', 'message' => 'A related restore record reached terminal truth before the operation run was updated.', ]], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::index()) ->assertOk() ->assertSee('Awaiting result'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::tenantlessView($staleRun)) ->assertOk() ->assertSee('Awaiting result'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::tenantlessView($reconciledRun)) ->assertOk() ->assertSee('Reconciled failed'); }); it('shows Spec359 matching-review reconciliation copy on operations hub and tenantless detail surfaces', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $run = OperationRun::factory()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'environment.review.compose', 'status' => 'completed', 'outcome' => 'succeeded', 'context' => [ 'reason_code' => 'run.adapter_out_of_sync', 'reconciliation' => [ 'reconciled_at' => now()->toIso8601String(), 'reason' => 'run.adapter_out_of_sync', 'reason_code' => 'run.adapter_out_of_sync', 'source' => 'adapter_reconciler', 'adapter' => 'environment_review_compose', 'decision' => 'reconciled_succeeded', 'related' => [ 'review' => [ 'id' => 44, ], ], ], ], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::index()) ->assertOk() ->assertSee('Automatically reconciled') ->assertSee('A matching review was already available.'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(\App\Support\OperationRunLinks::tenantlessView($run)) ->assertOk() ->assertSee('A matching review was already available.') ->assertSee('No action needed. A matching review was already available.'); });