TenantAtlas/specs/238-provider-identity-target-scope/plan.md
ahmido 110245a9ec
Some checks are pending
Main Confidence / confidence (push) Waiting to run
feat: neutralize provider connection target-scope surfaces (#274)
## Summary
- add a shared provider target-scope descriptor, normalizer, identity-context metadata, and surface-summary layer
- update provider connection list, detail, create, edit, and onboarding surfaces to use neutral target-scope vocabulary while keeping Microsoft identity contextual
- align provider connection audit and resolver output with the neutral target-scope contract and add focused guard/unit/feature coverage for regressions

## Validation
- browser smoke: opened the tenant-scoped provider connection list, drilled into detail, and verified the edit/create surfaces in local admin context

## Notes
- this PR comes from the session branch created for the active feature work
- no additional runtime or persistence layer was introduced in this slice

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #274
2026-04-25 09:07:40 +00:00

265 lines
26 KiB
Markdown

# Implementation Plan: Provider Identity & Target Scope Neutrality
**Branch**: `238-provider-identity-target-scope` | **Date**: 2026-04-24 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/238-provider-identity-target-scope/spec.md`
**Note**: This plan keeps the slice intentionally narrow. It introduces one small shared target-scope descriptor layer over the existing provider connection and identity-resolution path, rewrites Microsoft-shaped default labels and summaries only on the in-scope shared surfaces, preserves Entra-specific identity details as contextual provider-owned metadata, and adds focused guardrails so shared provider/platform seams do not regress into Microsoft-first default truth.
## Summary
Add a config-free, in-process target-scope descriptor layer that sits between existing provider connection truth and operator-facing shared surfaces. The implementation will harden two concrete hotspots already visible in code: first, the shared resolution objects (`ProviderIdentityResolution` and adjacent provider-connection resolution paths) still expose Microsoft-shaped semantics such as `tenantContext`, `authorityTenant`, and `entra_tenant_id` as if they were default shared truth; second, `ProviderConnectionResource` uses `Entra tenant ID` as a required shared form field and as the default list/detail summary. The plan narrows the shared contract to neutral provider and target-scope concepts, keeps Microsoft tenant and directory identity available only as provider-owned contextual metadata, extends the same neutral semantics into onboarding-adjacent setup, aligns shared audit wording, and proves the result with focused unit, feature, Filament, onboarding, and guard coverage without adding a new provider runtime, new persistence, or a broader schema rewrite.
## Technical Context
**Language/Version**: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4
**Primary Dependencies**: `ProviderConnectionResource`, `ManagedTenantOnboardingWizard`, `ProviderConnection`, `ProviderConnectionResolver`, `ProviderConnectionResolution`, `ProviderConnectionMutationService`, `ProviderConnectionStateProjector`, `ProviderIdentityResolver`, `ProviderIdentityResolution`, `PlatformProviderIdentityResolver`, `BadgeRenderer`, Pest v4
**Storage**: Existing PostgreSQL tables such as `provider_connections`, `provider_credentials`, and existing audit tables; no new database tables planned
**Testing**: Pest v4 unit and focused feature tests through Laravel Sail
**Validation Lanes**: `fast-feedback`, `confidence`
**Target Platform**: Laravel admin web application rendered through Filament on the existing workspace and tenant admin surfaces
**Project Type**: Monorepo with one Laravel runtime in `apps/platform` and spec artifacts at repository root
**Performance Goals**: Keep target-scope resolution deterministic and in-process, add no new outbound call before existing consent or verification paths, and preserve current Microsoft-backed runtime performance on supported flows
**Constraints**: No new provider runtime, no provider marketplace, no new persistence, no broad credential-model redesign, no full rewrite of Spec 137 scope, no new operator-facing shell, and no new Microsoft-shaped default labels on shared surfaces
**Scale/Scope**: One shared target-scope descriptor layer, one bounded contextual-metadata path, three operator-facing surfaces, and focused unit plus feature guard coverage
## Filament v5 Implementation Contract
- **Livewire v4.0+ compliance**: Preserved. The feature changes existing Filament resources, shared support code, and tests only, with no legacy Livewire APIs.
- **Provider registration location**: Unchanged. Panel providers remain registered in `apps/platform/bootstrap/providers.php`.
- **Global search coverage**: No new resource or page is added and no existing global-search posture is widened. Provider connection global-search behavior remains unchanged in this slice.
- **Destructive actions**: No new destructive action is added. Existing security-sensitive provider connection mutations continue to require confirmation and authorization on their current surfaces.
- **Asset strategy**: No new assets are planned. Deployment expectations remain unchanged, including `cd apps/platform && php artisan filament:assets` only when registered Filament assets change.
- **Testing plan**: Prove the slice with one shared descriptor unit seam, focused provider connection and onboarding feature coverage, existing audit and UI enforcement coverage extensions, and one bounded neutrality guard test.
## UI / Surface Guardrail Plan
- **Guardrail scope**: changed surfaces across provider connection list and detail, provider connection create and edit, and the tenant onboarding provider setup step
- **Native vs custom classification summary**: native Filament plus shared support helpers
- **Shared-family relevance**: shared provider connection family, onboarding provider setup family, shared audit wording
- **State layers in scope**: `page`, `detail`, `wizard-step`
- **Handling modes by drift class or surface**: `review-mandatory`
- **Repository-signal treatment**: `review-mandatory`
- **Special surface test profiles**: `standard-native-filament`
- **Required tests or manual smoke**: `functional-core`, `state-contract`, `manual-smoke`
- **Exception path and spread control**: one named Microsoft contextual-identity boundary for tenant or directory identifiers, consent wording, and verification detail that remain provider-owned instead of shared default truth
- **Active feature PR close-out entry**: `Guardrail`
## Shared Pattern & System Fit
- **Cross-cutting feature marker**: yes
- **Systems touched**: `ProviderConnectionResource`, `ManagedTenantOnboardingWizard`, provider connection resolution and mutation services, provider identity resolution, badge and summary rendering, and provider-connection audit wording
- **Shared abstractions reused**: existing provider connection resource and detail path, existing identity and connection resolution services, existing badge renderer domains, existing provider connection audit flows
- **New abstraction introduced? why?**: yes. One small target-scope descriptor and one small surface-summary mapping layer are needed because multiple real surfaces currently duplicate or embed Microsoft-shaped default meaning.
- **Why the existing abstraction was sufficient or insufficient**: the current shared path is sufficient for authorization, persistence, consent, and verification routing, but it is insufficient because the same path uses Microsoft-specific field names and summary language as the default shared contract.
- **Bounded deviation / spread control**: Microsoft tenant, directory, and consent-specific details remain allowed only inside explicitly provider-owned contextual sections, helper copy, and audit detail for the Microsoft provider
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: no
- **Central contract reused**: `N/A`
- **Delegated UX behaviors**: `N/A`
- **Surface-owned behavior kept local**: `N/A`
- **Queued DB-notification policy**: `N/A`
- **Terminal notification path**: `N/A`
- **Exception path**: none
## Provider Boundary & Portability Fit
- **Shared provider/platform boundary touched?**: yes
- **Provider-owned seams**: Microsoft-specific consent and verification descriptors, contextual Entra tenant or directory identifiers, platform-app and authority details resolved by existing Microsoft identity services
- **Platform-core seams**: shared provider connection form labels, list and detail summaries, target-scope descriptor layer, shared validation shape, onboarding provider setup summary, audit wording for shared provider connection mutations
- **Neutral platform terms / contracts preserved**: provider, provider connection, target scope, scope identifier, scope display name, consent state, verification state, readiness summary
- **Retained provider-specific semantics and why**: `entra_tenant_id`, `authorityTenant`, `redirectUri`, and Microsoft-specific consent or verification wording remain because current-release truth is still Microsoft-first and operators still need these values on Microsoft-only paths
- **Bounded extraction or follow-up path**: broader governed-subject and compare-boundary work remains `follow-up-spec`; this feature resolves the provider connection identity and target-scope hotspot itself
## Constitution Check
*GATE: Passed before Phase 0 research. Re-check after Phase 1 design: still passed with one small descriptor layer, no new persistence, and no new provider runtime.*
| Gate | Status | Plan Notes |
|------|--------|------------|
| Inventory-first / read-write separation | PASS | The slice changes shared provider connection semantics and existing write-surface wording only. No new snapshot or operational write class is introduced. |
| Single Graph contract path / no inline remote work | PASS | Existing consent and verification flows keep their current provider-owned Graph path. The feature adds no new Graph call and no inline remote work on shared surfaces. |
| RBAC, workspace isolation, tenant isolation | PASS | Existing provider connection and onboarding surfaces keep current workspace and tenant guards. Non-members remain 404 and members missing capability remain 403. |
| Run observability / Ops-UX lifecycle | PASS | No new `OperationRun` type or UX behavior is introduced. Existing health-check or verification runs keep their current service-owned lifecycle. |
| Shared pattern first | PASS | The implementation reuses existing provider connection surfaces and resolution services instead of creating a new provider management stack. |
| Proportionality / no premature abstraction | PASS | One small target-scope descriptor layer is justified by multiple real surfaces and shared audits already depending on the same semantics. No marketplace or second-provider framework is introduced. |
| Persisted truth / behavioral state | PASS | No new table, persisted entity, or lifecycle family is added. Consent and verification remain the existing state dimensions. |
| Provider boundary | PASS | Shared target-scope truth is kept platform-core while Microsoft-specific identity remains provider-owned contextual metadata. |
| Filament v5 / Livewire v4 contract | PASS | The slice uses native Filament forms, tables, infolists, and existing shared primitives only. |
| Test governance | PASS | Coverage stays in focused unit and feature lanes with no browser or heavy-governance expansion. |
## Test Governance Check
- **Test purpose / classification by changed surface**: `Unit` for the descriptor and neutral-summary mapping seam; `Feature` for provider connection list/detail/create/edit, onboarding provider step, audit wording, and neutrality guard coverage; `Heavy-Governance`: none; `Browser`: none
- **Affected validation lanes**: `fast-feedback`, `confidence`
- **Why this lane mix is the narrowest sufficient proof**: The risk is shared semantic drift and operator-facing meaning on existing admin surfaces, not browser interaction or long-running runtime behavior. Unit tests prove the neutral contract, while feature tests prove the same meaning across the real surfaces and authorization contexts.
- **Narrowest proving command(s)**:
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Providers/ProviderConnectionTargetScopeDescriptorTest.php tests/Unit/Providers/ProviderIdentityResolutionNeutralityTest.php tests/Unit/Providers/ProviderConnectionBadgeMappingTest.php tests/Unit/Badges/ProviderConnectionBadgesTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php tests/Feature/Filament/ProviderConnectionsUiEnforcementTest.php tests/Feature/ManagedTenantOnboardingWizardTest.php tests/Feature/Audit/ProviderConnectionIdentityAuditTest.php tests/Feature/Guards/ProviderConnectionNeutralityGuardTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- **Fixture / helper / factory / seed / context cost risks**: low to moderate; reuse existing `ProviderConnectionFactory`, workspace and tenant membership fixtures, and current provider connection audit helpers. Do not add a new default provider-world helper.
- **Expensive defaults or shared helper growth introduced?**: no
- **Heavy-family additions, promotions, or visibility changes**: none
- **Surface-class relief / special coverage rule**: standard native Filament relief for the provider connection resource plus one onboarding wizard-step extension
- **Closing validation and reviewer handoff**: Re-run `pint`, then the focused test commands above. Reviewers should verify that shared labels and required fields are neutral by default, Microsoft contextual details still appear where genuinely needed, audit prose stays aligned, and no generic provider surface regained `Entra tenant ID` as default shared truth.
- **Budget / baseline / trend follow-up**: none expected
- **Review-stop questions**: Did the slice drift into broad identity migration or credential-model redesign? Did any test or helper make Microsoft context implicit by default? Did any shared surface keep Microsoft-shaped default labels or required fields? Did audit or validation copy remain provider-shaped on shared paths?
- **Escalation path**: `document-in-feature`
- **Active feature PR close-out entry**: `Guardrail`
- **Why no dedicated follow-up spec is needed**: This feature resolves the concrete provider-connection target-scope hotspot directly. Later governed-subject and compare-boundary work is already explicitly tracked separately.
## Project Structure
### Documentation (this feature)
```text
specs/238-provider-identity-target-scope/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── provider-identity-target-scope.logical.openapi.yaml
└── tasks.md
```
### Source Code (repository root)
```text
apps/platform/
├── app/
│ ├── Filament/
│ │ ├── Pages/Workspaces/ManagedTenantOnboardingWizard.php
│ │ └── Resources/ProviderConnectionResource.php
│ ├── Models/
│ │ └── ProviderConnection.php
│ ├── Services/
│ │ └── Providers/
│ │ ├── PlatformProviderIdentityResolver.php
│ │ ├── ProviderConnectionMutationService.php
│ │ ├── ProviderConnectionResolution.php
│ │ ├── ProviderConnectionResolver.php
│ │ ├── ProviderConnectionStateProjector.php
│ │ ├── ProviderIdentityResolution.php
│ │ └── ProviderIdentityResolver.php
│ └── Support/
│ └── Providers/
│ └── TargetScope/
└── tests/
├── Feature/
│ ├── Audit/
│ ├── Filament/
│ ├── Guards/
│ ├── ManagedTenantOnboardingWizardTest.php
│ └── ProviderConnections/
└── Unit/
└── Providers/
```
**Structure Decision**: Keep the entire slice inside the existing Laravel runtime in `apps/platform`. The only new code shape planned is a small `Support/Providers/TargetScope` helper namespace or equivalent small support layer. Runtime changes stay inside existing provider connection resources and provider identity or connection services.
## Complexity Tracking
No constitutional violation is planned. One bounded complexity addition is tracked because the feature introduces a new shared descriptor layer.
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| BLOAT-001 bounded target-scope descriptor | Multiple real surfaces and shared audit copy already need the same neutral provider-connection truth, and the current shared path still exposes Microsoft-shaped default semantics | Page-local label rewrites would not fix the shared contract or stop future regressions on the next surface |
## Proportionality Review
- **Current operator problem**: Shared provider connection setup and inspection still teach Microsoft-specific identity as if it were the platform's default connection truth, which causes wrong operator assumptions and future provider-boundary drift.
- **Existing structure is insufficient because**: current resolution objects and resource schemas mix neutral provider-connection meaning with Microsoft-only fields such as `entra_tenant_id`, `tenantContext`, and `authorityTenant`.
- **Narrowest correct implementation**: add one shared target-scope descriptor and summary mapping layer, route shared labels and validation through it, and keep Microsoft-specific detail contextual instead of rewriting the full provider architecture.
- **Ownership cost created**: one small support namespace, one focused unit seam, a handful of extended feature and audit tests, and stricter review expectations on shared provider connection surfaces.
- **Alternative intentionally rejected**: reusing the broader migration and credential redesign scope from Spec 137. It would widen the slice beyond the current hotspot and obscure the real shared-contract goal.
- **Release truth**: current-release truth with bounded anti-drift hardening
## Phase 0 Research Summary
- The slice should reuse existing provider connection surfaces and services, not create a new provider-management framework.
- `ProviderConnectionResource` is a concrete hotspot because `entra_tenant_id` is still the required shared form field and a default list or detail summary.
- `ProviderIdentityResolution` is a second hotspot because shared identity output still uses Microsoft-shaped terms such as `tenantContext` and `authorityTenant` as if they were default shared semantics.
- Neutral target-scope truth should live in a small shared descriptor or summary layer used by forms, infolists, tables, onboarding, validation, and audit wording.
- Microsoft tenant, directory, consent, and authority details should remain contextual provider-owned metadata instead of disappearing or becoming the shared default contract.
- Focused unit and feature tests are sufficient; browser or heavy-governance coverage would add cost without proving unique behavior.
## Phase 1 Design Summary
- `research.md` captures the bounded contract and vocabulary decisions that keep the slice narrow.
- `data-model.md` defines the target-scope descriptor, provider-owned identity metadata, operator-facing surface summary, and guard result shape.
- `contracts/provider-identity-target-scope.logical.openapi.yaml` defines the internal logical contract for reading normalized target-scope summaries and evaluating neutrality drift.
- `quickstart.md` records the narrow implementation order and the validation sequence.
- `tasks.md` should sequence the work from target-scope descriptor foundation through shared surface adoption, audit wording alignment, and guard coverage.
## Implementation Close-Out Notes
- The implemented slice adds `App\Support\Providers\TargetScope` as the bounded target-scope support layer for descriptors, normalization, provider-owned contextual identity metadata, and surface summaries.
- Provider connection create, edit, list, detail, onboarding, identity-resolution, verification, health-check, and shared audit paths now carry neutral `target_scope` truth beside `provider_identity_context` metadata.
- Existing Microsoft runtime truth remains intentionally bounded: `entra_tenant_id` is still the persisted/runtime provider column, while shared operator surfaces use `Target scope` or `Target scope ID` by default and show `Microsoft tenant ID` only as contextual provider-owned detail.
- Unsupported provider and target-scope combinations now fail explicitly through the normalizer and provider connection resolver instead of falling through to Microsoft defaults.
- Existing security-sensitive provider connection actions remain confirmation-gated and capability-gated; this slice adds guard coverage rather than changing the action model.
- Close-out disposition remains `document-in-feature`. Broader governed-subject, compare-boundary, second-provider runtime, and credential-model redesign work stays deferred to follow-up specs.
## Phase 1 — Agent Context Update
Run after artifact generation:
- `.specify/scripts/bash/update-agent-context.sh copilot`
## Implementation Strategy
### Phase A — Add the shared target-scope descriptor
**Goal**: Introduce one neutral shared description of what a provider connection points to.
| Step | File | Change |
|------|------|--------|
| A.1 | `apps/platform/app/Support/Providers/TargetScope/*` | Add a small target-scope descriptor and summary helper layer that can express `provider`, `scope_kind`, `scope_identifier`, and `scope_display_name` as neutral truth while carrying provider-owned contextual details only in companion metadata or summary shapes. |
| A.2 | `apps/platform/app/Services/Providers/ProviderConnectionResolution.php` and `ProviderIdentityResolution.php` | Stop treating Microsoft-shaped identity output as the default shared semantic contract and expose neutral target-scope data for shared surfaces. |
| A.3 | `apps/platform/tests/Unit/Providers/ProviderConnectionTargetScopeDescriptorTest.php` | Prove the descriptor stays neutral by default and still allows bounded Microsoft contextual metadata. |
### Phase B — Adopt the descriptor on shared provider connection surfaces
**Goal**: Replace Microsoft-shaped default labels and summaries on the existing shared resource surfaces.
| Step | File | Change |
|------|------|--------|
| B.1 | `apps/platform/app/Filament/Resources/ProviderConnectionResource.php` | Replace `Entra tenant ID` as the default shared field or summary label with neutral target-scope wording on form, infolist, and table surfaces while keeping Microsoft-specific detail contextual. |
| B.2 | `apps/platform/app/Services/Providers/ProviderConnectionStateProjector.php` or adjacent summary helpers | Align default-visible connection summaries with the same neutral descriptor used by the resource. |
| B.3 | `apps/platform/tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php` and existing Filament UI enforcement coverage | Prove list, detail, create, and edit surfaces show neutral target-scope truth first and preserve Microsoft-specific detail only contextually. |
### Phase C — Extend onboarding and mutation or audit wording
**Goal**: Keep setup, validation, and audit semantics aligned with the same shared contract.
| Step | File | Change |
|------|------|--------|
| C.1 | `apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php` | Reuse the same target-scope descriptor in the onboarding provider setup step so the operator sees the same neutral meaning before continuing. |
| C.2 | `apps/platform/app/Services/Providers/ProviderConnectionMutationService.php` and related audit writers | Align create or update validation messages and audit wording with neutral provider and target-scope vocabulary while preserving provider context. |
| C.3 | `apps/platform/tests/Feature/Audit/ProviderConnectionIdentityAuditTest.php` and `apps/platform/tests/Feature/ManagedTenantOnboardingWizardTest.php` | Prove audit entries and onboarding copy use the same neutral target-scope contract. |
### Phase D — Add guardrails and finish bounded drift protection
**Goal**: Keep the hotspot closed without widening into a broader provider rewrite.
| Step | File | Change |
|------|------|--------|
| D.1 | `apps/platform/tests/Unit/Providers/ProviderIdentityResolutionNeutralityTest.php` | Prove shared identity-resolution output no longer requires Microsoft-shaped default terms. |
| D.2 | `apps/platform/tests/Feature/Guards/ProviderConnectionNeutralityGuardTest.php` | Block new Microsoft-specific default labels, filters, validation messages, helper copy, or audit prose on shared provider connection surfaces unless the path is explicitly provider-owned. |
| D.3 | `specs/238-provider-identity-target-scope/quickstart.md` and later `tasks.md` | Keep the neutral-target-scope contract, bounded Microsoft contextual metadata, and no-marketplace/no-second-provider guardrail explicit. |
## Risks and Mitigations
- **Scope creep into Spec 137**: Connection-type and credential redesign could get pulled back into this slice. Mitigation: keep the implementation centered on shared target-scope semantics only and leave connection-type migration out of scope.
- **Over-abstracting the descriptor**: A broad multi-provider descriptor framework could appear. Mitigation: keep the new support layer small and only as rich as the currently touched provider connection surfaces require.
- **Audit or validation drift**: Shared UI labels may become neutral while audit prose or validation messages stay Microsoft-shaped. Mitigation: extend existing audit and mutation coverage in the same slice.
- **Hidden Microsoft default leakage**: A later surface might reintroduce `Entra tenant ID` as the default shared label. Mitigation: add one focused guard test for shared provider connection surfaces.
- **Onboarding mismatch**: Provider connection surfaces may become neutral while onboarding still shows Microsoft-first setup meaning. Mitigation: include the onboarding provider step in the first implementation slice and in validation coverage.
## Post-Design Re-check
Phase 0 and Phase 1 outputs keep the feature constitution-compliant, Filament v5 and Livewire v4 compliant, and intentionally narrow. The plan introduces no new persistence, no second provider runtime, no provider marketplace workflow, and no broad identity-migration framework. It is ready for `/speckit.tasks`.