# Implementation Report: Spec 426 - Exchange / Teams Core Evidence & Stable Identity Readiness **Branch**: `426-exchange-teams-core-evidence-identity-readiness` **HEAD**: `33e496c1 feat: complete spec 425 enta certified compare pack (#492)` **Implementation date**: 2026-07-02 **Result**: PASS WITH CONDITIONS for Spec 426 closure; FAIL-safe for source-backed evidence readiness ## Gate Result - Activated skills/gates: `pest-testing`, Spec Readiness Gate, provider-freshness semantics, and TCM cutover guard for the post-review fix. Earlier implementation work also used workspace/RBAC/OperationRun/evidence/customer-output/Product Surface gates. - Closure gate: `PASS WITH CONDITIONS`. - Condition: Spec 426 proves fail-safe behavior for Exchange/Teams source-backed evidence. It does not prove readiness, capture support, compare support, render support, or certification. Typed normalizer/hash tests are future-contract helper proof only. - Source-backed evidence readiness gate: FAIL-safe/blocked because no verified production-safe source contract exists for the four mandatory types. - Completed dependency specs 414, 415, 417, 418, 419, 420, 422, and 425 remain read-only context. No completed spec artifacts were rewritten. - Review correction: removed unverified Graph v1.0 endpoint claims for `mailFlowRule`, `acceptedDomains`, `teamsAppPermissionPolicy`, and `teamsMeetingPolicy`. - Final dirty state: runtime service/config/test changes plus active Spec 426 artifacts, as expected for this active spec. ## Files Changed - Source contracts and capture: - `apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php` - `apps/platform/app/Services/TenantConfiguration/GenericContentEvidenceCaptureService.php` - `apps/platform/config/graph_contracts.php` was verified to contain no unverified final Spec 426 contract entries; it has no final diff in this correction. - Stable identity: - `apps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php` - Tests: - Focused Spec 426 unit and feature tests under `apps/platform/tests/Unit/Support/TenantConfiguration/` and `apps/platform/tests/Feature/TenantConfiguration/` - Updated Spec 417/420 expectations so the four Exchange/Teams types remain fail-closed until verified contracts exist - Spec artifacts: - `spec.md`, `plan.md`, `tasks.md`, this implementation report ## Source Contract Matrix | Type | Contract key | Endpoint | Source class | Outcome | |---|---|---|---|---| | `transportRule` | none | none | none | `capture_blocked_missing_contract` | | `acceptedDomain` | none | none | none | `capture_blocked_missing_contract` | | `appPermissionPolicy` | none | none | none | `capture_blocked_missing_contract` | | `meetingPolicy` | none | none | none | `capture_blocked_missing_contract` | No endpoint guessing, direct HTTP, runtime documentation lookup, provider bypass, or guessed Microsoft Graph source contract remains. Capture attempts stop before `ProviderGateway` / `GraphClientInterface` calls for these four types. ## Evidence Matrix | Type | Capture outcome | Provider call | Resource row | Evidence row | Empty/fake behavior | |---|---|---:|---:|---:|---| | `transportRule` | `capture_blocked_missing_contract` | no | no | no | no fake resource/evidence | | `acceptedDomain` | `capture_blocked_missing_contract` | no | no | no | no fake resource/evidence | | `appPermissionPolicy` | `capture_blocked_missing_contract` | no | no | no | no fake resource/evidence | | `meetingPolicy` | `capture_blocked_missing_contract` | no | no | no | no fake resource/evidence | OperationRun summaries remain flat numeric counts. Blocked runs record sanitized blocked outcomes only; no raw payload, provider response body, secret, mail content, Teams content, or raw permission context is placed in run context or audit metadata. ## Identity Matrix | Type | Strategy | Stable identity inputs | Explicitly rejected | |---|---|---|---| | `transportRule` | `tcm.exchange.transport_rule.v1` | `id`, `sourceId`, `Guid`, `RuleId` | `Identity`, display/name fields, order, payload hash | | `acceptedDomain` | `tcm.exchange.accepted_domain.v1` | `id`, `sourceId`, `DomainName`, `domainName` | `Identity`, display/name fields, domain type/default flag | | `appPermissionPolicy` | `tcm.teams.app_permission_policy.v1` | `id`, `sourceId`, `policyId` | `Identity`, display/name fields, settings/app hash | | `meetingPolicy` | `tcm.teams.meeting_policy.v1` | `id`, `sourceId`, `policyId` | `Identity`, display/name fields, settings hash | `CanonicalIdentityResolver` remains the only identity path. `Identity`-only payloads resolve to `missing_external_id`, not stable identity. ## Readiness Matrix | Type | `content_backed` | `identity_strategy_hardened` | `compare_render_ready` | `certified` | `restore_ready` | `customer_claimable` | |---|---:|---:|---:|---:|---:|---:| | `transportRule` | no | yes | no | no | no | no | | `acceptedDomain` | no | yes | no | no | no | no | | `appPermissionPolicy` | no | yes | no | no | no | no | | `meetingPolicy` | no | yes | no | no | no | no | Typed normalizer/hash/redaction tests remain as helper proof for future valid source payloads. They are not used to claim source-backed evidence or certification readiness. Post-review coverage now also proves that typed helper output keeps stable source identity fields (`Guid`, `RuleId`, `DomainName`, `policyId`, `sourceId`), that the future typed capture handoff preserves that type-specific `source_identity`, that contract-level volatile fields are excluded from typed unsupported diagnostics/material hashes, and that payload hashes ignore volatile-field diagnostics. ## Claim Guard And Redaction - Certified, restore-ready, full Exchange/Teams/Microsoft 365, and customer-ready claims remain blocked. - Internal compare/render wording remains bounded by Claim Guard, but this report does not claim source-backed compare/render readiness for the four mandatory types. - Redaction tests cover provider secrets, tokens, raw provider markers, mail subject/body-like fields, and Teams recording/transcript-like fields in normalized/helper output. ## Product Surface / Filament Contract - No runtime UI files, routes, navigation, Filament resources/pages/widgets, actions, reports, exports, or customer outputs were changed. - Product Surface decision: `N/A - no rendered UI surface changed`. - Product Surface exceptions: none. - Browser proof: `N/A - no rendered UI surface changed`. - Human Product Sanity: `N/A - no rendered UI surface changed`. - Livewire v4 compliance: unchanged; Filament v5 remains on Livewire v4. - Provider registration: unchanged; Laravel provider registration remains in `apps/platform/bootstrap/providers.php`. - Global search: unchanged; no resource/global search behavior was added. - Destructive/high-impact actions: none added. - Asset strategy: no new assets; no new `filament:assets` deployment requirement. ## Deployment Impact - Migrations: none. - Environment variables: none. - Queues/cron: no new workers or schedules; existing OperationRun-backed capture job reused. - Storage/volumes: none. - Assets: none. - Runtime config: no new Exchange/Teams graph contract entries remain; deploy through the normal container/config-cache path. - Staging/Production: do not promote Exchange/Teams certification from this branch. Next sequence is Spec 427 verified source contract enablement, Spec 428 content-backed evidence promotion, Spec 429 compare/render promotion, and only later certification. A future verified provider contract must pass staging before evidence promotion or certification proceeds. ## Validation Passed after post-review fix and closure review: - `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec426` - terminated with Signal 9 in the container runner. - Not a closure blocker because the equivalent direct file runs below passed. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsSourceContractResolverTest.php tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsCanonicalIdentityTest.php` - 22 passed, 124 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsPayloadNormalizationFromSourceTest.php tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsEvidenceHashTest.php tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsClaimGuardReadinessTest.php tests/Unit/Support/TenantConfiguration/Spec426ExchangeTeamsRedactionTest.php` - 24 passed, 77 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec426ExchangeTeamsCoreEvidenceReadinessTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsStableIdentityReadinessTest.php` - 3 passed, 69 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec426ExchangeTeamsProviderScopeTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsNoCertificationTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsNoRestoreTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsNoTenantIdTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsNoMiniPlatformTest.php tests/Feature/TenantConfiguration/Spec426ExchangeTeamsClaimGuardFeatureTest.php` - 7 passed, 55 assertions. - Direct file Spec 426 total: - 56 passed, 325 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php tests/Feature/TenantConfiguration/Spec420M365CaptureOperationRunTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureSourceContractResolverTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureEligibilityTest.php tests/Unit/Support/TenantConfiguration/Spec417CoverageIdentityStrategyRegistryTest.php` - 16 passed, 259 assertions. - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php` - 1 passed, 44 assertions. - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - passed. - `git diff --check` - passed. Spec 426 browser validation remains `N/A - no rendered UI surface changed`; the Spec 420 browser regression fixture changed and passed.