Spec 430: Exchange PowerShell adapter contract slice 1. Validation was not rerun during PR creation handoff; branch contains implementation report and tests. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #497
13 KiB
13 KiB
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.mdidentifiesnew_exchange_powershell_adapteras the recommended first Spec 430 adapter path and liststransportRule,remoteDomain, andinboundConnectoras 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, andCoverageIdentityStrategyRegistry.phponly where required forinboundConnectorand 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/andapps/platform/tests/Feature/TenantConfiguration/.
Included And Excluded Types
- Included:
transportRule,remoteDomain,inboundConnector. - Excluded/deferred:
acceptedDomain,organizationConfig,mailboxPlan,outboundConnector,sharingPolicy, Teams policy types includingappPermissionPolicy,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-TransportRuleremoteDomain->Get-RemoteDomaininboundConnector->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.phpapps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.phpapps/platform/app/Services/TenantConfiguration/ClaimGuard.phpapps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.phpapps/platform/app/Services/TenantConfiguration/ResourceTypeRegistry.phpapps/platform/app/Services/TenantConfiguration/SupportedScopeResolver.php
Tests:
apps/platform/tests/Support/TenantConfiguration/Spec430ExchangePowerShellFakeCommandRunner.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellCommandAllowlistTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430TransportRuleCommandContractTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430RemoteDomainCommandContractTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430InboundConnectorCommandContractTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellResolverTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellMetadataTest.phpapps/platform/tests/Feature/TenantConfiguration/Spec430ExchangePowerShellNoPromotionTest.php- Focused Spec 417/419/420/426/427 regression expectations updated for the verified-pending source-contract state and
inboundConnectordefault-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-*, andImport-*. - 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, andinboundConnectornow resolve to:source_contract_state:contract_verified_pending_captureprovider_adapter_state:adapter_contract_availableoutcome:capture_blocked_missing_contractprovider_calls_allowed:falseexecution_enabled:falseevidence_promotion_allowed:falsecustomer_claims_allowed:falserestore_allowed:falsecertification_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, includesExchange.ManageAsApponly 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:assetsis not required for this slice. - Deployment impact: no env vars, migrations, queues, scheduler, storage, assets, or browser build required. Runtime default sync now adds
inboundConnectorto registry/scope defaults whenResourceTypeRegistry::syncDefaults()andSupportedScopeResolver::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:
Spec426ExchangeTeamsSourceContractResolverTestSpec426ExchangeTeamsCoreEvidenceReadinessTestSpec426ExchangeTeamsNoCertificationTestSpec426ExchangeTeamsNoRestoreTestSpec426ExchangeTeamsNoMiniPlatformTestSpec426ExchangeTeamsNoTenantIdTest- 11 passed, 123 assertions.
- Selected Spec 427 regressions:
Spec427ExchangeTeamsSourceContractStateTestSpec427ExchangeTransportRuleContractTestSpec427SourceContractIdentityHandoffTestSpec427SourceContractPermissionMetadataTestSpec427SourceContractRedactionTestSpec427SourceContractResponseShapeTestSpec427ExchangeTeamsNoEvidencePromotionTestSpec427ExchangeTeamsNoCompareRenderCertificationTestSpec427ExchangeTeamsNoCustomerRestoreClaimTestSpec427ExchangeTeamsNoMiniPlatformTestSpec427ExchangeTeamsNoTenantIdTest- 30 passed, 306 assertions.
- Registry/identity/capture outcome regressions:
Spec415CoverageCaptureOutcomeTestSpec419M365WorkloadRegistryTestSpec419M365RegistryExpansionTestTenantConfigurationResourceTypeRegistryTestSpec420M365CaptureEligibilityTestSpec417CoverageIdentityStrategyRegistryTest- 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/Spec428filter 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*.phpfiles 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_captureas a newCaptureOutcomeenum 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_captureas the resolver/source-contract state and use the existingcapture_blocked_missing_contractcapture outcome for non-capturable transient capture result rows. - Post-fix validation: final Spec430, Spec426, Spec427, registry/identity/capture outcome, Pint, and
git diff --checkvalidations 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.