label)->not->toBe('Unknown') ->and($spec->icon)->not->toBeNull(); })->with([ 'healthy baseline artifact' => [BadgeDomain::GovernanceArtifactContent, 'trusted'], 'false-green baseline artifact' => [BadgeDomain::GovernanceArtifactExistence, 'created_but_not_usable'], 'historical baseline trace' => [BadgeDomain::GovernanceArtifactExistence, 'historical_only'], 'healthy evidence snapshot' => [BadgeDomain::GovernanceArtifactContent, 'trusted'], 'partial evidence snapshot' => [BadgeDomain::GovernanceArtifactContent, 'partial'], 'stale evidence snapshot' => [BadgeDomain::GovernanceArtifactFreshness, 'stale'], 'internal tenant review' => [BadgeDomain::GovernanceArtifactPublicationReadiness, 'internal_only'], 'blocked tenant review' => [BadgeDomain::GovernanceArtifactPublicationReadiness, 'blocked'], 'publishable tenant review' => [BadgeDomain::GovernanceArtifactPublicationReadiness, 'publishable'], 'historical review pack' => [BadgeDomain::GovernanceArtifactExistence, 'historical_only'], 'blocked review pack' => [BadgeDomain::GovernanceArtifactPublicationReadiness, 'blocked'], 'artifact requires action' => [BadgeDomain::GovernanceArtifactActionability, 'required'], ]); it('separates review existence from publication readiness', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $snapshot = $this->makeArtifactTruthEvidenceSnapshot($tenant); $review = $this->makeArtifactTruthReview( tenant: $tenant, user: $user, snapshot: $snapshot, reviewOverrides: [ 'status' => 'draft', 'completeness_state' => 'complete', ], summaryOverrides: [ 'publish_blockers' => ['Review the missing approval note before publication.'], ], ); $truth = app(ArtifactTruthPresenter::class)->forTenantReview($review); expect($truth->artifactExistence)->toBe('created') ->and($truth->publicationReadiness)->toBe('blocked') ->and($truth->primaryLabel)->toBe('Blocked') ->and($truth->nextStepText())->toContain('Resolve'); }); it('marks ready review packs as publishable only when their source review stays publishable', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $snapshot = $this->makeArtifactTruthEvidenceSnapshot($tenant); $review = $this->makeArtifactTruthReview($tenant, $user, $snapshot); $pack = $this->makeArtifactTruthReviewPack($tenant, $user, $snapshot, $review); $truth = app(ArtifactTruthPresenter::class)->forReviewPack($pack); expect($truth->artifactExistence)->toBe('created') ->and($truth->publicationReadiness)->toBe('publishable') ->and($truth->primaryLabel)->toBe('Publishable') ->and($truth->nextStepText())->toBe('No action needed'); }); it('keeps baseline snapshot lifecycle truth separate from current-vs-historical artifact truth', function (): void { $workspace = Workspace::factory()->create(); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $historicalSnapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), 'captured_at' => now()->subDay(), 'completed_at' => now()->subDay(), ]); $currentSnapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), 'captured_at' => now(), 'completed_at' => now(), ]); $incompleteSnapshot = BaselineSnapshot::factory()->incomplete(BaselineReasonCodes::SNAPSHOT_CAPTURE_FAILED)->create([ 'workspace_id' => (int) $workspace->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), ]); $profile->update(['active_snapshot_id' => (int) $currentSnapshot->getKey()]); $historicalTruth = app(ArtifactTruthPresenter::class)->forBaselineSnapshot($historicalSnapshot->fresh()); $incompleteTruth = app(ArtifactTruthPresenter::class)->forBaselineSnapshot($incompleteSnapshot->fresh()); expect($historicalTruth->artifactExistence)->toBe('historical_only') ->and($historicalTruth->diagnosticLabel)->toBe('Superseded') ->and(BadgeCatalog::spec(BadgeDomain::BaselineSnapshotLifecycle, $historicalSnapshot->lifecycleState()->value)->label)->toBe('Complete'); expect($incompleteTruth->artifactExistence)->toBe('created_but_not_usable') ->and($incompleteTruth->diagnosticLabel)->toBe('Incomplete') ->and($incompleteTruth->reason?->reasonCode)->toBe(BaselineReasonCodes::SNAPSHOT_CAPTURE_FAILED); });