# Research — Intune RBAC Baseline Compare & Findings v1 ## Decision: Add explicit baseline-compare support metadata on foundation types **Rationale**: `BaselineProfileResource::foundationTypeOptions()` currently lists all foundation types from `InventoryPolicyTypeMeta::foundations()`, which would make every configured foundation implicitly selectable for baseline compare. The spec requires intentional, type-level baseline eligibility metadata so only `intuneRoleDefinition` becomes eligible and `intuneRoleAssignment` stays excluded. **Alternatives considered**: - Filter foundation options with hard-coded type checks in Filament resource code: rejected because it hides eligibility rules in UI code and does not create a reusable source of truth. - Treat every foundation as baseline-eligible by default: rejected because the spec explicitly disallows broad foundation rollout. ## Decision: Reuse the existing RBAC Graph contracts and inventory/version evidence from Spec 127 **Rationale**: `config/graph_contracts.php` already contains inventory-grade contracts for `intuneRoleDefinition` and `intuneRoleAssignment`, and Spec 127 already established tenant-scoped inventory plus immutable `PolicyVersion` evidence for Role Definitions. The compare feature can therefore stay DB-first and does not need new live Graph calls for baseline rendering or run detail. **Alternatives considered**: - Add new RBAC-specific Graph contracts for compare: rejected because current contracts already include the fields needed for normalized Role Definition diffs. - Re-hydrate live Graph payloads during compare: rejected because it would violate the existing DB-first compare and monitoring model and introduce misleading drift when live state is unavailable. ## Decision: Use Role Definition ID as the compare identity for this foundation type **Rationale**: Current baseline capture and compare use display-name-derived `BaselineSubjectKey` matching. That is adequate for some policy surfaces but does not meet the spec requirement for Role Definition identity, where a delete-and-recreate with a new ID is meaningful drift. The capture and compare pipeline needs a targeted identity-model extension for `intuneRoleDefinition` so baseline items and current inventory match by stable external ID. **Alternatives considered**: - Keep display-name matching and treat renamed duplicates as ambiguous: rejected because it would hide recreated roles and violate the spec’s identity model. - Match by a composite of display name plus built-in/custom state: rejected because the object identity still changes when a role is recreated. ## Decision: Reuse `IntuneRoleDefinitionNormalizer::flattenForDiff()` as the normalized compare surface **Rationale**: The existing normalizer already produces deterministic, order-insensitive permission-block output and readable summary blocks for Role Definitions. Reusing that normalization path keeps diff semantics aligned with the inventory and version-display model from Spec 127 and avoids a second RBAC diff representation. **Alternatives considered**: - Hash raw snapshot payloads directly: rejected because transport noise and ordering differences would create unstable findings. - Build a separate RBAC-only diff normalizer: rejected because the existing normalizer already captures the governance-relevant fields required by the spec. ## Decision: Keep unified baseline.compare finding lifecycle and recurrence behavior **Rationale**: `CompareBaselineToTenantJob::upsertFindings()` already provides profile-scoped recurrence keys, idempotent `times_seen` updates, reopen behavior, and baseline auto-close support. The RBAC feature should plug into that same lifecycle so findings behave consistently with the rest of the drift engine. **Alternatives considered**: - Introduce an RBAC-specific finding table or source lifecycle: rejected because it would duplicate the compare engine and break existing findings surfaces. - Make recurrence snapshot-scoped for RBAC only: rejected because the current implementation and tests are profile-scoped, and the new spec only requires baseline profile participation in the fingerprint inputs. ## Decision: Extend the existing evidence contract instead of inventing an RBAC-only UI model **Rationale**: Existing baseline compare evidence already carries summary kind, provenance, baseline and current version references, and diff-compatible structures consumed by findings and run-detail surfaces. RBAC compare should extend those contracts with an RBAC-specific summary kind and before/after normalized evidence so existing UI surfaces can render the new findings without a dedicated page. **Alternatives considered**: - Render RBAC changes from ad-hoc JSON blobs in the UI: rejected because it would bypass the unified finding evidence contract and create fragile presentation logic. - Create a new RBAC findings screen: rejected because the spec explicitly requires compatibility with existing baseline, drift, and findings surfaces. ## Decision: Reuse existing coverage-guard and partial-success semantics for RBAC safe degradation **Rationale**: `CompareBaselineToTenantJob` already suppresses findings for uncovered types and records partial-success outcomes with coverage reason codes. RBAC compare should use the same mechanism so missing provider or permission coverage produces a clear run/report warning without emitting false drift. **Alternatives considered**: - Emit missing findings when current RBAC data is unavailable: rejected because it would create false drift. - Fail the entire compare run hard on any RBAC coverage gap: rejected because the current engine already supports safer partial-success semantics with explicit suppression reasons.