make(); expect($snapshot->lifecycleState())->toBe(BaselineSnapshotLifecycleState::Complete) ->and($snapshot->isConsumable())->toBeTrue() ->and($snapshot->isComplete())->toBeTrue(); }); it('tracks lifecycle transitions and completion proof metadata', 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(), ]); $snapshot->markBuilding(['expected_items' => 3]); $snapshot->refresh(); expect($snapshot->lifecycleState())->toBe(BaselineSnapshotLifecycleState::Building) ->and($snapshot->completed_at)->toBeNull() ->and(data_get($snapshot->completion_meta_jsonb, 'expected_items'))->toBe(3); $snapshot->markComplete('truth-hash', ['persisted_items' => 3]); $snapshot->refresh(); expect($snapshot->lifecycleState())->toBe(BaselineSnapshotLifecycleState::Complete) ->and($snapshot->snapshot_identity_hash)->toBe('truth-hash') ->and($snapshot->completed_at)->not->toBeNull() ->and($snapshot->failed_at)->toBeNull() ->and(data_get($snapshot->completion_meta_jsonb, 'persisted_items'))->toBe(3); }); it('marks snapshots incomplete with a persisted reason code', function (): void { $snapshot = BaselineSnapshot::factory()->building()->create(); $snapshot->markIncomplete(BaselineReasonCodes::SNAPSHOT_COMPLETION_PROOF_FAILED, ['persisted_items' => 1]); $snapshot->refresh(); expect($snapshot->lifecycleState())->toBe(BaselineSnapshotLifecycleState::Incomplete) ->and($snapshot->failed_at)->not->toBeNull() ->and($snapshot->completed_at)->toBeNull() ->and(data_get($snapshot->completion_meta_jsonb, 'finalization_reason_code'))->toBe(BaselineReasonCodes::SNAPSHOT_COMPLETION_PROOF_FAILED) ->and(data_get($snapshot->completion_meta_jsonb, 'persisted_items'))->toBe(1); }); it('refuses to transition incomplete snapshots back to complete', function (): void { $snapshot = BaselineSnapshot::factory()->incomplete()->create(); expect(fn () => $snapshot->markComplete('retry-hash'))->toThrow(RuntimeException::class); });