forSnapshot($tenant, $snapshot); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'review_fingerprint' => $fingerprint, ], ]); $review = EnvironmentReview::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $run->getKey(), 'fingerprint' => $fingerprint, 'status' => EnvironmentReviewStatus::Draft->value, ]); $run->update([ 'context' => array_replace(is_array($run->context) ? $run->context : [], [ 'review_id' => (int) $review->getKey(), ]), ]); $publishedRun = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, ]); EnvironmentReview::factory()->published()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $publishedRun->getKey(), 'fingerprint' => $fingerprint, ]); app()->call([new ComposeEnvironmentReviewJob((int) $review->getKey(), (int) $run->getKey()), 'handle']); $run->refresh(); $review->refresh(); expect($run->status)->toBe(OperationRunStatus::Completed->value) ->and($run->outcome)->toBe(OperationRunOutcome::Succeeded->value) ->and($run->reconciliationDecision())->toBe('reconciled_succeeded') ->and($review->status)->toBe(EnvironmentReviewStatus::Draft->value); }); it('blocks a late Spec359 job when a successor review exists but is not ready', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $snapshot = seedEnvironmentReviewEvidence($tenant); $fingerprint = app(EnvironmentReviewFingerprint::class)->forSnapshot($tenant, $snapshot); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'review_fingerprint' => $fingerprint, ], ]); $successor = EnvironmentReview::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'fingerprint' => $fingerprint, 'status' => EnvironmentReviewStatus::Draft->value, ]); $review = EnvironmentReview::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $run->getKey(), 'fingerprint' => $fingerprint, 'status' => EnvironmentReviewStatus::Superseded->value, 'superseded_by_review_id' => (int) $successor->getKey(), ]); $run->update([ 'context' => array_replace(is_array($run->context) ? $run->context : [], [ 'review_id' => (int) $review->getKey(), ]), ]); app()->call([new ComposeEnvironmentReviewJob((int) $review->getKey(), (int) $run->getKey()), 'handle']); $run->refresh(); $review->refresh(); expect($run->status)->toBe(OperationRunStatus::Completed->value) ->and($run->outcome)->toBe(OperationRunOutcome::Blocked->value) ->and($run->reconciliationDecision())->toBe('blocked') ->and($review->status)->toBe(EnvironmentReviewStatus::Superseded->value); }); it('records calm Spec359 attention copy for ambiguous matching review truth', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $snapshot = seedEnvironmentReviewEvidence($tenant); $fingerprint = app(EnvironmentReviewFingerprint::class)->forSnapshot($tenant, $snapshot); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'review_fingerprint' => $fingerprint, ], ]); $review = EnvironmentReview::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $run->getKey(), 'fingerprint' => $fingerprint, 'status' => EnvironmentReviewStatus::Draft->value, ]); $run->update([ 'context' => array_replace(is_array($run->context) ? $run->context : [], [ 'review_id' => (int) $review->getKey(), ]), ]); $firstPublishedRun = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, ]); $secondPublishedRun = OperationRun::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'type' => 'environment.review.compose', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, ]); EnvironmentReview::factory()->published()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $firstPublishedRun->getKey(), 'fingerprint' => $fingerprint, ]); EnvironmentReview::factory()->published()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'initiated_by_user_id' => (int) $user->getKey(), 'operation_run_id' => (int) $secondPublishedRun->getKey(), 'fingerprint' => $fingerprint, ]); app(AdapterRunReconciler::class)->reconcileOperationRun($run, false); $run->refresh(); expect($run->status)->toBe(OperationRunStatus::Completed->value) ->and($run->outcome)->toBe(OperationRunOutcome::Failed->value) ->and($run->reconciliationDecision())->toBe('attention_required') ->and(data_get($run->failure_summary, '0.message'))->toBe('Multiple matching reviews are available, so this run needs manual review.') ->and((string) data_get($run->failure_summary, '0.message'))->not->toContain('SQLSTATE') ->and((string) data_get($run->failure_summary, '0.message'))->not->toContain('duplicate key'); });