## Summary - Adds the Spec 431 Exchange PowerShell invocation gate and operation registration slice. - Introduces trusted provider operation start handling and read-only Exchange PowerShell runner abstractions. - Updates provider capability/readiness evaluation and coverage for Exchange PowerShell invocation safety. ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Providers/ProviderCapabilityEvaluationTest.php tests/Feature/Providers/ProviderOperationCapabilityGateTest.php tests/Feature/TenantConfiguration/Spec431ExchangePowerShellInvocationGateTest.php tests/Unit/Providers/ProviderCapabilityRegistryTest.php tests/Unit/Providers/ProviderOperationStartGateTest.php tests/Unit/Support/TenantConfiguration/Spec430ExchangePowerShellCommandAllowlistTest.php tests/Unit/Support/TenantConfiguration/Spec431ExchangePowerShellOperationRegistrationTest.php tests/Unit/Verification/ManagedEnvironmentPermissionCapabilityMappingTest.php` - Result: 80 passed, 685 assertions. ## Product Surface / Ops - Rendered UI surface changed: N/A. - Filament/Livewire surface changed: N/A. - Deployment impact: config/runtime service changes only; no migrations, queues, or assets added. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #498
288 lines
21 KiB
Markdown
288 lines
21 KiB
Markdown
# Implementation Plan: Exchange PowerShell Invocation Operation Registration and Execution Gate
|
|
|
|
**Branch**: `431-exchange-powershell-invocation-operation-registration-gate` | **Date**: 2026-07-05 | **Spec**: `specs/431-exchange-powershell-invocation-operation-registration-gate/spec.md`
|
|
**Input**: Feature specification from `specs/431-exchange-powershell-invocation-operation-registration-gate/spec.md`
|
|
|
|
## Summary
|
|
|
|
Implement a narrow OperationRun/provider operation registration and fake-runner invocation gate for the three Exchange PowerShell contracts introduced by Spec 430: `transportRule`, `remoteDomain`, and `inboundConnector`. The implementation registers the canonical operation path, provider operation, provider capability, capability resolver mapping, safe summaries, sanitized failures, one invocation gate, a structured runner interface, and a fake runner. Production execution remains disabled/inert. No live Exchange calls, evidence, UI, migrations, jobs, scheduled capture, customer claims, or `tenant_id` are allowed.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4 / Laravel 12
|
|
**Primary Dependencies**: Filament v5 / Livewire v4 baseline remains unchanged; no UI code planned
|
|
**Storage**: PostgreSQL via existing `operation_runs` table only; no migration
|
|
**Testing**: Pest 4 focused unit and feature tests
|
|
**Validation Lanes**: fast-feedback for unit/feature; selected regressions for Spec 430/426/427/417/419/420; browser N/A
|
|
**Target Platform**: Laravel Sail local, Dokploy staging/production remains unaffected because production execution is disabled
|
|
**Project Type**: Laravel monolith under `apps/platform`
|
|
**Performance Goals**: No real provider work; fake-runner tests complete locally without network or shell execution
|
|
**Constraints**: No raw provider output persistence, no PowerShell process, no Microsoft calls, no UI, no migrations
|
|
**Scale/Scope**: Exactly three Exchange target types and one invocation operation
|
|
|
|
## Preflight Findings
|
|
|
|
- Current prep branch before Spec Kit execution: `platform-dev`.
|
|
- HEAD before Spec Kit execution: `9b58a569 feat: add Exchange PowerShell adapter contract slice 1 (#497)`.
|
|
- Initial dirty state before Spec Kit execution: clean.
|
|
- Spec Kit helper created branch: `431-exchange-powershell-invocation-operation-registration-gate`.
|
|
- Existing related packages: `specs/429-exchange-teams-source-surface-catalog-adapter-strategy/`, `specs/430-exchange-powershell-adapter-contract-slice-1/`.
|
|
- Spec 430 implementation report proves the three command contracts and no OperationRun invocation/evidence/UI/migration.
|
|
- Existing OperationRun type for generic capture: `tenant_configuration.capture`; no Exchange PowerShell invocation type exists.
|
|
- Existing operation catalog has `tenant_configuration.capture`; no invocation entry exists.
|
|
- Existing provider operation registry has provider connection, inventory, compliance snapshot, restore, directory groups, and role definitions entries; no Exchange PowerShell invocation entry exists.
|
|
- Existing provider capability registry has provider connection, inventory, configuration, restore, directory groups, and role definitions capabilities; no Exchange PowerShell invocation capability exists.
|
|
- Existing summary keys are fixed in `OperationSummaryKeys::all()` and unknown keys are dropped by `SummaryCountsNormalizer`.
|
|
- Existing `ProviderOperationStartGate` stores `provider_connection_id` in OperationRun context and validates provider bindings/capabilities; implementation should reuse or compose with this path where practical.
|
|
- Existing actor capability registry includes `App\Support\Auth\Capabilities::PROVIDER_RUN`; Spec 431 reuses it instead of adding a new actor/RBAC capability.
|
|
- Existing provider credentials are represented through `ProviderConnection::credential`, `ProviderCredential`, `CredentialManager`, and `ProviderIdentityResolver`; Spec 431 must use that path and must not invent a credential reference store.
|
|
- Existing feature flags live under `tenantpilot.features.*`; Spec 431 uses `tenantpilot.features.exchange_powershell_invocation`, default `false`.
|
|
|
|
## UI / Surface Guardrail Plan
|
|
|
|
- **Guardrail scope**: no operator-facing surface change.
|
|
- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: N/A.
|
|
- **No-impact class, if applicable**: backend-only operation registration/gate.
|
|
- **Native vs custom classification summary**: N/A.
|
|
- **Shared-family relevance**: OperationRun and provider operation shared contracts only; no rendered UI shared family.
|
|
- **State layers in scope**: OperationRun lifecycle and summary/failure state only.
|
|
- **Audience modes in scope**: internal operator/reviewer only; no customer surface.
|
|
- **Decision/diagnostic/raw hierarchy plan**: No product view. Raw provider output is forbidden from persistence.
|
|
- **Raw/support gating plan**: Raw output not stored.
|
|
- **One-primary-action / duplicate-truth control**: N/A.
|
|
- **Handling modes by drift class or surface**: N/A.
|
|
- **Repository-signal treatment**: no rendered UI signal.
|
|
- **Special surface test profiles**: N/A.
|
|
- **Required tests or manual smoke**: `N/A - no rendered UI surface changed`.
|
|
- **Exception path and spread control**: none.
|
|
- **Active feature PR close-out entry**: no rendered product surface changed.
|
|
- **UI/Productization coverage decision**: No UI surface impact.
|
|
- **Coverage artifacts to update**: none.
|
|
- **No-impact rationale**: The spec forbids routes, Filament pages, Livewire components, navigation, global search, and assets.
|
|
- **Navigation / Filament provider-panel handling**: no panel/provider registration changes.
|
|
- **Screenshot or page-report need**: no.
|
|
|
|
## Product Surface Contract Plan
|
|
|
|
- **Product Surface Contract reference**: `docs/product/standards/product-surface-contract.md`.
|
|
- **No-legacy posture**: canonical addition only; no compatibility aliases, fallback readers, hidden routes, duplicate UI, old labels, or historical fixtures.
|
|
- **Page archetype and surface budget plan**: N/A.
|
|
- **Technical Annex and deep-link demotion plan**: N/A - no rendered product surface changed. OperationRun remains internal/audit truth.
|
|
- **Canonical status vocabulary plan**: N/A.
|
|
- **Product Surface exceptions**: none.
|
|
- **Browser verification plan**: `N/A - no rendered UI surface changed`.
|
|
- **Human Product Sanity plan**: N/A.
|
|
- **Visible complexity outcome target**: neutral.
|
|
- **Implementation report target**: `specs/431-exchange-powershell-invocation-operation-registration-gate/implementation-report.md`.
|
|
|
|
## Filament / Livewire / Deployment Posture
|
|
|
|
- **Livewire v4 compliance**: repo baseline remains Livewire v4; no Livewire runtime code planned.
|
|
- **Panel provider registration location**: no panel/provider change; Laravel providers remain in `apps/platform/bootstrap/providers.php`.
|
|
- **Global search posture**: no Filament resource/global search surface changed.
|
|
- **Destructive/high-impact action posture**: none added.
|
|
- **Asset strategy**: no assets; `filament:assets` is not required for this slice.
|
|
- **Testing plan**: no pages/widgets/relation managers/actions/browser tests because no rendered UI surface changes.
|
|
- **Deployment impact**: no env vars, migrations, queues, scheduler, storage, assets, container, or Dokploy changes. Production execution remains disabled/inert.
|
|
|
|
## Shared Pattern & System Fit
|
|
|
|
- **Cross-cutting feature marker**: yes.
|
|
- **Systems touched**: OperationRun type/catalog/lifecycle, provider operation registry, provider capability registry, capability resolver, summary normalization, failure sanitizer, provider operation start/gate path, Coverage v2 source contract resolver/contracts.
|
|
- **Shared abstractions reused**: `OperationRunService`, `OperationCatalog`, `ProviderOperationRegistry`, `ProviderCapabilityRegistry`, `OperationRunCapabilityResolver`, `OperationSummaryKeys`, `SummaryCountsNormalizer`, `RunFailureSanitizer`, `CoverageSourceContractResolver`, `ExchangePowerShellCommandContracts`.
|
|
- **New abstraction introduced? why?**: One invocation gate and one runner interface are allowed because provider/shell invocation must be centrally blocked, structured, and fake-testable before evidence work can safely use it.
|
|
- **Why the existing abstraction was sufficient or insufficient**: Existing registries and OperationRun lifecycle are sufficient foundations but lack this operation/capability/runner boundary.
|
|
- **Actor capability path**: Reuse `Capabilities::PROVIDER_RUN` for operation execution. If implementation proves that capability is too broad, stop and amend the spec before adding a new actor capability or role mapping.
|
|
- **Public gate boundary**: `ExchangePowerShellInvocationGate` is the sole public invocation entry point. It may call `ProviderOperationStartGate` internally for provider binding/capability/scope behavior; feature code and tests must not directly call the runner or provider start gate for this invocation.
|
|
- **Bounded deviation / spread control**: No parallel provider framework. If a production runner class is introduced, it must be inert and blocked by default.
|
|
|
|
## OperationRun UX Impact
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: yes, backend OperationRun creation/lifecycle only.
|
|
- **Central contract reused**: `OperationRunService` for creation/transition; `OperationSummaryKeys` and `SummaryCountsNormalizer`; `RunFailureSanitizer`.
|
|
- **Delegated UX behaviors**: no toast, run link, artifact link, browser event, queued DB notification, or surface messaging.
|
|
- **Surface-owned behavior kept local**: none.
|
|
- **Queued DB-notification policy**: N/A.
|
|
- **Terminal notification path**: central lifecycle mechanism where applicable; no feature-local notification.
|
|
- **Exception path**: none.
|
|
|
|
## Provider Boundary & Portability Fit
|
|
|
|
- **Shared provider/platform boundary touched?**: yes.
|
|
- **Provider-owned seams**: Exchange PowerShell command names, command contracts, response shape, command safety, fake-runner outcome vocabulary.
|
|
- **Platform-core seams**: OperationRun type/catalog, provider operation registry, provider capability registry, capability resolver, summary/failure sanitizer, provider connection context.
|
|
- **Neutral platform terms / contracts preserved**: operation, provider connection, capability, managed environment, workspace, source contract, summary counts, failure reason, runner mode.
|
|
- **Retained provider-specific semantics and why**: Exchange command names stay inside provider-owned contracts because Spec 431 is explicitly Exchange PowerShell only.
|
|
- **Bounded extraction or follow-up path**: document-in-feature; production runner hardening and evidence promotion remain separate specs.
|
|
|
|
## Constitution Check
|
|
|
|
- Inventory-first: no inventory or evidence truth is changed.
|
|
- Read/write separation: no restore/apply/mutation provider action; only read-only command contracts are fake-invoked.
|
|
- Graph contract path: no Microsoft Graph call path changed; no Graph call added.
|
|
- Deterministic capabilities: provider/actor capability mappings must be central and tested.
|
|
- Actor capability: the invocation operation maps to existing `Capabilities::PROVIDER_RUN`; no new actor capability is planned without a spec amendment.
|
|
- RBAC-UX: non-member workspace/environment scope is 404; member without capability is 403; readonly cannot invoke.
|
|
- Workspace isolation: workspace and managed environment are mandatory inputs.
|
|
- Tenant isolation: managed-environment entitlement is enforced before provider connection disclosure.
|
|
- Feature gate: logical key `tenant_configuration.exchange_powershell_invocation` is implemented via `tenantpilot.features.exchange_powershell_invocation` and defaults to false.
|
|
- Credential source: non-fake mode uses existing `ProviderConnection::credential` / `ProviderCredential` / `ProviderIdentityResolver` truth and blocks safely when no repo-supported credential reference exists.
|
|
- Run observability: invocation attempts are OperationRun-owned before fake runner execution.
|
|
- OperationRun start UX: no rendered start surface; lifecycle/summary/failure paths stay central.
|
|
- Ops-UX 3-surface feedback: no new UI feedback or notification policy.
|
|
- Ops-UX lifecycle: status/outcome transitions only through `OperationRunService`.
|
|
- Ops-UX summary counts: allowed keys only, flat numeric values only.
|
|
- Data minimization: OperationRun context stores safe identifiers and runner mode only; no raw output, credentials, tokens, transcripts, or payloads.
|
|
- Test governance: focused unit/feature tests with fake runners; no browser lane; no heavy shared fixtures by default.
|
|
- Proportionality: one operation/capability/gate/runner boundary justified by provider credential safety, RBAC, and operation truth.
|
|
- No premature abstraction: no generic PowerShell platform or multi-provider framework.
|
|
- Persisted truth: no new tables or persisted evidence truth.
|
|
- Behavioral state: blocker/failure codes must alter execution handling and sanitize operator diagnosis.
|
|
- Provider boundary: Exchange semantics remain provider-owned and do not become platform-core ownership truth.
|
|
- V1 explicitness / few layers: narrow explicit mapping and one gate preferred.
|
|
- Spec discipline / bloat check: proportionality review included in `spec.md`.
|
|
- Filament-native UI: N/A - no UI.
|
|
- Product Surface Contract: N/A - no rendered surface changed.
|
|
|
|
## Test Governance Check
|
|
|
|
- **Test purpose / classification by changed surface**: Unit for registries/summary/failure/runner shape; Feature for OperationRun/gate/provider scope/no evidence/no UI/no tenant_id.
|
|
- **Affected validation lanes**: fast-feedback/confidence focused tests; selected regressions; browser N/A.
|
|
- **Why this lane mix is the narrowest sufficient proof**: The behavior is backend registry/gate/service behavior with fake runner and DB OperationRun assertions; no rendered UI or PostgreSQL-specific migration exists.
|
|
- **Narrowest proving command(s)**:
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec431 --compact`
|
|
- selected Spec 430/426/427/417/419/420 regression files or filters documented by implementation
|
|
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
|
|
- `git diff --check`
|
|
- **Fixture / helper / factory / seed / context cost risks**: Provider connection/workspace/managed-environment/actor setup required for feature tests; keep helper setup explicit and local.
|
|
- **Expensive defaults or shared helper growth introduced?**: no; fake runner should be local to Spec 431 tests unless later specs adopt it intentionally.
|
|
- **Heavy-family additions, promotions, or visibility changes**: none planned.
|
|
- **Surface-class relief / special coverage rule**: backend-only; browser `N/A - no rendered UI surface changed`.
|
|
- **Closing validation and reviewer handoff**: implementation report must list exact tests and pass/fail counts.
|
|
- **Budget / baseline / trend follow-up**: none unless implementation materially expands regression lanes.
|
|
- **Review-stop questions**: verify no shell execution, no Microsoft calls, no evidence, no raw output persistence, no UI, no migration, no `tenant_id`, no mini-platform.
|
|
- **Escalation path**: reject-or-split if implementation attempts live provider execution, evidence promotion, UI, migrations, or broader Exchange types.
|
|
- **Active feature PR close-out entry**: no rendered UI surface changed; no deployment impact.
|
|
- **Why no dedicated follow-up spec is needed**: Routine test upkeep stays inside Spec 431; production runner hardening/evidence promotion are already separate follow-up candidates.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/431-exchange-powershell-invocation-operation-registration-gate/
|
|
|-- spec.md
|
|
|-- plan.md
|
|
|-- tasks.md
|
|
|-- checklists/
|
|
| `-- requirements.md
|
|
`-- implementation-report.md
|
|
```
|
|
|
|
### Source Code (likely affected by later implementation)
|
|
|
|
```text
|
|
apps/platform/app/Support/OperationRunType.php
|
|
apps/platform/app/Support/OperationCatalog.php
|
|
apps/platform/app/Services/Providers/ProviderOperationRegistry.php
|
|
apps/platform/app/Services/Providers/ProviderOperationStartGate.php
|
|
apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityRegistry.php
|
|
apps/platform/app/Support/Operations/OperationRunCapabilityResolver.php
|
|
apps/platform/app/Support/Auth/Capabilities.php
|
|
apps/platform/app/Support/OpsUx/OperationSummaryKeys.php
|
|
apps/platform/app/Support/OpsUx/SummaryCountsNormalizer.php
|
|
apps/platform/app/Support/OpsUx/RunFailureSanitizer.php
|
|
apps/platform/app/Models/ProviderConnection.php
|
|
apps/platform/app/Models/ProviderCredential.php
|
|
apps/platform/app/Services/Providers/ProviderIdentityResolver.php
|
|
apps/platform/app/Services/Providers/CredentialManager.php
|
|
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCommandContracts.php
|
|
apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php
|
|
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellInvocationGate.php
|
|
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCommandRunner.php
|
|
apps/platform/app/Services/TenantConfiguration/FakeExchangePowerShellCommandRunner.php
|
|
apps/platform/config/tenantpilot.php
|
|
apps/platform/tests/Unit/Support/TenantConfiguration/
|
|
apps/platform/tests/Feature/TenantConfiguration/
|
|
```
|
|
|
|
Forbidden source changes:
|
|
|
|
```text
|
|
apps/platform/routes/**
|
|
apps/platform/resources/views/**
|
|
apps/platform/app/Filament/**
|
|
apps/platform/app/Livewire/**
|
|
apps/platform/database/migrations/**
|
|
Exchange-specific evidence tables
|
|
jobs for live provider execution
|
|
provider clients that call Microsoft
|
|
```
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 0 - Preflight
|
|
|
|
- Capture branch, HEAD, dirty state.
|
|
- Reconfirm Spec 430 contract state and no pending checklist blocker.
|
|
- Reconfirm target types and command names.
|
|
- Reconfirm no migration, no UI, no evidence, no live execution, no `tenant_id`.
|
|
|
|
### Phase 1 - Operation and Provider Registration
|
|
|
|
- Add canonical OperationRun type.
|
|
- Add OperationCatalog entry and alias inventory entry only if required by repo pattern.
|
|
- Add ProviderOperationRegistry definition and Microsoft active binding or explicit internal binding.
|
|
- Add ProviderCapabilityRegistry capability.
|
|
- Add OperationRunCapabilityResolver execution mapping.
|
|
|
|
### Phase 2 - Summary and Failure Plumbing
|
|
|
|
- Reuse `total`, `processed`, `succeeded`, `failed`, `skipped`, and `items` unless implementation proves a new key is necessary.
|
|
- Extend failure normalization only with safe bounded mappings or known provider reason codes.
|
|
- Test known/unknown failures and redaction.
|
|
|
|
### Phase 3 - Invocation Gate
|
|
|
|
- Add single gate service.
|
|
- Compose with existing provider operation/capability/scope gates where practical.
|
|
- Require verified Spec 430 contracts, allowlisted commands, parameters, actor capability `Capabilities::PROVIDER_RUN`, provider scope, feature gate `tenantpilot.features.exchange_powershell_invocation`, runner mode, redaction policy, and existing provider credential/identity resolution unless fake-runner-only.
|
|
- Create OperationRun before fake runner execution.
|
|
|
|
### Phase 4 - Runner Boundary
|
|
|
|
- Add structured runner interface.
|
|
- Add fake runner and structured envelopes.
|
|
- Add disabled/inert production runner only if needed.
|
|
- Reject raw/mutation/arbitrary command paths.
|
|
|
|
### Phase 5 - No-Promotion Safety
|
|
|
|
- Prove no evidence rows, no coverage promotion, no compare/render/certification/restore/customer state, no customer output, no UI/routes/assets/global search, no migration, no `tenant_id`, no mini-platform.
|
|
|
|
### Phase 6 - Validation and Report
|
|
|
|
- Run focused Spec 431 tests.
|
|
- Run selected regressions.
|
|
- Run Pint and `git diff --check`.
|
|
- Complete implementation report with proportionality, proof, no-surface, and deployment close-out.
|
|
|
|
## Risk Controls
|
|
|
|
- Stop if implementation needs a migration.
|
|
- Stop if implementation needs live provider execution.
|
|
- Stop if a production runner would call PowerShell.
|
|
- Stop if evidence persistence is required.
|
|
- Stop if UI/routes/jobs/scheduled capture are introduced.
|
|
- Stop if new summary keys are emitted without registration/tests.
|
|
- Stop if provider/capability resolution cannot fail closed.
|
|
- Stop if `Capabilities::PROVIDER_RUN` proves too broad and a new actor capability would be required without a spec amendment.
|
|
- Stop if the feature gate cannot default disabled through `tenantpilot.features.exchange_powershell_invocation`.
|
|
- Stop if credential material would be persisted or logged.
|
|
- Stop if credential checks require a new credential persistence path.
|
|
- Stop if `tenant_id` is introduced.
|
|
|
|
## Preparation Analyze Result
|
|
|
|
Initial preparation self-review recorded this package as aligned with current repo truth and preparation-only boundaries. Follow-up `/speckit-analyze` remediation clarified actor capability, feature-gate path/default, credential-source truth, and the single public invocation gate boundary. No application code was modified. The repo does not expose executable `speckit.plan`, `speckit.tasks`, or `speckit.analyze` commands; this package was created with the available Spec Kit feature/plan scripts plus manual artifact authoring against the repository templates and prompts.
|