TenantAtlas/specs/422-exchange-teams-comparable-renderable-pack/implementation-report.md
ahmido 13d363c8b8 feat: complete spec 422 exchange teams comparable renderable pack (#489)
## Summary

This PR completes spec 422 exchange teams comparable renderable pack with comparable diffing, renderable summary builders, and comprehensive test updates.

## Commit
- 4c1e14c6 feat: complete spec 422 exchange teams comparable renderable pack

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #489
2026-06-30 04:20:13 +00:00

121 lines
16 KiB
Markdown

# Implementation Report: Spec 422 - Exchange & Teams Comparable / Renderable Pack
## Preflight
- **Active spec**: `specs/422-exchange-teams-comparable-renderable-pack/`
- **Branch**: `422-exchange-teams-comparable-renderable-pack`
- **HEAD**: `69d4ecbb feat: complete spec 421 Entra comparable/renderable pack (#488)`
- **Initial dirty state**: untracked active spec directory only.
- **Activated skills**: `spec-kit-implementation-loop`, `pest-testing`, `.agent/workflows/spec-readiness-gate`, `.agent/repo-contracts/workspace-scope-safety`, `.agent/repo-contracts/rbac-action-safety`, `.agent/repo-contracts/evidence-anchor-contract`, `.agent/repo-contracts/product-surface-gate`, `.agent/workflows/filament-livewire-v5-change-loop`.
- **Hard-gate stop conditions checked**: no unrelated dirty files; no completed spec rewrite; no new capture/source contract; no restore/apply/certification/customer output; no new route/navigation/action/table; no OperationRun lifecycle change; no `tenant_id` ownership path; no raw payload/default customer proof; no render-time Graph/TCM/provider/HTTP call.
## Completed-Spec Guardrail
Specs 414, 415, 417, 418, 419, 420, and 421 were used as read-only dependency context. No files under their spec directories were edited.
## Exchange/Teams Evidence Matrix
| Canonical type | Workload | Current repo source truth | Spec 422 result |
| --- | --- | --- | --- |
| `transportRule` | Exchange | Registry-only M365 representative entry; no source contract mapping; capture stays blocked/deferred. | Typed compare/render support for content-backed rows only; live capture deferred. |
| `acceptedDomain` | Exchange | Explicit Spec 420 missing-contract blocker in `CoverageSourceContractResolver`. | Typed compare/render support for content-backed rows only; live capture deferred as missing contract. |
| `remoteDomain` | Exchange | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `organizationConfig` | Exchange | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `sharedMailbox` | Exchange | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `mailboxPlan` | Exchange | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `appPermissionPolicy` | Teams | Explicit Spec 420 missing-contract blocker in `CoverageSourceContractResolver`. | Typed compare/render support for content-backed rows only; live capture deferred as missing contract. |
| `appSetupPolicy` | Teams | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `meetingPolicy` | Teams | Registry-only M365 representative entry; no source contract mapping; capture stays blocked/deferred. | Typed compare/render support for content-backed rows only; live capture deferred. |
| `messagingPolicy` | Teams | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `teamsUpdateManagementPolicy` | Teams | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
| `voiceRoute` | Teams | Registry-only M365 representative entry; no source contract mapping. | Deferred. |
## Implementation Summary
- Added bounded typed Exchange/Teams services under `apps/platform/app/Services/TenantConfiguration/`:
- `ExchangeTeamsComparablePayloadNormalizer`
- `ExchangeTeamsCoverageComparator`
- `ExchangeTeamsRenderableSummaryBuilder`
- Integrated renderable promotion in `CoverageEvidenceWriter` for content-backed typed rows only.
- Integrated typed Exchange/Teams summaries and compare summaries into `CoverageV2ReadinessReadModel`.
- Updated the existing Coverage v2 inspect modal to render generic `summary_fields` while preserving the Entra-specific fallback shape.
- Extended `ClaimGuard` so strictly scoped internal comparable/renderable wording is allowed for selected Exchange and Teams resources, while broad Exchange/Teams/M365 claims remain blocked.
- Hardened transport-rule redaction so content-bearing condition/action keys such as subject/body or attachment content matchers are diagnostics-only and never render values or material compare before/after data.
- Hardened Claim Guard so broad Exchange/Teams/M365 comparable/renderable wording is blocked unless it is explicitly scoped to selected internal/operator review.
- Hardened transport-rule redaction again for header matchers, HTML disclaimer text, subject/header mutation actions, and attachment filename/pattern matchers after review found additional content-bearing keys could still render.
- Hardened Claim Guard again so terse `full`, `complete`, or `all` workload-only Exchange/Teams/M365 claims are blocked instead of downgraded to limited.
- Hardened Claim Guard a third time so plain broad workload coverage/support wording such as `Exchange coverage`, `Teams supported`, or `M365 coverage` is blocked while selected internal comparable/renderable wording and registry-scoped internal denominator wording remain internal-only.
- Productized the shared Coverage v2 resource inspect surface after review: resource tables no longer show canonical keys as default row descriptions; the inspect slide-over shows operator-safe summary/context by default; source class, canonical type/key, evidence hash, source contract/schema, identity reason, and OperationRun links are hidden behind `View technical details`; duplicate claim/identity/capture rows were removed from typed Exchange/Teams summaries; Teams app permission summaries no longer display provider app IDs by default.
- Added focused Pest unit, feature, and browser coverage for normalization, compare, render, redaction, claims, promotion, no-restore/no-certification, scope/provider boundaries, no `tenant_id`, and no mini-platform drift.
## Promoted Types
| Canonical type | Normalizer | Compare | Render | Promotion path |
| --- | --- | --- | --- | --- |
| `transportRule` | display name, enabled/state, priority/order, mode, conditions, actions, exceptions, diagnostics | enabled critical; actions/conditions/exceptions/priority/mode material; volatile ignored | summary fields for display/enabled/priority/mode/conditions/actions/exceptions | Content-backed synthetic/existing evidence rows can become `renderable`. |
| `acceptedDomain` | domain name, domain type, default indicator, state, diagnostics | domain/default critical; type/state material | summary fields for domain/type/default/state | Content-backed synthetic/existing evidence rows can become `renderable`. |
| `appPermissionPolicy` | display name, policy mode, allowed/blocked apps, targets, diagnostics | allowed/blocked apps critical; policy mode/targets material | summary fields for policy mode, apps, targets | Content-backed synthetic/existing evidence rows can become `renderable`. |
| `meetingPolicy` | display name, state, external access, recording/transcription, lobby/admission, content sharing, diagnostics | external/recording critical; lobby/content sharing/state material | summary fields for external/recording/lobby/content sharing | Content-backed synthetic/existing evidence rows can become `renderable`. |
## Deferred Types
`remoteDomain`, `organizationConfig`, `sharedMailbox`, `mailboxPlan`, `appSetupPolicy`, `messagingPolicy`, `teamsUpdateManagementPolicy`, and `voiceRoute` remain deferred because this implementation did not add source contracts or content-backed evidence capture for those types.
## Product Surface Close-Out
- **Runtime UI files changed**: `apps/platform/resources/views/filament/modals/tenant-configuration/coverage-v2-resource-inspect.blade.php`.
- **UI impact decision**: Existing Coverage v2 Technical Annex inspect rendering only if typed summaries are present.
- **Product Surface exceptions**: none.
- **No-legacy posture**: no legacy UI, route, navigation, action, dashboard, or report surface added; existing Coverage v2 page/modal reused.
- **Product Surface Impact**: Technical Annex detail disclosure only. No customer-facing output, restore/certify/export/download flow, or management-report content changed.
- **UI Surface Impact**: Inspect modal can now show typed `summary_fields` for Exchange/Teams renderable evidence; existing Entra rendering remains compatible through fallback fields.
- **Page archetype**: existing read-only registry/report page with read-only inspect slide-over.
- **Surface budgets**: no new page/header/row actions, no new navigation, no new table, no new dashboard, no new asset bundle.
- **Technical Annex / deep-link demotion**: raw technical evidence remains demoted; default modal content exposes operator-safe summary fields, material compare context, resource type, provider connection, capture time, and capture outcome only. Source class, canonical keys, hashes, source contracts, identity reason codes, and OperationRun links require explicit `View technical details` disclosure.
- **Canonical status vocabulary**: reused `detected`, `content_backed`, `comparable`, `renderable`, claim/identity/evidence badge vocabulary; no new persisted status family.
- **Browser proof**: `./vendor/bin/sail artisan test tests/Browser/Spec422ExchangeTeamsComparableRenderableOperatorSurfaceSmokeTest.php` passed, including assertions that technical proof is hidden by default and still available in the disclosure.
- **Human Product Sanity**: passed after Productization fix loop. The 768px screenshot shows a focused operator summary, material changes, and a quiet technical disclosure instead of default-visible hashes, canonical keys, source contracts, or OperationRun links.
- **Visible complexity outcome**: decreased. One existing inspect section gained typed rows only when data exists, while technical proof moved out of the default visual hierarchy.
- **Livewire v4**: unchanged and compliant with current Filament v5 baseline.
- **Provider registration**: unchanged; Laravel panel providers remain in `bootstrap/providers.php`.
- **Global search**: unchanged; no Filament Resource was added or modified.
- **Destructive/high-impact actions**: none added or modified.
- **Asset strategy**: no new assets registered; no new `filament:assets` requirement beyond existing deployment practice.
## Validation
- `./vendor/bin/sail bin pint --dirty --format agent` - passed.
- `./vendor/bin/sail artisan test tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsRedactionTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsClaimGuardTest.php` - failed before the second hardening pass, then passed after the third Claim Guard hardening, 37 tests / 79 assertions.
- `./vendor/bin/sail artisan test tests/Unit/Support/TenantConfiguration/Spec421EntraClaimGuardTest.php tests/Unit/Support/TenantConfiguration/ClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec419M365ClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec420M365CaptureClaimGuardTest.php` - passed after the third Claim Guard hardening, 80 tests / 84 assertions.
- `./vendor/bin/sail artisan test tests/Unit/Support/TenantConfiguration/Spec422ExchangeTransportRuleNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeAcceptedDomainNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec422TeamsAppPermissionPolicyNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec422TeamsMeetingPolicyNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsComparableDiffTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsRenderableSummaryTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsRedactionTest.php tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsClaimGuardTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsCoverageLevelPromotionTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsComparableRenderableTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsNoRestoreNoCertificationTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsNoTenantIdTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsNoMiniPlatformTest.php` - passed after the third Claim Guard hardening, 70 tests / 280 assertions.
- `./vendor/bin/sail artisan test tests/Browser/Spec422ExchangeTeamsComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 1 test / 53 assertions.
- `./vendor/bin/sail artisan test tests/Unit/Support/TenantConfiguration/Spec421EntraClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraComparableDiffTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRenderableSummaryTest.php tests/Feature/TenantConfiguration/Spec421EntraComparableRenderableTest.php tests/Feature/TenantConfiguration/Spec421EntraCoverageLevelPromotionTest.php tests/Feature/Filament/CoverageV2ReadinessPageTest.php` - passed, 28 tests / 193 assertions.
- `./vendor/bin/sail artisan test tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 1 test / 54 assertions.
- Combined Spec 421/422 focused regression run passed after the third Claim Guard hardening, 98 tests / 473 assertions.
- Full smoke lane passed after the third Claim Guard hardening: Spec 422 focused unit/feature, Spec 421 comparable/renderable/readiness regression, and Spec 419/420/shared Claim Guard regression, 136 tests / 515 assertions.
- Manual Claim Guard probe passed: plain broad `Exchange coverage`, `Teams supported`, and `M365 coverage` evaluate to `claim_blocked`; selected internal comparable/renderable and registry-scoped internal denominator wording evaluate to `internal_only`.
- Combined Spec 421/422 browser smoke passed, 2 tests / 107 assertions.
- Productization fix loop validation:
- `./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsRenderableSummaryTest.php tests/Feature/Filament/CoverageV2ReadinessPageTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsComparableRenderableTest.php` - passed, 19 tests / 178 assertions.
- `./vendor/bin/sail artisan test --compact tests/Browser/Spec418CoverageV2OperatorSurfaceSmokeTest.php tests/Browser/Spec420M365GenericEvidenceOperatorSurfaceSmokeTest.php tests/Browser/Spec421EntraComparableRenderableOperatorSurfaceSmokeTest.php tests/Browser/Spec422ExchangeTeamsComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 4 tests / 206 assertions.
- Final focused rerun after duplicate-status pruning: `./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec422ExchangeTeamsRenderableSummaryTest.php tests/Feature/Filament/CoverageV2ReadinessPageTest.php tests/Feature/TenantConfiguration/Spec422ExchangeTeamsComparableRenderableTest.php` - passed, 19 tests / 178 assertions; `./vendor/bin/sail artisan test --compact tests/Browser/Spec422ExchangeTeamsComparableRenderableOperatorSurfaceSmokeTest.php` - passed, 1 test / 64 assertions.
- `./vendor/bin/sail bin pint --dirty --format agent` - passed.
- `git diff --check` - passed.
## Deployment Impact
- No migrations.
- No environment variables.
- No queues/cron changes.
- No storage/volume changes.
- No frontend asset registration or bundling changes.
- Existing deployment command posture remains sufficient; no additional `filament:assets` work is introduced by this spec.
## Residual Risks / Follow-Up Candidates
- Live capture/source contracts for Exchange/Teams remain intentionally deferred.
- Optional Exchange/Teams types remain registry-only until content-backed evidence and explicit tests exist.
- Restore/apply/certification/customer output remain out of scope and blocked from this implementation.
- Content-backed rows from future capture work must continue to pass through redaction before typed render/compare.
- Final dirty state after implementation: active Spec 422 runtime/test/spec changes only; no completed historical spec directories edited.