## Summary - normalize provider-neutral target-scope and identity contracts across provider connection resolution, operation-start gating, verification reporting, and boundary configuration - align provider connection resource, onboarding, tenant summaries, and operation follow-up on the same shared scope contract while keeping Microsoft-specific profile details in provider-owned metadata - add Spec 281 artifacts and focused feature/browser coverage for the new provider-scope contract - move the tenant dashboard context-chip rail into Filament header widgets so the metadata row renders directly under the page subtitle ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Providers/ProviderConnectionTargetScopeNeutralityTest.php tests/Feature/Providers/ProviderIdentityResolutionNeutralityTest.php tests/Feature/Providers/ProviderOperationStartGateTargetScopeContextTest.php tests/Feature/Filament/ProviderConnectionResourceScopeSummaryTest.php tests/Feature/Onboarding/ManagedTenantOnboardingProviderConnectionScopeTest.php tests/Feature/Guards/ProviderConnectionMicrosoftScopeLeakGuardTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Dashboard/TenantDashboardProductizationSummaryTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` ## Notes - Filament remains on v5 with Livewire v4-compatible surfaces only. - Provider registration location is unchanged; Laravel 11+ providers stay in `apps/platform/bootstrap/providers.php`. - `ProviderConnectionResource` remains non-globally-searchable and still exposes View/Edit pages. - No new asset registration was added; deploy-time `filament:assets` expectations are unchanged. - No new destructive action path was introduced; existing server authorization and confirmation handling remain in place where applicable. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #339
97 lines
11 KiB
Markdown
97 lines
11 KiB
Markdown
# 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. |