resolve(spec427IdentityResourceType($canonicalType)); $handoff = $decision->sourceMetadata['identity_handoff']; expect($decision->sourceContractState)->toBe(CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING) ->and($handoff['identity_stability_class'])->toBe('stable_candidate') ->and($handoff['singleton_or_collection_identity_rule'])->toBe('collection_item_identity_required') ->and($handoff['display_name_is_stable_identity'])->toBeFalse() ->and($handoff['known_identity_risks'])->toContain('display_name_not_stable') ->and($handoff['known_identity_risks'])->toContain('order_and_payload_hash_not_stable_identity'); $identityFields = [ ...$handoff['preferred_identity_fields'], ...$handoff['fallback_identity_fields'], ]; expect(array_intersect($identityFields, ['displayName', 'DisplayName', 'name', 'Name', 'Identity'])) ->toBe([]); })->with([ 'transportRule', 'acceptedDomain', 'appPermissionPolicy', 'meetingPolicy', ]); it('Spec427 keeps display-only payloads blocked from stable identity readiness', function (string $canonicalType): void { $result = app(CanonicalIdentityResolver::class)->resolve( spec427IdentityResourceType($canonicalType), ['DisplayName' => 'Display-only object', 'Name' => 'Display-only object', 'Identity' => 'Global'], ['source_contract_key' => $canonicalType, 'source_version' => 'not_verified'], ); expect($result->identityState)->toBe(IdentityState::MissingExternalId) ->and($result->sourceResourceId)->toStartWith('missing:') ->and($result->canonicalResourceKey)->not->toContain('Display-only object'); })->with([ 'transportRule', 'acceptedDomain', 'appPermissionPolicy', 'meetingPolicy', ]); function spec427IdentityResourceType(string $canonicalType): TenantConfigurationResourceType { $definition = collect(ResourceTypeRegistry::defaultDefinitions()) ->firstWhere('canonical_type', $canonicalType); expect($definition)->not->toBeNull("Missing default resource type definition for {$canonicalType}."); return new TenantConfigurationResourceType($definition); }