scopeMatches($environment, $connection)) { return ExchangePowerShellGateResult::blocked( reasonCode: ProviderReasonCodes::TenantTargetMismatch, failureCode: 'readiness_blocked_scope_mismatch', message: 'Exchange PowerShell invocation readiness requires a matching workspace, environment, and provider connection.', context: [ 'readiness_state' => 'blocked', 'readiness_blocker' => 'scope', 'scope_state' => 'mismatch', ], ); } $credentialGate = $this->credentials->resolve($connection); if (! $credentialGate->allowed) { return $this->blockedFromGate($credentialGate, 'credential'); } $permissionGate = $this->permissions->evaluate($environment, $connection); if (! $permissionGate->allowed) { return $this->blockedFromGate($permissionGate, 'permission'); } return ExchangePowerShellGateResult::allowed([ 'readiness_state' => 'ready_for_live_invocation', 'credential_state' => $credentialGate->context['credential_state'] ?? 'ready', 'permission_evidence_state' => $permissionGate->context['permission_evidence_state'] ?? 'verified', 'permission_currentness_days' => $permissionGate->context['permission_currentness_days'] ?? null, ]); } private function scopeMatches(ManagedEnvironment $environment, ProviderConnection $connection): bool { return (int) $connection->workspace_id === (int) $environment->workspace_id && (int) $connection->managed_environment_id === (int) $environment->getKey(); } private function blockedFromGate(ExchangePowerShellGateResult $gate, string $blocker): ExchangePowerShellGateResult { return ExchangePowerShellGateResult::blocked( reasonCode: $gate->reasonCode ?? ProviderReasonCodes::ProviderBindingUnsupported, failureCode: $gate->failureCode ?? 'readiness_blocked', message: $gate->message ?? 'Exchange PowerShell invocation readiness was blocked.', context: [ 'readiness_state' => 'blocked', 'readiness_blocker' => $blocker, ...$this->safeContext($gate->context), ], ); } /** * @param array $context * @return array */ private function safeContext(array $context): array { return array_filter( $context, static fn (mixed $value): bool => is_scalar($value) || $value === null, ); } }