TenantAtlas/specs/420-m365-generic-evidence-coverage-pack/plan.md
ahmido a73a8f5882 feat: complete m365 generic evidence coverage pack (#487)
Committing and publishing the current Spec 420 package changes.

Includes updated services, coverage tests, browser smoke coverage, and the spec/plan/tasks artifacts for the package.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #487
2026-06-27 12:24:00 +00:00

238 lines
19 KiB
Markdown

# 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.