570 lines
24 KiB
PHP
570 lines
24 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Governance;
|
|
|
|
use App\Support\Inventory\InventoryPolicyTypeMeta;
|
|
use App\Support\OperationCatalog;
|
|
use App\Support\Providers\ProviderReasonCodes;
|
|
|
|
final class PlatformVocabularyGlossary
|
|
{
|
|
public const string BOUNDARY_PLATFORM_CORE = 'platform_core';
|
|
|
|
public const string BOUNDARY_CROSS_DOMAIN_GOVERNANCE = 'cross_domain_governance';
|
|
|
|
public const string BOUNDARY_INTUNE_SPECIFIC = 'intune_specific';
|
|
|
|
public const string OWNER_PLATFORM_CORE = 'platform_core';
|
|
|
|
public const string OWNER_DOMAIN_OWNED = 'domain_owned';
|
|
|
|
public const string OWNER_PROVIDER_OWNED = 'provider_owned';
|
|
|
|
public const string OWNER_COMPATIBILITY_ALIAS = 'compatibility_alias';
|
|
|
|
public const string OWNER_COMPATIBILITY_ONLY = 'compatibility_only';
|
|
|
|
/**
|
|
* @return list<PlatformVocabularyTerm>
|
|
*/
|
|
public function terms(): array
|
|
{
|
|
return array_values(array_map(
|
|
static fn (array $term): PlatformVocabularyTerm => PlatformVocabularyTerm::fromArray($term),
|
|
$this->configuredTerms(),
|
|
));
|
|
}
|
|
|
|
public function term(string $term): ?PlatformVocabularyTerm
|
|
{
|
|
$normalized = trim(mb_strtolower($term));
|
|
|
|
foreach ($this->terms() as $candidate) {
|
|
if (trim(mb_strtolower($candidate->termKey)) === $normalized) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
|
|
return $this->resolveAlias($term);
|
|
}
|
|
|
|
public function resolveAlias(string $term, ?string $context = null): ?PlatformVocabularyTerm
|
|
{
|
|
$normalized = trim(mb_strtolower($term));
|
|
$normalizedContext = is_string($context) ? trim(mb_strtolower($context)) : null;
|
|
|
|
foreach ($this->terms() as $candidate) {
|
|
$aliases = array_map(
|
|
static fn (string $alias): string => trim(mb_strtolower($alias)),
|
|
$candidate->legacyAliases,
|
|
);
|
|
|
|
if (! in_array($normalized, $aliases, true)) {
|
|
continue;
|
|
}
|
|
|
|
if ($normalizedContext !== null && $candidate->allowedContexts !== []) {
|
|
$contexts = array_map(
|
|
static fn (string $allowedContext): string => trim(mb_strtolower($allowedContext)),
|
|
$candidate->allowedContexts,
|
|
);
|
|
|
|
if (! in_array($normalizedContext, $contexts, true)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return $candidate;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function canonicalName(string $term): ?string
|
|
{
|
|
return $this->term($term)?->termKey;
|
|
}
|
|
|
|
public function isCanonical(string $term): bool
|
|
{
|
|
$resolved = $this->term($term);
|
|
|
|
if (! $resolved instanceof PlatformVocabularyTerm) {
|
|
return false;
|
|
}
|
|
|
|
return trim(mb_strtolower($term)) === trim(mb_strtolower($resolved->termKey));
|
|
}
|
|
|
|
public function ownership(string $term): ?string
|
|
{
|
|
return $this->term($term)?->ownerLayer;
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public function canonicalTerms(): array
|
|
{
|
|
return array_values(array_map(
|
|
static fn (PlatformVocabularyTerm $term): string => $term->termKey,
|
|
$this->terms(),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{
|
|
* term_key: string,
|
|
* canonical_label: string,
|
|
* canonical_description: string,
|
|
* boundary_classification: string,
|
|
* owner_layer: string,
|
|
* allowed_contexts: list<string>,
|
|
* legacy_aliases: list<string>,
|
|
* alias_retirement_path: ?string,
|
|
* forbidden_platform_aliases: list<string>
|
|
* }>
|
|
*/
|
|
public function termInventory(): array
|
|
{
|
|
return collect($this->terms())
|
|
->mapWithKeys(static fn (PlatformVocabularyTerm $term): array => [
|
|
$term->termKey => $term->toArray(),
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/**
|
|
* @return array<string, list<string>>
|
|
*/
|
|
public function legacyAliases(): array
|
|
{
|
|
return collect($this->terms())
|
|
->filter(static fn (PlatformVocabularyTerm $term): bool => $term->legacyAliases !== [])
|
|
->mapWithKeys(static fn (PlatformVocabularyTerm $term): array => [
|
|
$term->termKey => $term->legacyAliases,
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{
|
|
* canonical_name: string,
|
|
* legacy_aliases: list<string>,
|
|
* retirement_path: ?string,
|
|
* forbidden_platform_aliases: list<string>
|
|
* }>
|
|
*/
|
|
public function aliasRetirementInventory(): array
|
|
{
|
|
return collect($this->terms())
|
|
->filter(static fn (PlatformVocabularyTerm $term): bool => $term->legacyAliases !== [])
|
|
->mapWithKeys(static fn (PlatformVocabularyTerm $term): array => [
|
|
$term->termKey => [
|
|
'canonical_name' => $term->termKey,
|
|
'legacy_aliases' => $term->legacyAliases,
|
|
'retirement_path' => $term->aliasRetirementPath,
|
|
'forbidden_platform_aliases' => $term->forbiddenPlatformAliases,
|
|
],
|
|
])
|
|
->all();
|
|
}
|
|
|
|
public function boundaryClassification(string $term): ?string
|
|
{
|
|
return $this->term($term)?->boundaryClassification;
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public function allowedBoundaryClassifications(): array
|
|
{
|
|
return [
|
|
self::BOUNDARY_PLATFORM_CORE,
|
|
self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
self::BOUNDARY_INTUNE_SPECIFIC,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return list<RegistryOwnershipDescriptor>
|
|
*/
|
|
public function registries(): array
|
|
{
|
|
return array_values(array_map(
|
|
static fn (array $descriptor): RegistryOwnershipDescriptor => RegistryOwnershipDescriptor::fromArray($descriptor),
|
|
$this->configuredRegistries(),
|
|
));
|
|
}
|
|
|
|
public function registry(string $registryKey): ?RegistryOwnershipDescriptor
|
|
{
|
|
$normalized = trim(mb_strtolower($registryKey));
|
|
|
|
foreach ($this->registries() as $descriptor) {
|
|
if (trim(mb_strtolower($descriptor->registryKey)) === $normalized) {
|
|
return $descriptor;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{
|
|
* registry_key: string,
|
|
* boundary_classification: string,
|
|
* owner_layer: string,
|
|
* source_class_or_file: string,
|
|
* canonical_nouns: list<string>,
|
|
* allowed_consumers: list<string>,
|
|
* compatibility_notes: ?string
|
|
* }>
|
|
*/
|
|
public function registryInventory(): array
|
|
{
|
|
return collect($this->registries())
|
|
->mapWithKeys(static fn (RegistryOwnershipDescriptor $descriptor): array => [
|
|
$descriptor->registryKey => $descriptor->toArray(),
|
|
])
|
|
->all();
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{
|
|
* owner_namespace: string,
|
|
* boundary_classification: string,
|
|
* owner_layer: string,
|
|
* compatibility_notes: ?string
|
|
* }>
|
|
*/
|
|
public function reasonNamespaceInventory(): array
|
|
{
|
|
return $this->configuredReasonNamespaces();
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* owner_namespace: string,
|
|
* boundary_classification: string,
|
|
* owner_layer: string,
|
|
* compatibility_notes: ?string
|
|
* }|null
|
|
*/
|
|
public function reasonNamespace(string $ownerNamespace): ?array
|
|
{
|
|
$normalized = trim(mb_strtolower($ownerNamespace));
|
|
|
|
foreach ($this->reasonNamespaceInventory() as $descriptor) {
|
|
$candidate = is_string($descriptor['owner_namespace'] ?? null)
|
|
? trim(mb_strtolower((string) $descriptor['owner_namespace']))
|
|
: null;
|
|
|
|
if ($candidate === $normalized) {
|
|
return $descriptor;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function classifyReasonNamespace(string $ownerNamespace): ?string
|
|
{
|
|
return $this->reasonNamespace($ownerNamespace)['boundary_classification'] ?? null;
|
|
}
|
|
|
|
public function classifyOperationType(string $operationType): ?string
|
|
{
|
|
if (trim($operationType) === '') {
|
|
return null;
|
|
}
|
|
|
|
return $this->registry('operation_catalog')?->boundaryClassification;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array<string, mixed>>
|
|
*/
|
|
private function configuredTerms(): array
|
|
{
|
|
$configured = config('tenantpilot.platform_vocabulary.terms');
|
|
|
|
if (is_array($configured) && $configured !== []) {
|
|
return $configured;
|
|
}
|
|
|
|
return [
|
|
'governed_subject' => [
|
|
'term_key' => 'governed_subject',
|
|
'canonical_label' => 'Governed subject',
|
|
'canonical_description' => 'The platform-facing noun for a governed object across compare, snapshot, evidence, and review surfaces.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['compare', 'snapshot', 'evidence', 'review', 'reporting'],
|
|
'legacy_aliases' => ['policy_type'],
|
|
'alias_retirement_path' => 'Retire false-universal policy_type wording from platform-owned summaries and descriptors while preserving Intune-owned storage.',
|
|
'forbidden_platform_aliases' => ['policy_type'],
|
|
],
|
|
'domain_key' => [
|
|
'term_key' => 'domain_key',
|
|
'canonical_label' => 'Governance domain',
|
|
'canonical_description' => 'The canonical domain discriminator for cross-domain and platform-near contracts.',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['governance', 'compare', 'snapshot', 'reporting'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'subject_class' => [
|
|
'term_key' => 'subject_class',
|
|
'canonical_label' => 'Subject class',
|
|
'canonical_description' => 'The canonical subject-class discriminator for governed-subject contracts.',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['governance', 'compare', 'snapshot'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'subject_type_key' => [
|
|
'term_key' => 'subject_type_key',
|
|
'canonical_label' => 'Governed subject key',
|
|
'canonical_description' => 'The domain-owned subject-family key used by platform-near descriptors.',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['governance', 'compare', 'snapshot', 'evidence'],
|
|
'legacy_aliases' => ['policy_type'],
|
|
'alias_retirement_path' => 'Prefer subject_type_key on platform-near payloads and keep policy_type only where the owning model is Intune-specific.',
|
|
'forbidden_platform_aliases' => ['policy_type'],
|
|
],
|
|
'subject_type_label' => [
|
|
'term_key' => 'subject_type_label',
|
|
'canonical_label' => 'Governed subject label',
|
|
'canonical_description' => 'The operator-facing label for a governed subject family.',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['snapshot', 'evidence', 'compare', 'review'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'resource_type' => [
|
|
'term_key' => 'resource_type',
|
|
'canonical_label' => 'Resource type',
|
|
'canonical_description' => 'Optional resource-shaped noun for platform-facing summaries when a governed subject also needs a resource family label.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['reporting', 'review'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'operation_type' => [
|
|
'term_key' => 'operation_type',
|
|
'canonical_label' => 'Operation type',
|
|
'canonical_description' => 'The canonical platform operation identifier shown on monitoring and launch surfaces.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['monitoring', 'reporting', 'launch_surfaces'],
|
|
'legacy_aliases' => ['type'],
|
|
'alias_retirement_path' => 'Expose canonical operation_type on read models while operation_runs.type remains a compatibility storage seam during rollout.',
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'platform_reason_family' => [
|
|
'term_key' => 'platform_reason_family',
|
|
'canonical_label' => 'Platform reason family',
|
|
'canonical_description' => 'The cross-domain platform family that explains the top-level cause category without erasing domain ownership.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['reason_translation', 'monitoring', 'review', 'reporting'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'reason_owner.owner_namespace' => [
|
|
'term_key' => 'reason_owner.owner_namespace',
|
|
'canonical_label' => 'Reason owner namespace',
|
|
'canonical_description' => 'The explicit namespace that marks whether a translated reason is provider-owned, governance-owned, access-owned, or runtime-owned.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['reason_translation', 'review', 'reporting'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'reason_code' => [
|
|
'term_key' => 'reason_code',
|
|
'canonical_label' => 'Reason code',
|
|
'canonical_description' => 'The original domain-owned or provider-owned reason identifier preserved for diagnostics and translation lookup.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['reason_translation', 'diagnostics'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'registry_key' => [
|
|
'term_key' => 'registry_key',
|
|
'canonical_label' => 'Registry key',
|
|
'canonical_description' => 'The stable identifier for a contributor-facing registry or catalog.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['governance', 'contributor_guidance'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'boundary_classification' => [
|
|
'term_key' => 'boundary_classification',
|
|
'canonical_label' => 'Boundary classification',
|
|
'canonical_description' => 'The explicit classification that marks a term or registry as platform_core, cross_domain_governance, or intune_specific.',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'allowed_contexts' => ['governance', 'contributor_guidance'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => [],
|
|
],
|
|
'policy_type' => [
|
|
'term_key' => 'policy_type',
|
|
'canonical_label' => 'Intune policy type',
|
|
'canonical_description' => 'The Intune-specific discriminator that remains valid on adapter-owned or Intune-owned models but must not be treated as a universal platform noun.',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_DOMAIN_OWNED,
|
|
'allowed_contexts' => ['intune_adapter', 'intune_inventory', 'intune_backup'],
|
|
'legacy_aliases' => [],
|
|
'alias_retirement_path' => null,
|
|
'forbidden_platform_aliases' => ['governed_subject'],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array<string, mixed>>
|
|
*/
|
|
private function configuredRegistries(): array
|
|
{
|
|
$configured = config('tenantpilot.platform_vocabulary.registries');
|
|
|
|
if (is_array($configured) && $configured !== []) {
|
|
return $configured;
|
|
}
|
|
|
|
return [
|
|
'governance_subject_taxonomy_registry' => [
|
|
'registry_key' => 'governance_subject_taxonomy_registry',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'source_class_or_file' => GovernanceSubjectTaxonomyRegistry::class,
|
|
'canonical_nouns' => ['domain_key', 'subject_class', 'subject_type_key', 'subject_type_label'],
|
|
'allowed_consumers' => ['baseline_scope', 'compare', 'snapshot', 'review'],
|
|
'compatibility_notes' => 'Provides the governed-subject source of truth, resolves legacy policy-type payloads, and preserves inactive or future-domain entries without exposing them as active operator choices.',
|
|
],
|
|
'operation_catalog' => [
|
|
'registry_key' => 'operation_catalog',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'source_class_or_file' => OperationCatalog::class,
|
|
'canonical_nouns' => ['operation_type'],
|
|
'allowed_consumers' => ['monitoring', 'reporting', 'launch_surfaces', 'audit'],
|
|
'compatibility_notes' => 'Resolves canonical operation meaning from historical storage values without treating every stored raw string as equally canonical.',
|
|
],
|
|
'provider_reason_codes' => [
|
|
'registry_key' => 'provider_reason_codes',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_PROVIDER_OWNED,
|
|
'source_class_or_file' => ProviderReasonCodes::class,
|
|
'canonical_nouns' => ['reason_code'],
|
|
'allowed_consumers' => ['reason_translation'],
|
|
'compatibility_notes' => 'Provider-owned reason codes remain namespaced domain details and become platform-safe only through the reason-translation boundary.',
|
|
],
|
|
'inventory_policy_type_catalog' => [
|
|
'registry_key' => 'inventory_policy_type_catalog',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_DOMAIN_OWNED,
|
|
'source_class_or_file' => InventoryPolicyTypeMeta::class,
|
|
'canonical_nouns' => ['policy_type'],
|
|
'allowed_consumers' => ['intune_adapter', 'inventory', 'backup'],
|
|
'compatibility_notes' => 'The supported Intune policy-type list is not a universal platform registry and must be wrapped before reuse on platform-near summaries.',
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{
|
|
* owner_namespace: string,
|
|
* boundary_classification: string,
|
|
* owner_layer: string,
|
|
* compatibility_notes: ?string
|
|
* }>
|
|
*/
|
|
private function configuredReasonNamespaces(): array
|
|
{
|
|
$configured = config('tenantpilot.platform_vocabulary.reason_namespaces');
|
|
|
|
if (is_array($configured) && $configured !== []) {
|
|
return $configured;
|
|
}
|
|
|
|
return [
|
|
'tenant_operability' => [
|
|
'owner_namespace' => 'tenant_operability',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'compatibility_notes' => 'Tenant operability reasons are platform-core guardrails for workspace and tenant context.',
|
|
],
|
|
'execution_denial' => [
|
|
'owner_namespace' => 'execution_denial',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'compatibility_notes' => 'Execution denial reasons remain platform-core run-legitimacy semantics.',
|
|
],
|
|
'operation_lifecycle' => [
|
|
'owner_namespace' => 'operation_lifecycle',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'compatibility_notes' => 'Lifecycle reconciliation reasons remain platform-core monitoring semantics.',
|
|
],
|
|
'governance.baseline_compare' => [
|
|
'owner_namespace' => 'governance.baseline_compare',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_DOMAIN_OWNED,
|
|
'compatibility_notes' => 'Baseline-compare reason codes are governance-owned details translated through the platform boundary.',
|
|
],
|
|
'governance.artifact_truth' => [
|
|
'owner_namespace' => 'governance.artifact_truth',
|
|
'boundary_classification' => self::BOUNDARY_CROSS_DOMAIN_GOVERNANCE,
|
|
'owner_layer' => self::OWNER_DOMAIN_OWNED,
|
|
'compatibility_notes' => 'Artifact-truth reason codes remain governance-owned and are surfaced through platform-safe summaries.',
|
|
],
|
|
'provider.microsoft_graph' => [
|
|
'owner_namespace' => 'provider.microsoft_graph',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_PROVIDER_OWNED,
|
|
'compatibility_notes' => 'Microsoft Graph provider reasons remain provider-owned and Intune-specific.',
|
|
],
|
|
'provider.intune_rbac' => [
|
|
'owner_namespace' => 'provider.intune_rbac',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_PROVIDER_OWNED,
|
|
'compatibility_notes' => 'Provider-owned Intune RBAC reasons remain Intune-specific.',
|
|
],
|
|
'rbac.intune' => [
|
|
'owner_namespace' => 'rbac.intune',
|
|
'boundary_classification' => self::BOUNDARY_INTUNE_SPECIFIC,
|
|
'owner_layer' => self::OWNER_DOMAIN_OWNED,
|
|
'compatibility_notes' => 'RBAC detail remains Intune-specific domain context.',
|
|
],
|
|
'reason_translation.fallback' => [
|
|
'owner_namespace' => 'reason_translation.fallback',
|
|
'boundary_classification' => self::BOUNDARY_PLATFORM_CORE,
|
|
'owner_layer' => self::OWNER_PLATFORM_CORE,
|
|
'compatibility_notes' => 'Fallback translation remains a platform-core compatibility seam until a domain owner is known.',
|
|
],
|
|
];
|
|
}
|
|
} |