TenantAtlas/specs/420-m365-generic-evidence-coverage-pack/implementation-report.md
Ahmed Darrazi 9405058433
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 5m46s
feat: complete m365 generic evidence coverage pack
2026-06-27 13:00:22 +02:00

16 KiB

Implementation Report: Spec 420 - M365 Generic Evidence Coverage Pack

Status

  • Result: passed implementation loop; ready for manual review.
  • Active spec directory: specs/420-m365-generic-evidence-coverage-pack.
  • Branch: 420-m365-generic-evidence-coverage-pack.
  • HEAD at implementation start/final validation: 52523980 feat: expand m365 tcm workload registry (#486).
  • Initial dirty state: active spec directory was untracked; no unrelated modified runtime files were present.
  • Final dirty state: expected active-spec, runtime, and test changes only. No unrelated work was modified.
  • Historical specs: Specs 414, 415, 417, 418, and 419 were used as read-only dependency context only. No closed historical spec was rewritten or stripped of validation history.

Activated Skills And Gates

  • Activated skills: spec-kit-implementation-loop, pest-testing; repo gates applied for spec readiness, workspace scope safety, RBAC/action safety, operation-run truth, evidence anchor contract, Product Surface, TCM cutover guard, customer output, and browser read-only audit.
  • Hard-gate result: pass with conditions before code, then pass after validation.
  • Stop conditions: none hit. No compare/render/restore/certification/customer output, no UI start action, no dashboard, no endpoint guessing, no direct HTTP, no tenant_id, no v1 compatibility adapter, and no M365 mini-platform were introduced.
  • Analysis/fix iterations: two implementation loops; one browser assertion correction because the existing safe inspect modal intentionally does not render source endpoint, followed by a manual-review fix loop for redaction proof, RBAC role precision, and OperationRun browser-fixture summary keys. Final post-implementation analysis found no confirmed in-scope findings.
  • Merge Readiness Gate: passed, subject to human review.

Changed Files

  • Runtime:
    • apps/platform/app/Services/TenantConfiguration/GenericContentEvidenceCaptureService.php
    • apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php
    • apps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php
    • apps/platform/app/Services/TenantConfiguration/CanonicalIdentityResolver.php
  • Existing test update:
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec417CoverageIdentityStrategyRegistryTest.php
  • New Spec 420 tests:
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureSourceContractResolverTest.php
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureEligibilityTest.php
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365GenericPayloadNormalizerTest.php
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureIdentityStrategyTest.php
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureClaimGuardTest.php
    • apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureRedactionTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365CaptureOperationRunTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365CaptureAuthorizationTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365ProviderConnectionScopeTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365NoOverclaimTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365NoLegacyTest.php
    • apps/platform/tests/Feature/TenantConfiguration/Spec420M365NoTenantIdTest.php
    • apps/platform/tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php
  • Spec artifacts:
    • specs/420-m365-generic-evidence-coverage-pack/tasks.md
    • specs/420-m365-generic-evidence-coverage-pack/implementation-report.md

Implementation Summary

  • conditionalAccessPolicy now resolves through an explicit graph_contracts.types.conditionalAccessPolicy mapping in the existing generic capture resolver.
  • The selected missing-contract types acceptedDomain, appPermissionPolicy, and dlpCompliancePolicy fail closed as capture_blocked_missing_contract with missing_source_contract_mapping.
  • Registry truth from Spec 419 is preserved: selected M365 types remain registry-seeded as source_class = tcm, support_state = out_of_scope, and default_claim_state = internal_only; runtime capture eligibility is narrow and contract-driven.
  • Source metadata now carries actual source contract key, endpoint, source version, source schema hash, registry source class, registry support state, and schema-hash availability where captured.
  • conditionalAccessPolicy uses a narrow identity strategy with Graph object ID stable keys. CanonicalIdentityResolver remains the only resolver path and now honors a strategy-level stable_key_kind before falling back to source-class defaults.
  • Existing CoverageResourceUpserter, CoverageEvidenceWriter, StartTenantConfigurationCapture, CaptureTenantConfigurationEvidenceJob, and OperationRunService are reused. GenericContentEvidenceCaptureService remains the capture path and now redacts normalized payloads before persistence/hashing while preserving the raw-payload evidence boundary.

Capture Eligibility Matrix

Canonical type Runtime result Provider called Evidence row Notes
conditionalAccessPolicy captured when fake Graph payload exists yes, through GraphClientInterface::listPolicies() yes Explicit repo-real source contract only; no alias or endpoint guessing.
acceptedDomain capture_blocked_missing_contract no no Stable reason missing_source_contract_mapping.
appPermissionPolicy capture_blocked_missing_contract no no Stable reason missing_source_contract_mapping.
dlpCompliancePolicy capture_blocked_missing_contract no no Stable reason missing_source_contract_mapping; source aliases are ignored at runtime.

Source Contract Matrix

Canonical type Contract key Endpoint Version/schema Outcome
conditionalAccessPolicy conditionalAccessPolicy /identity/conditionalAccess/policies v1.0; schema hash captured when available, explicit unavailable state tested Capture enabled.
acceptedDomain none none none Missing contract blocker.
appPermissionPolicy none none none Missing contract blocker.
dlpCompliancePolicy none none none Missing contract blocker.

Evidence And OperationRun Proof

  • Captured Conditional Access evidence persists one TenantConfigurationResource and one append-only TenantConfigurationResourceEvidence row with raw payload, normalized payload, deterministic payload hash, redacted permission context, source metadata, and operation_run_id.
  • Missing-contract selected types create no fake resources or evidence rows.
  • OperationRun type remains tenant_configuration.capture; no tenant_configuration.m365_capture type was introduced.
  • OperationRun lifecycle transitions remain service-owned through OperationRunService.
  • Summary counts use existing whitelisted numeric keys.
  • Dedupe/retry behavior reuses the active-run check in StartTenantConfigurationCapture.
  • OperationRun has no separate message column in the current model; OperationRun context and failure summary do not contain raw payloads, provider response bodies, tokens, or secrets.

RBAC And Scope Proof

  • Authorized owner and manager paths can start the selected capture.
  • Operator, readonly, and missing-capability paths are denied with 403 after membership is established.
  • Non-member and missing environment entitlement paths are denied without leaking environment data.
  • Cross-workspace/cross-environment provider connections are rejected before run creation.
  • Job execution revalidates workspace, managed environment, provider connection, OperationRun type, and target scope before provider work.

Redaction, Claims, And Safety Proof

  • Secret-bearing provider payload tests prove raw payload remains inside the evidence raw-payload storage boundary, while normalized evidence payload, permission context, OperationRun context/failure summary, audit metadata, and logs do not persist or emit the secret values.
  • Broad M365, certified, restore-ready, customer-ready, complete-tenant, all-resource, and unscoped 100% claims remain blocked.
  • Captured Conditional Access evidence remains internal_only by default.
  • No customer output, report, export, publish, restore, certify, or Review Pack path was added.

No-Legacy And Data Model Proof

  • No v1 adapter, fallback reader, dual-write path, old gap taxonomy, workload-specific table/model/engine/namespace/service, or mini-platform was introduced.
  • No tenant_id ownership truth was added to Coverage v2 tables or changed capture paths.
  • No migrations, constraints, indexes, enums/status families, operation types, tables, or persisted taxonomies were added.

Product Surface Close-Out

  • No-legacy posture: no legacy UI/API compatibility path and no approved exception.
  • Product Surface Impact: data-impact only on the existing Coverage v2 Readiness surface when a captured M365 generic evidence row exists.
  • UI Surface Impact: no runtime UI files, routes, navigation entries, Filament resources/widgets/pages/actions, Blade views, Livewire components, labels, customer outputs, dashboards, or downloads were edited.
  • Page archetype: existing internal operator readiness/review page.
  • Surface budgets: unchanged; one existing table row and existing inspect slide-over render seeded data.
  • Technical Annex/deep-link demotion: unchanged; source endpoint is intentionally not rendered in the safe inspect modal.
  • Canonical status vocabulary: existing Coverage v2 labels only (Content backed, Stable, Internal only, existing capture outcome/status values).
  • Product Surface exceptions: none.
  • Human Product Sanity: pass. Existing page renders the Conditional Access row as internal generic evidence, shows no broad M365/certified/restore-ready/customer-ready claim, does not expose payload secrets, and adds no new high-impact UI action.
  • Visible complexity outcome: unchanged; no new surface controls or navigation.
  • Browser proof: Spec420M365GenericEvidenceOperatorSurfaceSmokeTest passed. It loads CoverageV2Readiness, verifies the row and inspect slide-over, checks Livewire availability, checks no Graph/TCM/provider network calls during render, checks no console/JS errors, and checks no new capture/restore/certify/export/download action on the main surface.

Filament v5 Output Contract

  • Livewire v4.0+ compliance: no Livewire API changes; existing Filament v5/Livewire v4 surface is exercised by the browser test.
  • Provider registration location: no panel/provider registration changed. Laravel 12 provider registration remains under apps/platform/bootstrap/providers.php.
  • Global search: no Filament Resource global-search behavior changed; no new Resource was added.
  • Destructive/high-impact actions: no new Filament action or UI start action was added. Backend capture start remains capability-gated and OperationRun/audit backed; there is no new destructive action requiring a Filament confirmation modal.
  • Asset strategy: no assets registered and no frontend bundles changed. php artisan filament:assets is not newly required by this implementation beyond existing deployment practice.
  • Testing plan: page/data impact covered by browser smoke; resolver/capture/evidence/identity/RBAC/scope/OperationRun/no-overclaim/no-legacy/no-tenant-id covered by Pest unit and feature tests.

Validation Commands

  • 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/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 - passed in latest combined focused run.
  • 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 - passed in latest combined focused run.
  • 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 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 - 35 passed, 215 assertions.
  • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php - 1 passed, 43 assertions.
  • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec415CoverageSourceContractResolverTest.php tests/Unit/Support/TenantConfiguration/Spec417CanonicalIdentityResolverTest.php tests/Unit/Support/TenantConfiguration/Spec417CoverageIdentityStrategyRegistryTest.php tests/Unit/Support/TenantConfiguration/Spec419M365WorkloadRegistryTest.php tests/Feature/TenantConfiguration/Spec415GenericContentBackedCaptureTest.php tests/Feature/TenantConfiguration/Spec417CanonicalIdentityPersistenceTest.php tests/Feature/TenantConfiguration/Spec417CoverageResourceIdentityUpsertTest.php tests/Feature/TenantConfiguration/Spec419M365RegistryExpansionTest.php - 31 passed, 2 skipped, 975 assertions.
  • git diff --check - passed.

Deployment Impact

  • Migrations: none.
  • Environment variables: none.
  • Queue/cron: existing tenant-configuration capture queue path still applies; no new worker type.
  • Storage/volumes: none.
  • Assets: none.
  • Dokploy/staging: deploy as ordinary app code/test change; validate on Staging before Production per repo release rules. No additional rollback step beyond reverting code.
  • PostgreSQL lane: N/A because no schema/check constraint/index changed.

Deferred Work And Residual Risk

  • Deferred by spec: explicit source contracts for acceptedDomain, appPermissionPolicy, and dlpCompliancePolicy; compare/render/restore/certification/customer output; broader M365 dashboard or support claims.
  • Residual risk: this proves a fake-provider generic capture path for the selected first pack, not real Microsoft Graph production coverage for all M365 resources.
  • Remaining in-scope findings: none.