setAccessible(true); return $reflection->invoke( $migration, (object) [ 'id' => (int) $snapshot->getKey(), 'workspace_id' => (int) $snapshot->workspace_id, 'baseline_profile_id' => (int) $snapshot->baseline_profile_id, 'captured_at' => $snapshot->captured_at, 'created_at' => $snapshot->created_at, 'updated_at' => $snapshot->updated_at, 'summary_jsonb' => json_encode($snapshot->summary_jsonb), ], is_array($snapshot->summary_jsonb) ? $snapshot->summary_jsonb : [], BaselineSnapshotItem::query()->where('baseline_snapshot_id', (int) $snapshot->getKey())->count(), ); } it('classifies legacy snapshots as complete when summary counts prove completion', function (): void { $workspace = Workspace::factory()->create(); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $snapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), 'summary_jsonb' => ['total_items' => 2], ]); BaselineSnapshotItem::factory()->count(2)->create([ 'baseline_snapshot_id' => (int) $snapshot->getKey(), ]); $classification = classifyLegacySnapshotForTest($snapshot); expect($classification['lifecycle_state'])->toBe(BaselineSnapshotLifecycleState::Complete->value) ->and(data_get($classification, 'completion_meta.expected_items'))->toBe(2) ->and(data_get($classification, 'completion_meta.persisted_items'))->toBe(2); }); it('classifies proven empty legacy captures as complete when the producer run confirms zero subjects', function (): void { $workspace = Workspace::factory()->create(); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $snapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), 'summary_jsonb' => ['total_items' => 0], ]); OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'type' => OperationRunType::BaselineCapture->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'baseline_profile_id' => (int) $profile->getKey(), 'result' => ['snapshot_id' => (int) $snapshot->getKey()], 'baseline_capture' => ['subjects_total' => 0], ], 'completed_at' => now(), ]); $classification = classifyLegacySnapshotForTest($snapshot); expect($classification['lifecycle_state'])->toBe(BaselineSnapshotLifecycleState::Complete->value) ->and(data_get($classification, 'completion_meta.was_empty_capture'))->toBeTrue(); }); it('classifies ambiguous legacy snapshots as incomplete with a conservative reason code', function (): void { $workspace = Workspace::factory()->create(); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $snapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), 'summary_jsonb' => ['total_items' => 2], ]); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => (int) $snapshot->getKey(), ]); $classification = classifyLegacySnapshotForTest($snapshot); expect($classification['lifecycle_state'])->toBe(BaselineSnapshotLifecycleState::Incomplete->value) ->and(data_get($classification, 'completion_meta.finalization_reason_code'))->toBe(BaselineReasonCodes::SNAPSHOT_LEGACY_CONTRADICTORY); });