# Implementation Report: Spec 430 - Exchange PowerShell Adapter Contract Slice 1 ## Preflight - **Active spec**: `specs/430-exchange-powershell-adapter-contract-slice-1/` - **Branch**: `430-exchange-powershell-adapter-contract-slice-1` - **HEAD before implementation**: `0e2cea30 spec: add Exchange Teams source-surface catalog adapter strategy (#496)` - **Initial dirty state**: untracked active spec package only: `specs/430-exchange-powershell-adapter-contract-slice-1/` - **Activated skills/gates**: - `spec-kit-implementation-loop`: requested implementation loop. - `.agent/workflows/spec-readiness-gate`: active Spec Kit implementation readiness check. - `.agent/repo-contracts/workspace-scope-safety`: provider-connection and future execution handoff scope proof. - `.agent/repo-contracts/provider-freshness-semantics`: provider permission/source-boundary metadata proof. - `.agent/temporary-migrations/tcm-cutover-guard`: Coverage v2/source-contract no-promotion guard. - `pest-testing`: focused Pest 4 test implementation and validation. - **Hard-gate stop conditions checked**: no unrelated dirty files; no completed spec rewrite; no live provider execution; no evidence rows; no OperationRun creation; no UI/routes/navigation/Filament/Livewire changes; no migrations; no `tenant_id`; no fallback readers, legacy shims, dual writes, customer output, restore/certification/customer claim, or Exchange mini-platform required. ## Repo Truth And Path Decisions - **Spec 429 source path**: `specs/429-exchange-teams-source-surface-catalog-adapter-strategy/implementation-report.md` identifies `new_exchange_powershell_adapter` as the recommended first Spec 430 adapter path and lists `transportRule`, `remoteDomain`, and `inboundConnector` as the first Exchange PowerShell slice candidates. - **Runtime support path**: use the existing TenantConfiguration/Coverage v2 service path under `apps/platform/app/Services/TenantConfiguration/`. - **Command contract support file**: `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCommandContracts.php`. - **Resolver integration**: `apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php`. - **Registry metadata updates**: `apps/platform/app/Services/TenantConfiguration/ResourceTypeRegistry.php`, `SupportedScopeResolver.php`, and `CoverageIdentityStrategyRegistry.php` only where required for `inboundConnector` and the three included identity handoffs. - **Fake runner boundary**: `apps/platform/tests/Support/TenantConfiguration/Spec430ExchangePowerShellFakeCommandRunner.php`; no production runner is introduced or wired. - **Focused tests**: new Spec 430 Pest files under `apps/platform/tests/Unit/Support/TenantConfiguration/` and `apps/platform/tests/Feature/TenantConfiguration/`. ## Included And Excluded Types - **Included**: `transportRule`, `remoteDomain`, `inboundConnector`. - **Excluded/deferred**: `acceptedDomain`, `organizationConfig`, `mailboxPlan`, `outboundConnector`, `sharingPolicy`, Teams policy types including `appPermissionPolicy`, `appSetupPolicy`, `meetingPolicy`, `messagingPolicy`, `teamsUpdateManagementPolicy`, `teamsChannelsPolicy`, `externalAccessPolicy`, and the rest of Cohort 1. ## Implementation Summary Implemented the first Exchange PowerShell adapter-contract slice as a structured, no-live-execution command contract boundary for: - `transportRule` -> `Get-TransportRule` - `remoteDomain` -> `Get-RemoteDomain` - `inboundConnector` -> `Get-InboundConnector` The contract boundary is metadata-only and fake-runner-testable. It does not introduce a production runner, shell execution, provider calls, Microsoft service calls, evidence promotion, OperationRun creation, compare/render/certification/restore/customer readiness, UI, routes, navigation, downloads, or customer-facing copy. ## Files Changed Runtime: - `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCommandContracts.php` - `apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php` - `apps/platform/app/Services/TenantConfiguration/ClaimGuard.php` - `apps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php` - `apps/platform/app/Services/TenantConfiguration/ResourceTypeRegistry.php` - `apps/platform/app/Services/TenantConfiguration/SupportedScopeResolver.php` Tests: - `apps/platform/tests/Support/TenantConfiguration/Spec430ExchangePowerShellFakeCommandRunner.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellCommandAllowlistTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430TransportRuleCommandContractTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430RemoteDomainCommandContractTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430InboundConnectorCommandContractTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellResolverTest.php` - `apps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellMetadataTest.php` - `apps/platform/tests/Feature/TenantConfiguration/Spec430ExchangePowerShellNoPromotionTest.php` - Focused Spec 417/419/420/426/427 regression expectations updated for the verified-pending source-contract state and `inboundConnector` default-sync denominator. Spec artifacts: - `specs/430-exchange-powershell-adapter-contract-slice-1/implementation-report.md` ## Command Boundary And Allowlist Proof - Only the three read-only command names are allowlisted: `Get-TransportRule`, `Get-RemoteDomain`, `Get-InboundConnector`. - Mutation command families are rejected before fake-runner execution: `Set-*`, `New-*`, `Remove-*`, `Enable-*`, `Disable-*`, `Update-*`, `Start-*`, `Stop-*`, `Invoke-*`, `Search-*`, `Export-*`, and `Import-*`. - Raw command strings, shell metacharacters, pipelines, script blocks, semicolon-separated commands, redirection, file writes, module installation fragments, unknown commands, and unknown parameters are rejected. - The fake runner accepts only structured contract arrays and returns test-only structured results. No production runner was added. ## Resolver Proof - `transportRule`, `remoteDomain`, and `inboundConnector` now resolve to: - `source_contract_state`: `contract_verified_pending_capture` - `provider_adapter_state`: `adapter_contract_available` - `outcome`: `capture_blocked_missing_contract` - `provider_calls_allowed`: `false` - `execution_enabled`: `false` - `evidence_promotion_allowed`: `false` - `customer_claims_allowed`: `false` - `restore_allowed`: `false` - `certification_allowed`: `false` - `CoverageSourceContractDecision::capturable()` remains false for these decisions because there is no source endpoint and the outcome is blocked pending capture. - Excluded Exchange and Teams types remain blocked/deferred. No guessed Graph endpoint or fallback reader was added. ## Metadata Proof - Permission metadata is `runtime_validation_pending`, includes `Exchange.ManageAsApp` only as a pending runtime validation note, and keeps Exchange RBAC validation deferred to a future execution spec. - Response-shape metadata distinguishes empty collection, permission denied, command unavailable, adapter unavailable, malformed response, and unexpected object shape. - Identity handoff rejects display-name-only identity and records stable/derived candidates per type. - Redaction metadata forbids tokens, secrets, authorization headers, cookies, certificate private material, passwords, raw transcripts, raw shell stdout/stderr, mail body/content, mailbox content, file content, and Teams transcript/content. - Protected configuration examples cover email/domain/rule-pattern fields for transport rules, domain fields for remote domains, and IP/certificate/routing metadata for inbound connectors. - Provider-owned Exchange cmdlet and response-field details remain internal source-contract metadata, not customer vocabulary, platform-core ownership truth, or product surface copy. - Future execution scope requires workspace, managed environment, and provider connection. Provider-native scope identifiers are metadata only and cannot bypass workspace/provider scope. ## Claim And Promotion Boundary - The only allowed claim wording is the exact internal/operator-only statement: `Exchange PowerShell adapter contracts exist for transportRule, remoteDomain, and inboundConnector.` - The same statement is blocked when not marked internal/operator-only. - No customer claim, certification, restore, compare/render, content-backed evidence, or readiness promotion is introduced. - No `TenantConfigurationResource`, `TenantConfigurationResourceEvidence`, or OperationRun record is created by this slice. ## Product Surface And Filament Close-Out - Product Surface Impact: no rendered product surface changed. - UI Surface Impact: none. - Page archetype/surface budgets/Technical Annex/deep-link demotion/canonical status vocabulary: `N/A - no rendered UI surface changed`. - Product Surface exceptions: none. - Focused browser proof: `N/A - no rendered UI surface changed`. - Human Product Sanity: `N/A - no product surface changed`. - Visible complexity outcome: unchanged. - No-legacy posture: no legacy shim, fallback reader, dual truth, v1 adapter, or mini-platform added. - Livewire v4 compliance: no Livewire/Filament runtime code changed; repo baseline remains Livewire v4. - Provider registration: no panel/provider changes; Laravel providers remain in `apps/platform/bootstrap/providers.php`. - Global search: no Filament resource/global search surface changed. - Destructive/high-impact actions: none added. - Asset strategy: no assets registered; `filament:assets` is not required for this slice. - Deployment impact: no env vars, migrations, queues, scheduler, storage, assets, or browser build required. Runtime default sync now adds `inboundConnector` to registry/scope defaults when `ResourceTypeRegistry::syncDefaults()` and `SupportedScopeResolver::syncDefaults()` are run; migration baseline stays historical at 32 resource rows. ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec430 --compact` - 64 passed, 479 assertions. - Selected Spec 426 regressions: - `Spec426ExchangeTeamsSourceContractResolverTest` - `Spec426ExchangeTeamsCoreEvidenceReadinessTest` - `Spec426ExchangeTeamsNoCertificationTest` - `Spec426ExchangeTeamsNoRestoreTest` - `Spec426ExchangeTeamsNoMiniPlatformTest` - `Spec426ExchangeTeamsNoTenantIdTest` - 11 passed, 123 assertions. - Selected Spec 427 regressions: - `Spec427ExchangeTeamsSourceContractStateTest` - `Spec427ExchangeTransportRuleContractTest` - `Spec427SourceContractIdentityHandoffTest` - `Spec427SourceContractPermissionMetadataTest` - `Spec427SourceContractRedactionTest` - `Spec427SourceContractResponseShapeTest` - `Spec427ExchangeTeamsNoEvidencePromotionTest` - `Spec427ExchangeTeamsNoCompareRenderCertificationTest` - `Spec427ExchangeTeamsNoCustomerRestoreClaimTest` - `Spec427ExchangeTeamsNoMiniPlatformTest` - `Spec427ExchangeTeamsNoTenantIdTest` - 30 passed, 306 assertions. - Registry/identity/capture outcome regressions: - `Spec415CoverageCaptureOutcomeTest` - `Spec419M365WorkloadRegistryTest` - `Spec419M365RegistryExpansionTest` - `TenantConfigurationResourceTypeRegistryTest` - `Spec420M365CaptureEligibilityTest` - `Spec417CoverageIdentityStrategyRegistryTest` - 21 passed, 983 assertions, 1 PostgreSQL-only check skipped in the local driver. - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - pass. - `git diff --check` - pass. - `rg -- "- \[ \]" specs/430-exchange-powershell-adapter-contract-slice-1/tasks.md` - no unchecked Spec 430 tasks remain. - Broad parallel `Spec426`/`Spec427`/`Spec428` filter attempts were not used as final proof because Sail processes were killed with signal 9 under parallel load. Final proof uses serial direct-file runs listed above. - Spec 428 executable test status: no `apps/platform/tests/**/Spec428*.php` files exist. Spec 428 remains a prepared no-op spec package; runtime no-evidence proof is covered by the Spec430 and selected Spec427 no-promotion tests above. ## Post-Implementation Analysis - Iteration 1 finding: adding `capture_blocked_pending_capture` as a new `CaptureOutcome` enum value would have diverged from the historical evidence-table check constraint while this spec explicitly avoids migrations and does not persist blocked evidence. - Fix applied: keep `contract_verified_pending_capture` as the resolver/source-contract state and use the existing `capture_blocked_missing_contract` capture outcome for non-capturable transient capture result rows. - Post-fix validation: final Spec430, Spec426, Spec427, registry/identity/capture outcome, Pint, and `git diff --check` validations passed. - Browser Smoke Test Gate: passed as N/A because no rendered UI surface changed. - Remaining confirmed in-scope findings: none. ## Final Repository State - Final dirty state is expected implementation work: runtime TenantConfiguration/Coverage v2 service updates, focused Spec 417/419/420/426/427 regression updates, new Spec 430 tests/support fake runner, and the active Spec 430 package. - No unrelated dirty files were observed. ## Deferred Work - A future spec must add any production Exchange PowerShell execution adapter, runtime RBAC/permission validation, provider connection handshake, command execution, capture normalization, evidence promotion, compare/render/certification/restore behavior, UI, or customer-facing claims.