# Implementation Report: Spec 421 - Entra Core Comparable / Renderable Pack ## Status - Result: implementation loop completed; manual review findings fixed; final validation passed. - Active spec directory: `specs/421-entra-core-comparable-renderable-pack`. - Branch: `421-entra-core-comparable-renderable-pack`. - HEAD at implementation start: `a73a8f58 feat: complete m365 generic evidence coverage pack (#487)`. - Initial dirty state: active spec directory was untracked; no unrelated runtime files were dirty. - Final dirty state: expected active spec, runtime, test, and implementation-report changes only. - Historical specs: Specs 414, 415, 417, 418, 419, and 420 were used as read-only dependency context only. No files under those completed spec directories were modified or stripped of validation history. ## Activated Skills And Gates - Activated skills: `spec-kit-implementation-loop`, `pest-testing`, `browsertest`. - Repo gates applied: spec readiness, workspace scope safety, RBAC/action safety, evidence anchor contract, Product Surface, TCM cutover guard, and Filament/Livewire v5 change loop. - Gate result before code: pass with bounded conditions. - Hard-gate stop conditions: none hit. No new capture/source contract, restore/apply, certification, customer output, OperationRun type, route/navigation/action, persisted compare table, Entra table family, provider mini-platform, or `tenant_id` ownership path was introduced. - Analysis/fix iterations: one implementation/test/fix iteration plus one post-review fix iteration. The focused unit run exposed a camelCase redaction gap for `privateKey`; `CoveragePayloadRedactor` was fixed to match compacted key forms. Manual review then exposed that typed render summaries were not hard-gated on same-scope content-backed captured renderable evidence and that compare output was not visible at runtime; both findings were fixed. A related Spec 420 runtime test was updated because Conditional Access evidence now correctly promotes from `content_backed` to `renderable`. Browser review later exposed a narrow-viewport clipping issue on the existing Coverage v2 readiness badge; the badge was made non-shrinking/nowrap and the focused browser smoke now asserts it remains fully inside the section. A follow-up browser review exposed redundant `Resource type` cell content; the default table now shows the human label only, with canonical technical values remaining in inspect details. ## Files Changed - Runtime: - `apps/platform/app/Services/TenantConfiguration/ClaimGuard.php` - `apps/platform/app/Services/TenantConfiguration/CoverageEvidenceWriter.php` - `apps/platform/app/Services/TenantConfiguration/CoveragePayloadRedactor.php` - `apps/platform/app/Services/TenantConfiguration/CoverageV2ReadinessReadModel.php` - `apps/platform/app/Services/TenantConfiguration/EntraComparablePayloadNormalizer.php` - `apps/platform/app/Services/TenantConfiguration/EntraCoverageComparator.php` - `apps/platform/app/Services/TenantConfiguration/EntraRenderableSummaryBuilder.php` - `apps/platform/resources/views/filament/modals/tenant-configuration/coverage-v2-resource-inspect.blade.php` - Existing test update: - `apps/platform/tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php` - New Spec 421 tests: - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec421EntraConditionalAccessNormalizerTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec421EntraComparableDiffTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec421EntraRenderableSummaryTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec421EntraRedactionTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec421EntraClaimGuardTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec421EntraCoverageLevelPromotionTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec421EntraComparableRenderableTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec421EntraNoRestoreNoCertificationTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec421EntraNoTenantIdTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec421EntraNoMiniPlatformTest.php` - `apps/platform/tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php` - Spec artifacts: - `specs/421-entra-core-comparable-renderable-pack/tasks.md` - `specs/421-entra-core-comparable-renderable-pack/implementation-report.md` ## Implementation Summary - Added bounded Entra typed helpers under the existing Tenant Configuration service boundary: - `EntraComparablePayloadNormalizer` - `EntraCoverageComparator` - `EntraRenderableSummaryBuilder` - `conditionalAccessPolicy` is the only promoted type. It now receives deterministic typed normalization, compare output, operator-safe render summaries, and evidence-gated `CoverageLevel::Renderable` promotion when captured as content-backed evidence. - `securityDefaults`, `application`, `servicePrincipal`, `roleDefinition`, and `administrativeUnit` remain unpromoted/deferred because current repo truth does not prove content-backed evidence for them without new capture/source-contract scope. - Existing `CoveragePayloadRedactor` now also catches camelCase and separator-free sensitive key forms such as `privateKey`. - `CoverageV2ReadinessReadModel::inspectDetails()` exposes a typed render summary only when latest evidence is same-scope, content-backed, captured, renderable, and supported by the bounded Entra summary builder. - The existing Coverage v2 inspect slide-over now displays the safe Conditional Access summary and a bounded material-change compare summary while keeping raw payloads, previous/current raw compare values, source endpoints, provider response bodies, tokens, secrets, restore/certification/customer claims, and high-impact actions out of the default surface. ## Entra Evidence Matrix | Canonical type | Current repo truth | Spec 421 result | Provider/render calls | | --- | --- | --- | --- | | `conditionalAccessPolicy` | Explicit Spec 420 content-backed source contract path exists | promoted to typed comparable/renderable when captured; coverage level `renderable` | capture remains existing path; render/compare are DB-only | | `securityDefaults` | registry planning row only; no proven content-backed evidence | deferred/unpromoted | no provider/render call | | `application` | registry planning row only; no proven content-backed evidence | deferred/unpromoted | no provider/render call | | `servicePrincipal` | registry planning row only; no proven content-backed evidence | deferred/unpromoted | no provider/render call | | `roleDefinition` | registry planning row only; no proven content-backed evidence | deferred/unpromoted | no provider/render call | | `administrativeUnit` | registry planning row only; no proven content-backed evidence | deferred/unpromoted | no provider/render call | ## Normalizer / Compare / Render Proof - Normalization: - Produces deterministic Conditional Access typed shape. - Ignores volatile Graph context, etag, created, and modified timestamps. - Sorts unordered scalar lists for stable compare. - Tracks unsupported field names and redacted field paths as diagnostics. - Compare: - Emits classifications: `added`, `removed`, `changed`, `unchanged`, `ignored_volatile`, `redacted`, and `unsupported_field`. - Uses derived non-persisted importance labels: `critical`, `important`, `informational`. - Marks state changes critical; target/grant/session/condition changes important; volatile, unsupported, and redacted diagnostics informational. - Render: - Shows Conditional Access display name, state, user/group/role/application targets, client/platform/location/risk conditions, grant controls, session controls, claim state, identity state, capture time, material compare status/field labels, unsupported fields, and redacted field names. - Does not render raw payload, raw Graph response, source endpoint, provider body, before/after compare values, tokens, credential values, private keys, authorization headers, cookies, or unneeded PII. ## Claim Guard, Redaction, And Safety Proof - Claim Guard now permits only scoped internal operator wording for selected Entra comparable/renderable support. - Claim Guard blocks certified, restore-ready, customer-ready, all-resource, full, and 100 percent Entra/M365 claims. - Redaction tests prove render and compare output exclude secret values from credential-like keys, authorization headers, cookies, OperationRun diagnostic context, and audit metadata. - `Spec421EntraNoRestoreNoCertificationTest` proves Conditional Access remains not restorable, not certified, and internal-only by registry default. - `Spec421EntraNoTenantIdTest` proves the changed runtime files do not introduce `tenant_id`. - `Spec421EntraNoMiniPlatformTest` proves this implementation did not add Spec 421 Entra-specific migrations, models, routes, or Filament resources. ## Product Surface Close-Out - No-legacy posture: canonical Coverage v2 extension; no compatibility exception. - UI Surface Impact: existing Coverage v2 technical annex / internal operator inspect surface changed. - Product Surface Impact: existing rendered inspect slide-over now shows safe Conditional Access typed summary plus bounded material compare status. - Page archetype: Technical Annex / internal operator evidence inspection surface. - Surface budget: pass. No new page, route, navigation, action family, customer surface, report, export, or download was added. - Technical Annex/deep-link demotion: raw payload, source endpoint, source keys, provider IDs, OperationRun context, and evidence internals remain secondary/diagnostic; raw payload values are not default-visible. - Canonical status vocabulary: existing Coverage v2 labels only; no `Entra covered`, certified, restore-ready, customer-ready, full coverage, or 100 percent labels. - Product Surface exceptions: none. - Human Product Sanity: pass. The inspect modal answers what the selected Conditional Access policy is, whether it is enabled, who/apps it targets, what controls apply, whether material changes exist versus prior comparable evidence, and which diagnostics are unsupported/redacted without implying certification, restore, or customer proof. - Visible complexity outcome: decreased for selected Conditional Access evidence because operators no longer need to infer meaning from raw payloads. ## Browser Proof - Browser proof required: yes, rendered Coverage v2 output changed. - Focused path: existing Coverage v2 readiness route for a seeded workspace and managed environment with prior comparable Conditional Access evidence and latest content-backed renderable evidence. - Test: `apps/platform/tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php`. - Result: passed. - Verified: - page loads in `/admin` workspace/managed-environment context - Conditional Access row is visible with `Renderable` and `Internal only` - inspect slide-over opens from the existing primary link column - typed summary shows display name, state, users, applications, grant controls, compare summary, material change field label, redacted fields - no raw secrets, certified/restore-ready/customer-ready/full/100 percent Entra claims, high-impact actions, console errors, JavaScript errors, or Graph/TCM/provider network calls during render ## Filament v5 Output Contract - Livewire v4.0+ compliance: unchanged; the existing Filament v5/Livewire v4 surface is exercised by the focused browser test. - Provider registration location: no panel/provider registration changed. Laravel 12 providers remain under `apps/platform/bootstrap/providers.php`. - Global search: no Filament Resource global-search behavior changed; no new Resource was added. - Destructive/high-impact actions: none added or changed. No restore/apply/capture/export/certify action was introduced. - Asset strategy: no assets registered and no frontend bundles changed. `php artisan filament:assets` is not newly required beyond existing deployment practice. - Testing plan: unit, feature, related regression, and browser lanes were run. ## Validation Commands - `cd apps/platform && ./vendor/bin/sail php -l app/Services/TenantConfiguration/EntraComparablePayloadNormalizer.php` - passed. - `cd apps/platform && ./vendor/bin/sail php -l app/Services/TenantConfiguration/EntraCoverageComparator.php` - passed. - `cd apps/platform && ./vendor/bin/sail php -l app/Services/TenantConfiguration/EntraRenderableSummaryBuilder.php` - passed. - `cd apps/platform && ./vendor/bin/sail php -l app/Services/TenantConfiguration/CoverageEvidenceWriter.php` - passed. - `cd apps/platform && ./vendor/bin/sail php -l app/Services/TenantConfiguration/CoverageV2ReadinessReadModel.php` - passed. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec421EntraConditionalAccessNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraComparableDiffTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRenderableSummaryTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRedactionTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraClaimGuardTest.php tests/Feature/TenantConfiguration/Spec421EntraCoverageLevelPromotionTest.php tests/Feature/TenantConfiguration/Spec421EntraComparableRenderableTest.php tests/Feature/TenantConfiguration/Spec421EntraNoRestoreNoCertificationTest.php tests/Feature/TenantConfiguration/Spec421EntraNoTenantIdTest.php tests/Feature/TenantConfiguration/Spec421EntraNoMiniPlatformTest.php` - passed, 24 tests / 98 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 1 test / 52 assertions. - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - passed. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec421EntraConditionalAccessNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraComparableDiffTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRenderableSummaryTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRedactionTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraClaimGuardTest.php tests/Feature/TenantConfiguration/Spec421EntraCoverageLevelPromotionTest.php tests/Feature/TenantConfiguration/Spec421EntraComparableRenderableTest.php tests/Feature/TenantConfiguration/Spec421EntraNoRestoreNoCertificationTest.php tests/Feature/TenantConfiguration/Spec421EntraNoTenantIdTest.php tests/Feature/TenantConfiguration/Spec421EntraNoMiniPlatformTest.php tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 25 tests / 150 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec415CoverageRedactionTest.php tests/Unit/Support/TenantConfiguration/Spec415GenericPayloadNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec417CoverageIdentityStrategyRegistryTest.php tests/Unit/Support/TenantConfiguration/Spec420M365GenericPayloadNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureRedactionTest.php tests/Feature/TenantConfiguration/Spec415GenericContentBackedCaptureTest.php tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php tests/Feature/TenantConfiguration/Spec420M365NoOverclaimTest.php tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php` - passed, 23 tests / 248 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php tests/Feature/Filament/CoverageV2ReadinessPageTest.php` - passed, 12 tests / 189 assertions. - `git diff --check` - passed. - Post-browser-comment validation: focused integrated-browser check at narrow viewport confirmed the `Ready` badge rendered as full text within the Activation readiness section; `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php` passed, 1 test / 53 assertions. - Post-resource-type-redundancy validation: the Resource instances table now omits the redundant canonical type line from the default `Resource type` cell while keeping canonical type available in inspect details; the focused browser smoke asserts this table-cell behavior. ## Deployment Impact - Migrations: none. - Environment variables: none. - Queue/cron: no new queue or scheduled work; existing capture path remains unchanged. - Storage/volumes: none. - Assets: none. - Dokploy/staging: deploy as ordinary app code/test/view change. Staging validation should include the focused Coverage v2 inspect path before production promotion. - Rollback: revert code/view/test changes. No data migration or compatibility step required. ## Deferred Work And Residual Risk - Deferred by spec: Security Defaults content-backed capture/source contract, application/service principal comparable/renderable pack, role definition and administrative unit comparable/renderable pack, certified compare, restore/apply, customer reports, Review Pack output, exports/downloads, and broad M365/Entra claims. - Residual risk: this proves deterministic typed semantics for captured Conditional Access payloads in the existing internal Coverage v2 path; it does not certify real Microsoft tenant-wide Entra coverage. - Remaining in-scope findings: none after the focused test/fix loop.