Implements spec 424 with comparable renderable capture/readiness changes and supporting tests/spec artifacts. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #491
18 KiB
Implementation Plan: Spec 424 - Security Defaults Content-Backed Comparable Support
Branch: 424-security-defaults-content-backed-comparable-support | Date: 2026-06-30 | Spec: spec.md
Input: Feature specification from specs/424-security-defaults-content-backed-comparable-support/spec.md
Summary
Close the precise Coverage v2 gap for securityDefaults: it is registry-only/out-of-scope today and cannot participate in later Entra certification. The implementation should either prove and promote Security Defaults through the existing Coverage v2 path as content-backed, comparable, and renderable internal/operator evidence, or keep it safely blocked with no fake support.
The path is intentionally narrow: source contract, capture eligibility, evidence persistence, canonical identity, typed normalization, deterministic compare, operator-safe render summary, Claim Guard, redaction, RBAC/scope, Product Surface proof if rendered, and focused tests. No certification, restore/apply, customer output, new dashboard, new route, new table, or additional Entra type is in scope.
Technical Context
Language/Version: PHP 8.4, Laravel 12, Filament v5, Livewire v4, Pest v4.
Primary Dependencies: Existing TenantConfiguration Coverage v2 services (ResourceTypeRegistry, CoverageSourceContractResolver, GenericContentEvidenceCaptureService, CoverageResourceUpserter, CoverageEvidenceWriter, CoverageIdentityStrategyRegistry, CanonicalIdentityResolver, CoveragePayloadRedactor, ClaimGuard, CoverageV2ReadinessReadModel, Entra comparable/renderable helpers).
Storage: Existing PostgreSQL-backed Coverage v2 tables only. No migration is planned.
Tests: Focused Pest unit/feature tests; browser test only if rendered output changes; PostgreSQL lane only if implementation unexpectedly changes schema/index/JSONB behavior.
Constraints: No restore/apply/certification/customer claims; no new route/navigation/dashboard/action/report/export; no tenant_id; no completed-spec rewrites; no direct HTTP/Graph SDK bypass; no live docs/network calls in render/compare/tests.
Current Repo Evidence
ResourceTypeRegistry::m365RepresentativeDefinitions()seedssecurityDefaultsas an Entra representative row withsource_class = tcm,support_state = out_of_scope,default_coverage_level = detected,default_evidence_state = not_captured,default_claim_state = internal_only,restore_tier = not_restorable, and registry-only metadata.CoverageSourceContractResolver::CONTRACT_KEYSmapsconditionalAccessPolicy,assignmentFilter,notificationMessageTemplate, androleScopeTag, but notsecurityDefaults.config/graph_contracts.phpcontainsconditionalAccessPolicybut notsecurityDefaults.CoverageIdentityStrategyRegistrycontainsconditionalAccessPolicybut notsecurityDefaults.SupportedScopeResolverincludessecurityDefaultsin the Entra planning list, but no internal comparable scope or certified scope is active for this slice.EntraComparablePayloadNormalizer,EntraCoverageComparator, andEntraRenderableSummaryBuildercurrently support Conditional Access only.GenericContentEvidenceCaptureService::responseItems()can handle list responses and single-object payloads with anid, but implementation must prove the Security Defaults source shape works safely before promotion.- Existing capture outcomes do not include
capture_blocked_source_unavailable; use existing outcomes and stable reason codes unless the spec is amended.
Constitution Check
- Inventory/snapshots: PASS. This works over Coverage v2 observed evidence only; no snapshot/backup semantics are changed.
- Read/write separation: PASS. The feature is read/capture/compare/render only; no write or restore behavior.
- Single Graph contract path: REQUIRED. Any source contract must live in the repo graph contract registry and be invoked through
GraphClientInterfacevia existing provider/capture services. - Proportionality: PASS with a bounded exception. One source mapping, one identity strategy, and one typed mapping are justified by a concrete denominator blocker.
- No premature abstraction: PASS if implementation extends existing concrete helpers. Stop if a broad singleton framework or Entra mini-platform appears necessary.
- Workspace/managed-environment/provider ownership: REQUIRED. Existing
workspace_id,managed_environment_id, and same-scopeprovider_connection_idremain the only internal ownership truth. - OperationRun: PASS if existing tenant-configuration capture OperationRun is reused and no new start UX is introduced.
- Product Surface Contract: APPLIES only if rendered Coverage v2 output changes. Focused browser proof and Human Product Sanity are required in that case.
- Test governance: REQUIRED. Focused unit/feature tests must prove source contract, evidence, identity, compare/render, redaction, RBAC, and overclaim boundaries.
Affected Repository Surfaces
Expected runtime surfaces if implementation proceeds:
apps/platform/config/graph_contracts.php
apps/platform/app/Services/TenantConfiguration/ResourceTypeRegistry.php
apps/platform/database/migrations/2026_06_26_000419_expand_tenant_configuration_workloads.php (one-row fresh-install seed alignment for `securityDefaults` only)
apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php
apps/platform/app/Services/TenantConfiguration/SupportedScopeResolver.php
apps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php
apps/platform/app/Services/TenantConfiguration/EntraComparablePayloadNormalizer.php
apps/platform/app/Services/TenantConfiguration/EntraCoverageComparator.php
apps/platform/app/Services/TenantConfiguration/EntraRenderableSummaryBuilder.php
apps/platform/app/Services/TenantConfiguration/CoverageV2ReadinessReadModel.php
apps/platform/app/Services/TenantConfiguration/ClaimGuard.php
apps/platform/app/Services/TenantConfiguration/CoveragePayloadRedactor.php
apps/platform/tests/Unit/Support/TenantConfiguration/
apps/platform/tests/Feature/TenantConfiguration/
apps/platform/tests/Browser/ only if rendered output changes
Potential no-change surfaces:
- No new schema migration expected. Existing migration seed data may be edited only for the already-identified
securityDefaultsrow in2026_06_26_000419_expand_tenant_configuration_workloads.phpso fresh installs matchResourceTypeRegistry::syncDefaults()after real source support is proven. - No Filament Resource/Page/Widget class expected unless rendered output cannot be exposed through the existing read model/data path; amend this plan and tasks before editing runtime UI files.
- No route, navigation, dashboard, customer output, report/export, action, or asset registration expected.
Domain / Model Implications
TenantConfigurationResourceTypecontinues to represent the resource type definition.TenantConfigurationResourcecontinues to represent the observed resource identity within a workspace, managed environment, and provider connection.TenantConfigurationResourceEvidencecontinues to represent append-only content-backed evidence and payload hashes.securityDefaultsshould move out of registry-only/out-of-scope posture only if source contract and capture proof exist.RestoreTier::NotRestorableremains the no-restore posture for this slice.- No new persisted compare result or readiness table is justified.
Data / Truth Flow
- Registry defines
securityDefaultsconservatively. - Source contract resolver either finds an explicit approved contract or returns a blocked missing-contract/unsupported outcome.
- Capture runs through existing provider gateway and
GraphClientInterface; no direct HTTP or render-time provider calls. - Generic capture persists raw and normalized evidence only after successful response handling.
- Canonical identity resolver produces stable/derived/blocked identity state.
- Entra typed normalizer reduces Security Defaults to deterministic enabled-state semantics and diagnostics.
- Comparator classifies enabled-state changes as critical and volatile-only changes as ignored.
- Render summary exposes operator-safe enabled/evidence/identity/claim/blocker fields.
- Claim Guard allows only scoped internal/operator content-backed/comparable/renderable statements and blocks broader claims.
- Supported-scope behavior stays internal/operator-only if touched and never advertises certified, restore-ready, customer-ready, full Entra, or broad M365 scope.
UI / Filament / Livewire Implications
- Livewire v4 compliance remains required; no Livewire v3 API may be introduced.
- Panel providers remain registered in
apps/platform/bootstrap/providers.php; no provider registration change is expected. - No globally searchable Filament Resource is added or changed. Global search posture should remain unchanged.
- No destructive/high-impact action is added. If implementation unexpectedly adds any action, stop and amend the spec first.
- Asset strategy: no new Filament assets expected; no new
filament:assetsrequirement beyond existing deploy practice. - Existing Coverage v2 surface may render data-driven Security Defaults summary if implementation wires the read model. If rendered output changes, browser smoke and Human Product Sanity are required. Blade, Livewire, Filament Resource/Page/Widget, route, navigation, or action edits are outside the current plan until the affected files and proof criteria are added here.
RBAC / Policy Implications
- Non-member or wrong workspace/environment access must remain 404.
- Established member without the required evidence/view capability must receive 403.
- Readonly users must not start tenant-configuration capture.
- Provider connection, OperationRun, resource, and evidence rows must belong to the same workspace and managed environment.
- UI visibility is not authorization; server-side policies/gates/read model scoping must enforce boundaries.
Audit / Observability / OperationRun Implications
- Capture is remote/provider work and must remain OperationRun-backed through existing tenant-configuration capture behavior.
- No new OperationRun type, toast, DB notification, start UX, or terminal notification path is expected.
- OperationRun context and summaries must stay sanitized: numeric summary counts only, no raw payloads, no tokens/secrets, no provider response bodies.
- Render/compare paths must be DB-only and must not trigger provider calls.
Test Strategy
Unit Tests
- Source contract resolver allows Security Defaults only with explicit mapping and blocks missing/unsupported/beta-only source safely.
- Registry defaults are conservative: no restore, no certification, no customer claim, no
tenant_id. - Identity strategy never uses display name alone and blocks unsafe states.
- Normalizer emits deterministic enabled-state representation and drops/demotes volatile fields.
- Comparator detects enabled true/false/null, added/removed, unchanged, ignored volatile, redacted, and unsupported-field cases.
- Render summary hides raw payload/normalized JSON/permission context and exposes only operator-safe fields.
- Claim Guard allows scoped internal/operator content-backed/comparable/renderable wording and blocks certification/restore/customer/full claims.
- Redaction covers tokens, secrets, authorization headers, cookies, raw payload markers, and unsafe diagnostic context.
Feature Tests
- Fake provider response persists content-backed Security Defaults evidence with raw/normalized payloads, payload hash, source metadata, operation run, and same-scope provider connection.
- Missing contract/permission/unsupported source does not create evidence and does not promote coverage levels.
- Promotion to comparable/renderable happens only after content-backed evidence and typed helpers exist.
- RBAC: non-member/wrong scope 404, member without capability 403, readonly cannot start capture, provider connection scope enforced.
- No certification, no restore, no customer claim, no
tenant_id, no route/navigation/dashboard/report/export, no mini-platform. - Render/compare performs no remote/provider/docs calls.
Browser Tests
Required only if rendered output changes. Focused smoke should prove:
- Existing Coverage v2 page loads for an authorized operator.
- Security Defaults state is visible as enabled/disabled/unknown.
- Comparable/renderable state and blockers are readable.
- Raw payload, normalized JSON, secrets, source keys, certified/restore/customer-ready claims are absent.
- No console, Livewire, Filament, network, or 500 errors.
Implementation Phases
Phase 0 - Preflight
Record branch, HEAD, dirty state, activated skills, completed-spec guardrail, current registry row, contract mapping absence, identity strategy absence, typed helper support absence, and no duplicate Spec 424 package.
Phase 1 - Tests First: Contract, Capture, and Identity
Add failing tests for source-contract resolver behavior, graph contract shape, registry promotion, singleton-safe capture, evidence persistence, provider scope, OperationRun scope, and canonical identity.
Phase 2 - Tests First: Typed Semantics and Claim Safety
Add failing tests for normalizer, comparator, render summary, redaction, Claim Guard allowed/blocked wording, no restore/certification/customer claims, no tenant_id, and no mini-platform.
Phase 3 - Source Contract and Registry Closure
Add the minimal approved securityDefaults graph contract and resolver mapping if safe. Update registry defaults only when capture is real; otherwise leave blocked/out-of-scope and document the blocker.
Phase 4 - Capture and Evidence Integration
Ensure existing generic capture handles the Security Defaults source response shape. Add bounded handling only inside existing source/capture machinery if singleton shape requires it. Persist evidence only for real captured payloads.
Phase 5 - Identity, Normalization, Compare, Render
Add Security Defaults identity strategy and typed Entra semantics. Normalize enabled state, compare deterministic changes, and render a concise operator-safe summary.
Phase 6 - Claim Guard, Read Model, Product Surface
Harden Claim Guard. Wire the summary into the existing read model only if rendered output is intended. Keep diagnostics demoted and customer/restore/certification claims blocked. If SupportedScopeResolver is touched, keep any Security Defaults scope internal/operator-only and forbid certified or restore-ready scope names.
Phase 7 - Browser Proof If Rendered
Run focused browser smoke and Human Product Sanity if the existing Coverage v2 surface renders new Security Defaults output. Otherwise record exact no-rendered-change proof.
Phase 8 - Validation and Implementation Report
Run focused tests, formatting, git diff --check, and complete an implementation report with source/capture/identity/compare/render/claim matrices and deployment impact.
Stop Conditions
- Only a beta or undocumented source is available.
- Source contract requires direct HTTP, Graph SDK bypass, runtime docs fetch, or endpoint guessing.
- Existing capture cannot support singleton payload safely without a broader framework.
- Implementation needs a schema migration, a migration-seed edit beyond the one-row
securityDefaultsalignment in2026_06_26_000419_expand_tenant_configuration_workloads.php, a new table, new persisted enum/status family, new route/navigation/dashboard, or customer output. - Restore/apply, certification, customer-ready, Review Pack/report/export, full Entra, 100 percent, or broad M365 claims appear.
- Raw payload, normalized JSON, permission context, tokens, secrets, source keys, or provider response bodies must be default-visible.
tenant_idappears as Coverage v2 ownership truth.- Completed specs 414, 415, 417, 418, 419, 420, 421, or 423 would need to be rewritten.
Rollout / Deployment Considerations
- Migrations: no new schema migration expected. Align only the existing
securityDefaultsfresh-install seed row in2026_06_26_000419_expand_tenant_configuration_workloads.phpif real source support is proven; any other existing migration seed edit or new migration remains a stop condition. - Existing database default sync: after deployment and before Security Defaults capture, run
cd apps/platform && php artisan tenant-configuration:sync-defaultsto synchronize Coverage v2 resource-type and supported-scope defaults and deactivate any stale active Security Defaults TCM planning row. - Environment variables: none expected.
- Queues/workers: existing tenant-configuration capture queue/worker behavior applies.
- Scheduler: no change expected.
- Storage/volumes: no change expected.
- Assets: no new assets expected; no new
filament:assetsrequirement beyond current deploy practice. - Staging/Dokploy: validate capture and rendered existing Coverage v2 surface on staging before any production promotion if this support later participates in certification work.
Complexity Tracking
| Risk | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| Security Defaults source contract mapping | Real content-backed evidence is required before later certification inclusion | Registry-only support would fake evidence |
| Security Defaults typed helper support | Enabled-state compare/render cannot be safely derived from generic raw payload display | Raw JSON display leaks technical payloads and forces manual interpretation |
| Singleton source handling if needed | Security Defaults may be one object rather than a collection | A broad singleton framework would be premature; keep handling bounded inside existing capture path |
Draft-To-Repo Deviation Handling
- Use
not_restorableinstead of draftcompare_only. - Use existing
configurationresource class unless the spec is amended. - Use existing capture outcomes and stable reason codes.
- Treat
securityDefaultsTCM registry row as planning truth only until a real source contract is proven.