# Implementation Plan: Intune RBAC Baseline Compare & Findings v1

**Branch**: `feat/128-rbac-baseline-compare` | **Date**: 2026-03-09 | **Spec**: `specs/128-rbac-baseline-compare/spec.md`
**Input**: Feature specification from `specs/128-rbac-baseline-compare/spec.md`

## Summary

Extend the existing baseline capture and compare engine to support one additional foundation type, `intuneRoleDefinition`, by introducing explicit baseline-support metadata, capturing Role Definition baseline references from the existing RBAC version history, and adding an ID-based normalized compare path that emits unified baseline.compare findings with RBAC-specific severity, evidence, and summaries. Keep `intuneRoleAssignment` explicitly excluded from baseline scope, compare, and findings.

## Technical Context

**Language/Version**: PHP 8.4  
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4  
**Storage**: PostgreSQL via Laravel Sail  
**Testing**: Pest v4 on PHPUnit 12  
**Target Platform**: Dockerized Laravel web application (Sail)  
**Project Type**: Web application  
**Performance Goals**: Baseline capture and compare stay chunked and DB-first, avoid UI-time Graph calls, and remain bounded by covered in-scope subjects  
**Constraints**: Existing Ops-UX `OperationRun` contract, numeric-only `summary_counts`, deny-as-not-found tenant/workspace isolation, no RBAC write path, and deterministic compare/finding identity  
**Scale/Scope**: Workspace-owned baseline profiles and snapshots plus tenant-scoped compare runs and findings across potentially large in-scope baseline subject sets

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

- Inventory-first: PASS — current state remains `InventoryItem` plus existing RBAC `PolicyVersion` evidence from Spec 127; baseline snapshots stay immutable.
- Read/write separation: PASS — this release is read-only governance analysis; no restore or Graph write behavior is added.
- Graph contract path: PASS — existing `intuneRoleDefinition` and `intuneRoleAssignment` contracts already exist in `config/graph_contracts.php`; compare and UI rendering remain DB-only at runtime.
- Deterministic capabilities: PASS — scope eligibility can be derived from config metadata and helper filters with tests; no raw capability strings are needed.
- RBAC-UX planes and isolation: PASS — workspace baseline management stays in the tenant/admin plane, compare and findings stay tenant-context, and cross-plane behavior is unchanged.
- Workspace isolation: PASS — baseline profiles and snapshots remain workspace-owned; workspace membership enforcement remains unchanged.
- Tenant isolation: PASS — compare runs, current inventory, and findings remain tenant-owned and deny-as-not-found for non-members.
- Destructive confirmation: PASS — no new destructive RBAC action is introduced; existing baseline archive/capture surfaces keep their current confirmation semantics.
- Global search: PASS — no new searchable resource is added, so the existing global-search rules are unaffected.
- Run observability: PASS — baseline capture and compare already use `OperationRun` and queued jobs; this release only extends their scope and result payloads.
- Ops-UX 3-surface feedback: PASS — baseline start surfaces already use queued-only toasts and canonical run links.
- Ops-UX lifecycle: PASS — `OperationRun` transitions remain service-owned through `OperationRunService`.
- Ops-UX summary counts: PASS — RBAC-specific counts can live in `context.baseline_compare` while `summary_counts` stay numeric-only.
- Ops-UX guards: PASS — existing baseline compare guard tests can be extended for the new foundation type.
- Ops-UX system runs: PASS — unchanged; initiator-null behavior remains handled by the existing operation framework.
- Data minimization: PASS — inventory remains metadata-only; baseline evidence reuses existing version references and normalized snapshots without introducing secret-bearing payloads.
- Badge semantics: PASS — severity and label additions can use the centralized badge and renderer system.
- Filament Action Surface Contract: PASS — only existing Baseline Profile, Baseline Snapshot, Baseline Compare, and Findings surfaces are extended; no new resource or unreviewed action surface is introduced.
- Filament UX-001: PASS — the feature only adjusts scope options, summaries, and evidence blocks inside established sectioned forms and detail screens.
- Filament v5 / Livewire v4 compliance: PASS — no version or API changes are introduced.
- Provider registration (`bootstrap/providers.php`): PASS — no new providers or panels are introduced.
- Global search resource rule: PASS — no new globally searchable resource is added.
- Asset strategy: PASS — no new panel or shared assets are needed; `filament:assets` deployment behavior is unchanged.

## Project Structure

### Documentation (this feature)

```text
specs/128-rbac-baseline-compare/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md
```

### Source Code (repository root)

```text
app/
├── Filament/
│   ├── Pages/
│   │   └── BaselineCompareLanding.php
│   └── Resources/
│       ├── BaselineProfileResource.php
│       └── BaselineSnapshotResource.php
├── Jobs/
│   ├── CaptureBaselineSnapshotJob.php
│   └── CompareBaselineToTenantJob.php
├── Models/
│   ├── BaselineProfile.php
│   ├── BaselineSnapshot.php
│   ├── BaselineSnapshotItem.php
│   ├── Finding.php
│   ├── InventoryItem.php
│   └── PolicyVersion.php
├── Services/
│   ├── Baselines/
│   │   ├── BaselineCaptureService.php
│   │   ├── BaselineCompareService.php
│   │   ├── BaselineAutoCloseService.php
│   │   ├── BaselineSnapshotIdentity.php
│   │   └── Evidence/
│   ├── Intune/
│   │   ├── IntuneRoleDefinitionNormalizer.php
│   │   └── PolicyNormalizer.php
│   └── Inventory/
│       └── InventorySyncService.php
├── Support/
│   ├── Baselines/
│   │   ├── BaselineCompareReasonCode.php
│   │   ├── BaselineScope.php
│   │   └── BaselineSubjectKey.php
│   ├── Badges/
│   └── Inventory/
│       └── InventoryPolicyTypeMeta.php
config/
├── graph_contracts.php
└── tenantpilot.php
tests/
├── Feature/
│   ├── Baselines/
│   ├── Findings/
│   ├── Filament/
│   └── Inventory/
└── Unit/
    └── IntuneRoleDefinitionNormalizerTest.php
```

**Structure Decision**: Keep all work in the existing Laravel application. The feature is a targeted extension of current baseline capture/compare services, baseline Filament surfaces, config-driven type metadata, and existing baseline/findings tests.

## Complexity Tracking

No constitution violations are required for this feature.

## Phase 0 — Outline & Research (DONE)

Outputs:
- `specs/128-rbac-baseline-compare/research.md`

Key findings captured:
- Baseline scope already supports `foundation_types`, but the current UI options list every configured foundation type without an explicit baseline-support gate.
- Baseline capture already queries `scope->allTypes()` and can include foundations, but current subject identity is display-name-derived via `BaselineSubjectKey`, which is not acceptable for Role Definition compare.
- Compare and finding upsert infrastructure already supports unified lifecycle, evidence provenance, coverage guards, and recurrence logic; this feature should plug into those seams rather than inventing a separate RBAC compare engine.

## Phase 1 — Design & Contracts (DONE)

Outputs:
- `specs/128-rbac-baseline-compare/data-model.md`
- `specs/128-rbac-baseline-compare/contracts/openapi.yaml`
- `specs/128-rbac-baseline-compare/quickstart.md`

Design highlights:
- Add explicit baseline-compare metadata on foundation types so `intuneRoleDefinition` is opt-in supported and `intuneRoleAssignment` remains explicitly unsupported.
- Extend baseline capture and compare with a Role Definition ID identity strategy while leaving the existing display-name subject-key flow in place for previously supported policy types.
- Reuse `IntuneRoleDefinitionNormalizer::flattenForDiff()` for normalized governance diffs and feed the result into the existing baseline evidence and finding contracts.

## Phase 1 — Agent Context Update (DONE)

Run:
- `.specify/scripts/bash/update-agent-context.sh copilot`

## Phase 2 — Implementation Plan

### Step 1 — Add explicit baseline-support metadata for foundation types

Goal: implement FR-128-01 through FR-128-04.

Changes:
- Extend `config/tenantpilot.php` foundation-type rows with explicit baseline-compare support metadata instead of implicitly treating every foundation as baseline-eligible.
- Mark `intuneRoleDefinition` as supported for baseline compare and `intuneRoleAssignment` as unsupported.
- Add helper accessors in `App\Support\Inventory\InventoryPolicyTypeMeta` for baseline-supported foundations so scope selection, summaries, and tests read from one canonical source.
- Update `BaselineProfileResource::foundationTypeOptions()` and related infolist formatting to show only eligible foundation types while making the RBAC Role Definition label explicit.

Tests:
- Add or update focused tests proving `intuneRoleDefinition` is baseline-supported and `intuneRoleAssignment` is excluded.
- Add or update baseline profile selection tests proving Role Definitions can be chosen and Assignments cannot leak into the saved scope.

### Step 2 — Introduce Role Definition ID identity for baseline capture and compare

Goal: implement FR-128-05 through FR-128-10.

Changes:
- Extend the baseline subject identity model so `intuneRoleDefinition` can use stable external ID matching instead of the current display-name-derived `BaselineSubjectKey` flow.
- Update `CaptureBaselineSnapshotJob::collectInventorySubjects()` and `buildSnapshotItems()` to preserve the tenant Role Definition ID, a workspace-safe external reference, and an explicit identity marker for Role Definition baseline items.
- Update compare-side loaders in `CompareBaselineToTenantJob` so Role Definitions are loaded and matched by Role Definition ID while existing policy types keep their current behavior.
- Ensure delete-and-recreate with a new ID resolves to `missing` + `unexpected`, not a silent rename match.

Tests:
- Add capture tests proving baseline snapshot items for `intuneRoleDefinition` keep evidence-ready references and exclude `intuneRoleAssignment`.
- Add compare tests proving same-name/different-ID Role Definitions do not match and instead produce missing/unexpected outcomes.

### Step 3 — Add normalized RBAC Role Definition diffing and classification

Goal: implement FR-128-11 through FR-128-19.

Changes:
- Introduce a narrow Role Definition compare helper that uses `IntuneRoleDefinitionNormalizer::flattenForDiff()` as the governance-normalized diff surface.
- Define classification logic for unchanged, modified, missing, and unexpected Role Definitions.
- Split modified Role Definition diffs into metadata-only versus permission-impacting changes so severity can map to Low versus High.
- Reuse existing coverage-guard and evidence-gap handling so provider or permission issues suppress false findings instead of inventing RBAC-only failure semantics.

Tests:
- Add normalized diff tests that prove ordering noise in permission blocks is ignored.
- Add compare classification tests for unchanged, modified, missing, and unexpected Role Definitions.
- Add severity tests proving permission changes are High, missing is High, unexpected is Medium, and metadata-only is Low.

### Step 4 — Extend finding evidence, fingerprints, and run summaries for RBAC

Goal: implement FR-128-18 through FR-128-26.

Changes:
- Implement the `intuneRoleDefinition` finding fingerprint composition explicitly in the compare job so the stable fingerprint includes baseline profile scope, Role Definition identity, change kind, and normalized diff fingerprint inputs.
- Reuse baseline compare finding upsert and recurrence behavior, keeping fingerprints recurrence-stable and profile-scoped while adding `intuneRoleDefinition`-specific diff fingerprints as evidence inputs.
- Extend the evidence contract builder to emit an RBAC-specific `summary.kind`, readable before/after normalized evidence, baseline and current version references, and built-in/custom visibility.
- Extend compare-run context with an RBAC Role Definition summary bucket: total compared, unchanged, modified, missing, and unexpected.
- Update label and presentation helpers so findings and run detail surfaces identify these records as Intune RBAC Role Definition drift, not generic policy drift.

Tests:
- Add or update evidence contract tests for modified, missing, and unexpected RBAC Role Definition findings.
- Add fingerprint/idempotency tests for repeated identical compare runs and recurrence tests for resolved-then-reappearing RBAC drift.
- Add summary serialization tests for the RBAC run-level counts.

### Step 5 — Extend existing Filament surfaces without introducing RBAC restore semantics

Goal: implement FR-128-24 through FR-128-29 and the UX-001 layout and UI Action Matrix constraints already defined in the spec and constitution.

Changes:
- Update existing baseline profile, baseline snapshot, compare landing or run detail, and findings detail surfaces to surface Role Definition scope, summary counts, RBAC-specific wording, and readable evidence blocks.
- Keep action surfaces unchanged except for the new scope option and evidence presentation; no new destructive or restore actions are introduced.
- Use existing badge and tag renderers for any new severity or compare-state display values.
- Ensure no screen text implies Role Assignment coverage or executable RBAC restore.

Tests:
- Add or update Filament tests asserting the baseline profile scope picker shows Intune Role Definition and not Intune Role Assignment.
- Add or update UI tests for compare landing and finding detail labels so RBAC findings are clearly labeled, show readable evidence, and do not imply restore support.

### Step 6 — Preserve safe degradation, auditability, and isolation semantics

Goal: implement FR-128-28 through FR-128-30 and the failure-path test requirements.

Changes:
- Reuse compare coverage and evidence-gap reason codes for RBAC so unavailable current-state data results in warning or partial-success outcomes instead of false drift.
- Ensure RBAC compare audit events and `OperationRun.context` capture effective scope, RBAC compare counts, and suppression reasons without adding non-canonical `summary_counts` keys.
- Confirm workspace and tenant scoping on compare queries, finding upserts, and UI read paths.

Tests:
- Add isolation coverage ensuring one tenant’s Role Definition baseline items and findings cannot match another tenant’s current state.
- Add failure-path tests proving provider or permission gaps emit zero false RBAC findings.
- Keep existing baseline compare coverage-guard, run-authorization, and stale auto-close tests passing.

## Post-design Constitution Re-check

Expected: PASS.

- Livewire v4.0+ compliance: unchanged and preserved because no new Filament panel or Livewire version changes are introduced.
- Provider registration location: unchanged; no new providers or panel registration changes outside `bootstrap/providers.php`.
- Globally searchable resources: unchanged; no new Resource is added, so no new Edit/View global-search requirement applies.
- Destructive actions: unchanged; existing baseline archive actions remain confirmed and no new destructive RBAC action is added.
- Asset strategy: unchanged; no new assets are introduced, so the existing deploy-time `php artisan filament:assets` behavior remains sufficient.
- Testing plan: extend focused Pest coverage for baseline eligibility, scope selection, capture references, compare classification, evidence, severity, fingerprinting, assignment exclusion, isolation, failure paths, and unchanged baseline behavior for existing supported types.

