# Implementation Plan: Spec 420 - M365 Generic Evidence Coverage Pack **Branch**: `420-m365-generic-evidence-coverage-pack` | **Date**: 2026-06-27 | **Spec**: `specs/420-m365-generic-evidence-coverage-pack/spec.md` **Input**: Feature specification from `/specs/420-m365-generic-evidence-coverage-pack/spec.md` ## Summary Extend the existing Coverage v2 generic capture path to a bounded M365 first pack. The implementation should enable one explicit contract-backed content capture path for `conditionalAccessPolicy`, prove missing-contract blockers for `acceptedDomain`, `appPermissionPolicy`, and `dlpCompliancePolicy`, and preserve workspace/managed-environment/provider scope, OperationRun lifecycle, canonical identity, redaction, and Claim Guard boundaries. No compare/render/restore/certification/customer output, new UI start action, M365 dashboard, or workload-specific mini-platform is in scope. ## Technical Context **Language/Version**: PHP 8.4.x, Laravel 12.x **Primary Dependencies**: existing TenantConfiguration Coverage v2 models/services/enums, `GraphClientInterface`, provider gateway, `OperationRunService`, Pest 4, PostgreSQL via Sail **Storage**: Existing `tenant_configuration_resources` and `tenant_configuration_resource_evidence` for concrete resource/evidence rows; existing registry tables from Specs 414/419. No new table by default. **Testing**: Pest 4 / PHPUnit 12 via Sail; all provider calls faked. **Validation Lanes**: fast-feedback, confidence, PostgreSQL lane if migrations/check constraints change, focused browser if existing Coverage v2 surface renders new captured/blocked M365 data. **Target Platform**: Laravel Sail locally, Dokploy/container deployment for staging/production. **Project Type**: Laravel monolith under `apps/platform`. **Performance Goals**: no provider call for missing contracts, async capture for enabled contract, deterministic normalization/hash, no render-time Graph calls. **Constraints**: no direct HTTP, no endpoint guessing, no customer claims, no `tenant_id`, no UI start action, no new dashboard/report/download, no workload-specific engines/tables/classes. ## UI / Surface Guardrail Plan - **Guardrail scope**: no runtime UI files, routes, navigation, Filament providers, actions, reports, downloads, or customer output are planned. - **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: existing Spec 418 Coverage v2 operator surface may show data-driven captured/blocked M365 resource/evidence rows. - **No-impact class, if applicable**: not applicable if captured/blocked rows render. Implementation must explicitly choose one close-out path: focused browser/Human Product Sanity when rendered output changes, or `N/A - no rendered UI surface changed` with exact proof when it does not. - **Native vs custom classification summary**: no custom UI. - **Shared-family relevance**: no new UI shared-family path. - **State layers in scope**: backend capture outcomes, evidence rows, identity state, claim state, OperationRun state; existing rendered data only if already queried. - **Audience modes in scope**: internal operator only. - **Decision/diagnostic/raw hierarchy plan**: default product views must not expose raw payloads, provider responses, OperationRun internals, source keys, permission context, identity diagnostics, or customer-proof claims. - **Raw/support gating plan**: raw payload remains in evidence storage only; existing UI must not render it by default. - **One-primary-action / duplicate-truth control**: no new actions. - **Handling modes by drift class or surface**: hard stop if runtime UI code, route, navigation, action, report/download, or customer surface is needed. - **Repository-signal treatment**: no UI audit registry update unless implementation amends scope to runtime UI files. - **Special surface test profiles**: existing technical/evidence operator surface if browser proof is required. - **Required tests or manual smoke**: focused browser proof when existing rendered output changes. - **Exception path and spread control**: none. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. - **UI/Productization coverage decision**: existing internal operator data impact only. - **Coverage artifacts to update**: active Spec 420 artifacts and implementation report only. Do not rewrite Specs 414/415/417/418/419. - **No-impact rationale**: no runtime UI file is planned, but data-driven rendered impact is possible. - **Navigation / Filament provider-panel handling**: no panel/provider registration change. - **Screenshot or page-report need**: focused browser proof screenshot only if existing rendered output changes. ## Product Surface Contract Plan - **Product Surface Contract reference**: `docs/product/standards/product-surface-contract.md`. - **No-legacy posture**: canonical Coverage v2 generic capture extension; no compatibility exception. - **Page archetype and surface budget plan**: existing internal/operator Technical Annex / evidence inspection surface only. - **Technical Annex and deep-link demotion plan**: OperationRun links, raw/normalized payloads, source contract metadata, provider IDs, identity diagnostics, and permission context stay secondary/internal. - **Canonical status vocabulary plan**: use existing Coverage v2 internal state labels and product canonical labels if rendered. No `M365 covered`, `certified`, `restore-ready`, or `customer-ready` wording. - **Product Surface exceptions**: none. - **Browser verification plan**: focused existing-surface proof if captured/blocked M365 data renders. - **Human Product Sanity plan**: required only when rendered output changes. - **Visible complexity outcome target**: neutral; no new surface family. - **Implementation report target**: `specs/420-m365-generic-evidence-coverage-pack/implementation-report.md`. ## Filament / Livewire / Deployment Posture - **Livewire v4 compliance**: Livewire v4.x remains required. No Livewire code is planned. - **Panel provider registration location**: Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`; no panel/provider change is planned. - **Global search posture**: no Filament Resource changes. If a Resource is unexpectedly added, stop and amend the spec. - **Destructive/high-impact action posture**: no UI action. Backend capture start remains high-impact and must be server-authorized, audited, queued, and OperationRun-backed through existing service paths. - **Asset strategy**: no assets. `filament:assets` not required unless scope is amended to register assets. - **Testing plan**: focused unit and feature tests; focused browser only if existing rendered Coverage v2 output changes. - **Deployment impact**: queue worker required for capture job; possible config/code only by default; migrations only if implementation proves schema/check constraints need updates. No env vars, scheduler, storage, or assets expected. ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes at evidence/operation/provider-contract level; no new UI interaction family. - **Systems touched**: `CoverageSourceContractResolver`, `CoverageSourceContractDecision`, `GenericContentEvidenceCaptureService`, `CoverageResourceUpserter`, `CoverageEvidenceWriter`, `GenericPayloadNormalizer`, `CoveragePayloadRedactor`, `CoverageCaptureOutcomeSummarizer`, `CoverageIdentityStrategyRegistry`, `CanonicalIdentityResolver`, `ClaimGuard`, `StartTenantConfigurationCapture`, `CaptureTenantConfigurationEvidenceJob`, `OperationRunService`, and existing tests. - **Shared abstractions reused**: existing Coverage v2 registry, source resolver, capture service, identity registry, evidence writer, Claim Guard, OperationRun lifecycle, provider gateway. - **New abstraction introduced? why?**: none by default. A small local mapping in existing resolver/identity registry is preferred over new M365-specific classes. - **Why the existing abstraction was sufficient or insufficient**: The existing generic capture stack already handles the hard parts; it lacks selected M365 contract/identity mappings. - **Bounded deviation / spread control**: no `EntraEvidenceEngine`, `ExchangeEvidenceEngine`, `TeamsEvidenceEngine`, `SecurityComplianceEvidenceEngine`, new dashboard, or separate table family. ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: backend lifecycle yes; no new start/link UI. - **Central contract reused**: existing `OperationRunService`, `OperationRunType::TenantConfigurationCapture`, `CaptureTenantConfigurationEvidenceJob`, and terminal notification lifecycle. - **Delegated UX behaviors**: no new toast/link/browser event. Existing diagnostic links remain secondary and authorized if rendered. - **Surface-owned behavior kept local**: none. - **Queued DB-notification policy**: no new queued DB notification opt-in. - **Terminal notification path**: central lifecycle mechanism. - **Exception path**: none. Do not add `tenant_configuration.m365_capture` unless a distinct lifecycle/operator consequence is proven. ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: yes. - **Provider-owned seams**: Microsoft Graph/TCM source names, source contract keys, endpoint paths, source aliases, permission context, provider IDs in metadata. - **Platform-core seams**: resource/evidence persistence, capture outcomes, coverage/evidence/identity/claim states, OperationRun, RBAC, redaction. - **Neutral platform terms / contracts preserved**: provider connection, managed environment, resource type, source contract, evidence, identity, claim, operation. - **Retained provider-specific semantics and why**: selected M365 canonical type names and Graph contract keys are necessary provider-owned source metadata for this M365 pack. - **Bounded extraction or follow-up path**: document-in-feature for source/identity mapping; follow-up-spec for compare/render/certification/customer output. ## Constitution Check - Inventory/evidence truth: PASS. Real evidence rows are created only for real payload capture; missing contracts do not create fake evidence. - Read/write separation: PASS. Capture is read-only provider work and queued; no restore/apply/write to Microsoft. - Graph contract path: PASS if implementation uses explicit `GraphClientInterface`/provider gateway contracts only. - Deterministic capabilities: PASS. Capture eligibility and claim behavior are testable. - RBAC-UX: PASS with required 404/403 semantics and readonly denial. - Workspace isolation: PASS with same-scope workspace/managed-environment/provider checks before run creation and job work. - OperationRun: PASS with existing `tenant_configuration.capture` and service-owned lifecycle. - Evidence/currentness: PASS. Evidence payload truth is distinct from OperationRun execution truth. - Customer output: PASS. No customer output or customer-safe claim. - Provider boundary: PASS if provider-native IDs remain metadata only. - Product Surface: PASS with existing-surface data-impact proof if rendered. - Test governance: PASS. Unit/Feature/Browser-if-rendered lanes are named. - Proportionality: PASS. No new tables/status families/frameworks by default; extension is bounded to selected resource types. - No premature abstraction: PASS if existing services are extended. - Persisted truth: PASS. Existing durable resource/evidence tables are product truth for observed configuration. - Behavioral state: PASS using existing outcome/state families. - No legacy / pre-production lean: PASS. No compatibility path, v1 adapter, fallback reader, dual write, or `tenant_id`. ## Test Governance Check - **Test purpose / classification by changed surface**: Unit for pure resolver/identity/redaction/claim behavior; Feature for persistence, authorization, OperationRun, provider scope, no-overclaim/no-legacy; Browser if existing UI renders new data. - **Affected validation lanes**: fast-feedback, confidence, PostgreSQL if schema changes, browser if rendered. - **Why this lane mix is the narrowest sufficient proof**: Runtime behavior is service/job/evidence based; browser is only required for actual rendered output. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec420M365CaptureSourceContractResolverTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureEligibilityTest.php tests/Unit/Support/TenantConfiguration/Spec420M365GenericPayloadNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureIdentityStrategyTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureRedactionTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php tests/Feature/TenantConfiguration/Spec420M365CaptureOperationRunTest.php tests/Feature/TenantConfiguration/Spec420M365CaptureAuthorizationTest.php tests/Feature/TenantConfiguration/Spec420M365ProviderConnectionScopeTest.php tests/Feature/TenantConfiguration/Spec420M365NoOverclaimTest.php tests/Feature/TenantConfiguration/Spec420M365NoLegacyTest.php tests/Feature/TenantConfiguration/Spec420M365NoTenantIdTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php` if existing rendered output changes - `git diff --check` - **Fixture / helper / factory / seed / context cost risks**: keep fake M365 provider responses local to Spec 420 tests. - **Expensive defaults or shared helper growth introduced?**: none expected. - **Heavy-family additions, promotions, or visibility changes**: no heavy-governance family; focused browser only when rendered. - **Surface-class relief / special coverage rule**: no UI code change; browser may be N/A with proof. - **Closing validation and reviewer handoff**: implementation report records matrices, tests, no-claim/no-leak/no-scope proof, browser/N/A proof, deployment impact. - **Budget / baseline / trend follow-up**: none expected. - **Review-stop questions**: endpoint guessing, raw leak, provider scope, identity stability, broad claim, no UI scope, no historical-spec rewrite. - **Escalation path**: document-in-feature for contained mapping choices; follow-up-spec for broad packs. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. - **Why no dedicated follow-up spec is needed**: this is the narrow first M365 generic evidence slice; later semantic packs are listed separately. ## Project Structure ### Documentation (this feature) ```text specs/420-m365-generic-evidence-coverage-pack/ +-- spec.md +-- plan.md +-- tasks.md +-- checklists/ +-- requirements.md ``` ### Source Code (likely affected in later implementation) ```text apps/platform/app/ +-- Services/TenantConfiguration/ | +-- CoverageSourceContractResolver.php | +-- GenericContentEvidenceCaptureService.php | +-- CoverageCaptureOutcomeSummarizer.php | +-- CoverageResourceUpserter.php | +-- CoverageEvidenceWriter.php | +-- CoverageIdentityStrategyRegistry.php | +-- GenericPayloadNormalizer.php | +-- CoveragePayloadRedactor.php | +-- ClaimGuard.php +-- Jobs/TenantConfiguration/ | +-- CaptureTenantConfigurationEvidenceJob.php +-- Support/ +-- OperationRunType.php only if a distinct operation type is approved apps/platform/config/ +-- graph_contracts.php only if selected source contracts need narrow metadata adjustment apps/platform/tests/ +-- Unit/Support/TenantConfiguration/ +-- Feature/TenantConfiguration/ +-- Browser/ only if existing rendered output changes ``` **Structure Decision**: Reuse existing Coverage v2 generic services and tests. Do not add workload-specific service namespaces, tables, dashboards, routes, or providers. ## Implementation Phases ### Phase 0 - Preflight Capture branch, HEAD, dirty state, activated skills, related spec guardrail, and stop conditions. Confirm no unrelated dirty files before implementation. Re-read current resolver, registry, identity, claim, OperationRun, and Graph contract truth. ### Phase 1 - Tests First: Source Contracts And Eligibility Add tests proving `conditionalAccessPolicy` resolves through an explicit repo-real contract and selected missing-contract resource types block safely without provider calls or evidence rows. Include explicit retry/idempotency and duplicate active-run/resource-row protection. ### Phase 2 - Tests First: Identity, Redaction, And Claims Add tests for selected M365 identity strategies, no display-name-only stable identity, deterministic normalization/hash, redaction, and broad M365 claim blocking. ### Phase 3 - Tests First: Persistence, OperationRun, RBAC, And Scope Add feature tests for fake-provider capture persistence, append-only evidence, retry/idempotency, stale active-run/deduplication behavior, bounded duplicate resource/evidence behavior, same-scope provider connection enforcement, OperationRun lifecycle, authorization 404/403 behavior, readonly denial, no `tenant_id`, no legacy, and no mini-platform. ### Phase 4 - Source Contract And Eligibility Implementation Extend `CoverageSourceContractResolver` narrowly for the selected first pack. Use existing `graph_contracts.php` contract metadata for `conditionalAccessPolicy` if valid; if it is not valid, stop and amend the package. Leave `acceptedDomain`, `appPermissionPolicy`, and `dlpCompliancePolicy` as missing-contract blockers for Spec 420. ### Phase 5 - Identity And Evidence Implementation Add/confirm identity strategies, source metadata handling, evidence writing, normalization/redaction, and claim-state preservation for selected M365 resource types. ### Phase 6 - OperationRun, Authorization, And Guardrails Reuse `StartTenantConfigurationCapture`, `CaptureTenantConfigurationEvidenceJob`, `OperationRunService`, audit, and queued execution legitimacy paths. Add focused guards for no direct status writes, no raw payload context, no direct Graph calls, and no UI/customer output scope. ### Phase 7 - Product Surface Data-Impact Verification Confirm no UI route, page, navigation, provider, action, report, download, or customer output changed. If existing Coverage v2 surface renders captured/blocked M365 rows, run focused browser proof and Human Product Sanity. If runtime UI code changes are needed, stop and amend artifacts. ### Phase 8 - Validation And Implementation Report Run Pint dirty, focused unit/feature tests, PostgreSQL lane if required, browser if rendered, and `git diff --check`. Complete implementation report with matrices and required proof. ## Rollout And Deployment Considerations - Migrations: not expected; if added, validate on staging before production. - Queue workers: required for capture job processing. - Scheduler: no new scheduled job. - Environment variables: none expected. - Storage/volumes: no change. - Assets: no change; `filament:assets` not required. - Staging: validate fake/provider-safe tests and any rendered existing-surface proof before production promotion. ## Risk Controls - Stop if implementation requires endpoint guessing or direct HTTP. - Stop if implementation requires a new UI start action, route, dashboard, report, restore/certify/export/download action, or customer output. - Stop if `tenant_id` appears as Coverage v2 ownership truth. - Stop if a new operation type, enum/status family, table, or abstraction is introduced without proportionality review. - Stop if raw payloads, credentials, tokens, or provider response bodies enter OperationRun/audit/log/default UI. - Stop if a broad M365/certified/restore-ready/customer-ready claim must be allowed.