TenantAtlas/specs/424-security-defaults-content-backed-comparable-support/plan.md
ahmido 2cd512915a feat: complete spec 424 security defaults content-backed comparable support (#491)
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
2026-07-01 14:41:24 +00:00

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() seeds securityDefaults as an Entra representative row with source_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_KEYS maps conditionalAccessPolicy, assignmentFilter, notificationMessageTemplate, and roleScopeTag, but not securityDefaults.
  • config/graph_contracts.php contains conditionalAccessPolicy but not securityDefaults.
  • CoverageIdentityStrategyRegistry contains conditionalAccessPolicy but not securityDefaults.
  • SupportedScopeResolver includes securityDefaults in the Entra planning list, but no internal comparable scope or certified scope is active for this slice.
  • EntraComparablePayloadNormalizer, EntraCoverageComparator, and EntraRenderableSummaryBuilder currently support Conditional Access only.
  • GenericContentEvidenceCaptureService::responseItems() can handle list responses and single-object payloads with an id, 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 GraphClientInterface via 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-scope provider_connection_id remain 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 securityDefaults row in 2026_06_26_000419_expand_tenant_configuration_workloads.php so fresh installs match ResourceTypeRegistry::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

  • TenantConfigurationResourceType continues to represent the resource type definition.
  • TenantConfigurationResource continues to represent the observed resource identity within a workspace, managed environment, and provider connection.
  • TenantConfigurationResourceEvidence continues to represent append-only content-backed evidence and payload hashes.
  • securityDefaults should move out of registry-only/out-of-scope posture only if source contract and capture proof exist.
  • RestoreTier::NotRestorable remains the no-restore posture for this slice.
  • No new persisted compare result or readiness table is justified.

Data / Truth Flow

  1. Registry defines securityDefaults conservatively.
  2. Source contract resolver either finds an explicit approved contract or returns a blocked missing-contract/unsupported outcome.
  3. Capture runs through existing provider gateway and GraphClientInterface; no direct HTTP or render-time provider calls.
  4. Generic capture persists raw and normalized evidence only after successful response handling.
  5. Canonical identity resolver produces stable/derived/blocked identity state.
  6. Entra typed normalizer reduces Security Defaults to deterministic enabled-state semantics and diagnostics.
  7. Comparator classifies enabled-state changes as critical and volatile-only changes as ignored.
  8. Render summary exposes operator-safe enabled/evidence/identity/claim/blocker fields.
  9. Claim Guard allows only scoped internal/operator content-backed/comparable/renderable statements and blocks broader claims.
  10. 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:assets requirement 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 securityDefaults alignment in 2026_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_id appears 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 securityDefaults fresh-install seed row in 2026_06_26_000419_expand_tenant_configuration_workloads.php if 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-defaults to 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:assets requirement 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_restorable instead of draft compare_only.
  • Use existing configuration resource class unless the spec is amended.
  • Use existing capture outcomes and stable reason codes.
  • Treat securityDefaults TCM registry row as planning truth only until a real source contract is proven.