# Research: Provider Connection Scope & Microsoft Profile Extraction **Date**: 2026-05-07 **Branch**: `281-provider-connection-scope` ## Decision 1: Keep `281` as a contract-extraction slice, not a new provider framework - **Decision**: limit the slice to provider-neutral target-scope and identity contract extraction across the existing `ProviderConnection`, resolver, summary, onboarding, and start-gate seams. - **Rationale**: current repo truth already contains the right seam inventory: `ProviderConnectionTargetScopeDescriptor`, `ProviderConnectionTargetScopeNormalizer`, `ProviderConnectionSurfaceSummary`, `ProviderIdentityResolution`, and `ProviderOperationStartGate`. The problem is the Microsoft-shaped payload still flowing through them, not the absence of a framework. - **Alternatives considered**: - Introduce a provider registry or capability framework: rejected because the feature has only one current provider and the constitution explicitly prefers bounded extraction over speculative multi-provider machinery. - Add a generic provider profile subsystem first: rejected because the current release does not need new persistence to describe provider profile detail. ## Decision 2: Treat `ProviderConnection` as the unchanged persisted source of truth - **Decision**: `ProviderConnection` stays the only persisted binding record for this slice, anchored by `workspace_id` plus `managed_environment_id`. No `tenant_id` migration work and no provider-profile table are added. - **Rationale**: `ProviderConnection` already belongs to `ManagedEnvironment` via `managed_environment_id`, and the raw candidate wording about replacing `tenant_id` is stale. The remaining defect is contract shaping, not relationship ownership. - **Alternatives considered**: - Re-open the old `tenant_id` to `managed_environment_id` candidate: rejected because repo truth already completed that move. - Add a separate provider-profile entity: rejected because provider profile detail is still derived from the current connection and identity-resolution path. - **Documented candidate deviation**: the raw candidate also proposed a new shared field family around `provider_key`, `external_account_id`, `provider_metadata`, and explicit run-context workspace/environment keys. In current repo truth, `provider`, `workspace_id`, and `managed_environment_id` already exist, so this package narrows the shared contract to `target_scope`, `effective_client_identity`, nested `provider_context`, and existing provider-owned metadata instead of inventing new top-level persisted fields. ## Decision 3: Make `ProviderConnectionTargetScopeDescriptor` the canonical shared `target_scope` contract - **Decision**: standardize every shared target-scope output on `provider`, `scope_kind`, `scope_identifier`, `scope_display_name`, `shared_label`, and `shared_help_text` instead of any Microsoft-named top-level keys. - **Rationale**: the descriptor already exists and already exposes the right field names. What is missing is consistent reuse across resolver, audit, UI summary, onboarding, and run-context seams. - **Alternatives considered**: - Keep `target_scope.entra_tenant_id` alongside the neutral fields: rejected because pre-production lean doctrine favors canonical replacement over shared compatibility aliases. - Add a second DTO or presenter for neutral naming: rejected because the existing descriptor already covers the need. ## Decision 4: Reshape the shared identity result around effective client identity and nested provider context - **Decision**: keep `ProviderIdentityResolution` as the single shared identity-result object, but move its planned contract center of gravity to `target_scope`, `effective_client_identity`, `blocked_reason`, and nested `provider_context` rather than shared `tenantContext` naming. - **Rationale**: the current implementation already carries these concepts as `targetScope`, `effectiveClientId`, `credentialSource`, and contextual detail arrays. The shared leak is the `tenantContext` field name and its downstream use as the primary field in platform-core seams, so the package standardizes the planned artifact language on `target_scope`, `effective_client_identity`, and `provider_context`. - **Alternatives considered**: - Leave `tenantContext` as the canonical shared field and just add neutral labels in UI: rejected because the identity contract would remain Microsoft-shaped below the UI. - Split dedicated and platform identities into new separate result types: rejected because one existing result object already supports both paths. ## Decision 5: Keep Microsoft-specific detail inside provider-owned profile or context metadata - **Decision**: preserve Microsoft tenant ID, authority tenant, redirect URI, consent URL shaping, and Graph runtime options only inside provider-owned nested metadata or provider-owned consumer seams such as `ProviderIdentityContextMetadata`, `AdminConsentUrlFactory`, and `ProviderGateway`. - **Rationale**: the boundary catalog already marks identity resolution and operation start as platform-core while runtime transport and consent URL shaping remain provider-owned. The narrowest fix is to push Microsoft semantics outward, not to pretend they disappear. - **Alternatives considered**: - Strip Microsoft detail from all outputs entirely: rejected because support, consent, and troubleshooting still need those fields. - Keep Microsoft detail as top-level fields in shared summaries: rejected because that keeps the platform-core contract Microsoft-shaped. ## Decision 6: Reuse `ProviderConnectionSurfaceSummary` as the only summary adapter - **Decision**: keep `ProviderConnectionSurfaceSummary` as the one summary contract reused by `ProviderConnectionResource`, `ManagedTenantOnboardingWizard`, and managed-environment related-context summaries. - **Rationale**: the wizard already calls `ProviderConnectionSurfaceSummary::forConnection($connection)->targetScopeSummary()`, and the resource already delegates summary rendering there. Reusing the same adapter is the narrowest way to remove duplicate truth. - **Alternatives considered**: - Let onboarding and provider-connections each compute their own neutral labels: rejected because it would create the second identity story this feature is meant to remove. - Put raw `entra_tenant_id` back into onboarding for clarity: rejected because it would reintroduce a Microsoft-only summary path. ## Decision 7: Rewrite provider-operation start context to use neutral shared scope fields - **Decision**: `ProviderOperationStartGate` should emit the neutral `target_scope` descriptor in started and blocked run context, with any Microsoft-specific detail nested under provider-owned provider-context metadata. - **Rationale**: the start gate is one of the explicit boundary hotspots in `config/provider_boundaries.php`, and it still writes `target_scope.entra_tenant_id` directly today. Later artifact and taxonomy work would inherit that leak if it stays. - **Alternatives considered**: - Leave the start-gate context alone and only update UI summaries: rejected because `OperationRun` context is shared operational truth. - Add neutral fields beside `entra_tenant_id`: rejected because the feature is pre-production and should replace the shared shape rather than dual-write it. ## Decision 8: Keep credential validation neutral at the platform-core seam - **Decision**: `CredentialManager` stays the existing credential access seam, but its scope-validation rule and error wording should align to the normalized target-scope identifier rather than a Microsoft-shaped mismatch message. - **Rationale**: `CredentialManager` is already inside the shared provider identity path and currently compares a payload scope assertion against `connection->entra_tenant_id`. That is acceptable as an implementation detail, but not as the platform-core contract wording. - **Alternatives considered**: - Ignore the message mismatch and leave it Microsoft-shaped: rejected because it leaks provider semantics back into the shared identity failure path. - Change provider-credential persistence shape now: rejected because no new persistence is justified for this slice. ## Decision 9: Keep Filament surface behavior and deployment strategy unchanged - **Decision**: `ProviderConnectionResource` remains non-globally-searchable, keeps `View` and `Edit` pages, preserves the current confirmation-protected mutations, and adds no new asset registration. - **Rationale**: the feature changes summary and contract shaping only. The current resource already satisfies the required Filament action and search guardrails. - **Alternatives considered**: - Enable global search for provider connections while touching the resource: rejected because the feature does not need it and the spec explicitly keeps it out of scope. - Add new UI chrome or asset bundles for provider profile detail: rejected because the existing resource and wizard can carry the nested disclosure. ## Decision 10: Prove the slice with focused feature coverage and one browser smoke - **Decision**: use focused provider feature tests, one Filament/browser smoke covering provider-connections plus onboarding, and keep proof commands identical across spec, plan, and quickstart. - **Rationale**: this slice changes shared contract truth across several PHP seams and two operator-facing consumers, but it does not justify a new heavy-governance family or broad browser matrix. - **Alternatives considered**: - Feature tests only: rejected because the live resource plus onboarding continuity is part of the user-visible contract. - Broad browser or smoke expansion across unrelated provider pages: rejected because it would create unnecessary suite cost. ## Final Research Outcome - `ProviderConnection` and `ProviderCredential` persistence stay unchanged. - The canonical shared `target_scope` contract is the existing descriptor shape, not `entra_tenant_id`. - The canonical shared identity result is the existing `ProviderIdentityResolution` seam, but its planned contract must stop centering `tenantContext` as shared truth and instead standardize on `target_scope`, `effective_client_identity`, and nested `provider_context`. - Provider-specific Microsoft details remain available through `ProviderIdentityContextMetadata` and provider-owned consumers only. - `ProviderConnectionSurfaceSummary` stays the one summary adapter for provider-connections, onboarding, and related context. - `ProviderOperationStartGate` becomes the critical contract rewrite point for neutral run context. - `ProviderConnectionResource` remains non-globally-searchable with confirmation-protected mutations intact. - The narrowest honest proof is the feature suite already named in the spec, one browser smoke, and no new runtime abstractions.