TenantAtlas/specs/283-provider-capability-registry/plan.md
ahmido 1debe40ced feat: implement provider capability registry (#342)
## Summary
- implement the provider capability registry and derived capability evaluation flow
- update provider connections, onboarding, required-permissions diagnostics, and provider blocker translation to use capability-first summaries
- add bounded unit, feature, and browser test coverage plus the prepared Spec 283 artifacts

## Notes
- branch: `283-provider-capability-registry`
- commit: `74e75c3e`
- no additional validation commands were run in this git/PR flow step

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #342
2026-05-08 21:17:05 +00:00

27 KiB

Implementation Plan: Provider Capability Registry

Branch: 283-provider-capability-registry | Date: 2026-05-08 | Spec: spec.md Input: Feature specification from specs/283-provider-capability-registry/spec.md

Summary

Prepare the next reserved provider-boundary slice by introducing one provider capability registry and derived capability-evaluation path over the repo's existing provider connection, required-permissions, onboarding, blocked-operation, and support-diagnostic seams. The narrow implementation path reuses the current provider operation registry, provider operation start gate, required-permissions matrix, provider reason translation, provider-connections resource, onboarding wizard, and support links while explicitly rejecting a provider-capability table, a provider framework, a user RBAC rewrite, a broader taxonomy, and adjacent cutover work from Specs 284 through 287.

This plan is intentionally bounded. Filament remains v5 on Livewire v4, provider registration remains in apps/platform/bootstrap/providers.php, ProviderConnectionResource remains non-globally-searchable, existing destructive provider-connection actions stay confirmation-protected and server-authorized, and raw Graph permission names remain provider-owned evidence rather than the new primary operator vocabulary.

Inherited Baseline / Explicit Delta

Inherited baseline

  • Spec 279 already completed the managed-environment core cutover and is historical prerequisite context only.
  • Spec 280 already prepared the workspace-first shell and remains separate adjacent context only.
  • Spec 281 already prepared the provider-neutral target-scope and provider-identity baseline and is an implementation prerequisite for 283.
  • apps/platform/app/Filament/Resources/ProviderConnectionResource.php already exists with List, Create, View, and Edit pages, remains protected static bool $isGloballySearchable = false;, and already groups mutating actions behind confirmation-protected Filament actions.
  • apps/platform/app/Filament/Pages/TenantRequiredPermissions.php already lives under the workspace-first managed-environment route shell and already keeps its actions inside the page body rather than the page header.
  • apps/platform/app/Support/Verification/TenantPermissionCheckClusters.php already groups raw permission rows into diagnostic clusters such as admin consent, directory/groups, Intune configuration, apps, RBAC, and scripts, but it does not yet publish workflow capability truth.
  • apps/platform/app/Services/Providers/ProviderOperationRegistry.php already maps operation types to user RBAC capability requirements and provider bindings, but it does not yet map those operations to provider application capabilities.
  • apps/platform/app/Services/Providers/ProviderOperationStartGate.php already blocks or starts provider-backed work through one shared path, but it still explains missing prerequisites through reason codes and raw requirement detail rather than a stable capability contract.
  • apps/platform/app/Support/Providers/ProviderReasonTranslator.php, contextual-help catalog or resolver seams, and support-diagnostic consumers already translate provider blockers and route operators to RequiredPermissionsLinks, but they still lead with provider-specific requirement language in several cases.
  • apps/platform/app/Models/ProviderConnection.php already persists consent state, verification state, scopes_granted, and metadata, so 283 must treat provider capability state as derived truth rather than add another table.

Explicit delta in this plan

  • Introduce one bounded provider capability definition and evaluation layer over the existing provider connection, required-permissions, and blocked-operation seams.
  • Keep provider capability state derived and request-time or snapshot-derived; do not add a provider-capability table or ledger.
  • Map the current provider-backed operation types to explicit provider capability keys.
  • Reuse the required-permissions page as the canonical diagnostic deep dive, but group or summarize raw requirement rows under shared capability headings and statuses.
  • Reuse ProviderConnectionResource and ManagedTenantOnboardingWizard as the operator-facing summary consumers for the new capability contract.
  • Reuse ProviderReasonTranslator, ProviderNextStepsRegistry, and existing support or product-knowledge links so blocked-operation and diagnostic guidance adopts the same capability vocabulary.
  • Keep raw Graph permission names, Intune RBAC prerequisite detail, consent links, and provider portal metadata nested inside provider-owned remediation or evidence blocks.
  • Keep Specs 284 through 287 explicitly deferred.

Technical Context

Language/Version: PHP 8.4.15, Laravel 12.52
Primary Dependencies: Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, existing provider operation, provider reason, onboarding, and required-permissions seams
Storage: PostgreSQL, no new persistence or schema change in this slice
Testing: Pest unit tests, Pest feature tests, and one Pest browser smoke
Validation Lanes: fast-feedback, confidence, browser
Target Platform: Laravel monolith in apps/platform
Project Type: web application
Performance Goals: preserve current provider-connection, onboarding, and required-permissions responsiveness while changing only derived capability evaluation and summary presentation; no new remote inline work or asset path is introduced
Constraints: no provider-capability table, no provider framework, no user RBAC changes, no route-cutover work from Spec 280, no provider-identity extraction work from Spec 281, provider registration stays in apps/platform/bootstrap/providers.php, and ProviderConnectionResource stays non-globally-searchable
Scale/Scope: one shared capability contract over the existing Microsoft provider implementation and current provider-backed workflows only

Likely Affected Repo Surfaces

  • apps/platform/app/Services/Providers/ProviderOperationRegistry.php
  • apps/platform/app/Services/Providers/ProviderOperationStartGate.php
  • apps/platform/app/Support/Providers/ProviderReasonCodes.php
  • apps/platform/app/Support/Providers/ProviderReasonTranslator.php
  • apps/platform/app/Support/Providers/ProviderNextStepsRegistry.php
  • apps/platform/app/Support/Verification/TenantPermissionCheckClusters.php
  • apps/platform/app/Services/Intune/TenantRequiredPermissionsViewModelBuilder.php
  • apps/platform/app/Filament/Pages/TenantRequiredPermissions.php
  • apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php
  • apps/platform/app/Filament/Resources/ProviderConnectionResource.php
  • apps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.php
  • apps/platform/app/Support/Links/RequiredPermissionsLinks.php
  • apps/platform/app/Support/ProductKnowledge/ContextualHelpCatalog.php
  • apps/platform/app/Support/ProductKnowledge/ContextualHelpResolver.php
  • apps/platform/app/Support/TenantDashboard/TenantDashboardSummaryBuilder.php if that surface already consumes the shared provider prerequisite explanation and must stay aligned without becoming a separate dashboard initiative
  • apps/platform/config/provider_boundaries.php
  • apps/platform/config/intune_permissions.php only if the implementation needs to read existing provider-owned requirement identifiers from the current config rather than re-declare them locally
  • new bounded support files under apps/platform/app/Support/Providers/Capabilities/ only if implementation needs a small dedicated namespace for the registry, status enum, and evaluator
  • representative proof files under apps/platform/tests/Unit/Providers/, apps/platform/tests/Unit/Verification/, apps/platform/tests/Feature/Providers/, apps/platform/tests/Feature/Filament/, apps/platform/tests/Feature/Onboarding/, apps/platform/tests/Feature/RequiredPermissions/, apps/platform/tests/Feature/SupportDiagnostics/, and apps/platform/tests/Browser/

Filament v5 / Capability Surface Notes

  • Livewire v4.0+ compliance: all touched Filament work remains on Filament v5 with Livewire v4.
  • Provider registration location: provider registration stays in apps/platform/bootstrap/providers.php; nothing moves to bootstrap/app.php.
  • Global search rule: ProviderConnectionResource remains non-globally-searchable and keeps its View and Edit pages. No new searchable resource is introduced by this slice.
  • Destructive actions: touched provider-connection mutations keep the existing ->action(...), ->requiresConfirmation(), and server-authorization contracts. Grant admin consent remains navigation-only.
  • Asset strategy: no new asset registration or deploy-step change is planned.

Provider Capability Contract Fit

  • Introduce one bounded capability definition source for current-release provider-backed workflows only.
  • Keep the shared capability-key inventory limited to the workflows already present in repo truth:
    • provider_connection_check
    • inventory_read
    • configuration_read
    • restore_execute
    • directory_groups_read
    • directory_role_definitions_read
  • Keep capability status values limited to the derived family from the spec: supported, missing, blocked, unknown, and not_applicable.
  • Keep capability definitions platform-core, but keep provider requirement mappings provider-owned. That means capability definitions can point to provider-owned requirement keys such as cluster identifiers or Graph permission names without promoting those raw identifiers into the top-level operator vocabulary.
  • Prefer one small code-first registry plus evaluator namespace over a new config-first framework or a new table. If implementation discovers that one small code-first registry cannot stay reviewable, that change must be re-justified rather than added silently.
  • Reuse existing permission-cluster evidence and provider-connection state as inputs. The capability layer should not replace those inputs; it should standardize how they are interpreted by workflow consumers.

UI / Surface Guardrail Plan

  • Guardrail scope: changed surfaces
  • Native vs custom classification summary: mixed native Filament resource plus existing custom onboarding wizard
  • Shared-family relevance: provider summary, blocked guidance, onboarding readiness, required-permissions diagnostics, support translation
  • State layers in scope: page, detail, modal, Livewire state, URL-query
  • Audience modes in scope: operator-MSP, support-platform
  • Decision/diagnostic/raw hierarchy plan: capability-first summary, diagnostics-second evidence, provider-raw-third remediation detail
  • Raw/support gating plan: provider-specific requirement detail stays nested or lower-priority; raw permission rows stay on the diagnostic page rather than the top-level summary
  • One-primary-action / duplicate-truth control: provider-connections and onboarding surface one dominant next step from the capability result and delegate the full proof to the required-permissions page
  • Handling modes by drift class or surface: review-mandatory
  • Repository-signal treatment: review-mandatory until provider-connections, onboarding, required-permissions, and blocked-operation messaging all use the same capability labels
  • Special surface test profiles: standard-native-filament, workflow-hub, shared-detail-family
  • Required tests or browser smoke: functional-core, state-contract, browser-smoke
  • Exception path and spread control: none; the slice removes explanation drift rather than adding a new exception
  • Active feature PR close-out entry: Guardrail

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched: provider operation registry, provider operation start gate, required-permissions diagnostics, onboarding readiness, provider connection summaries, provider reason translation, contextual help, and support-diagnostic guidance
  • Shared abstractions reused: ProviderOperationRegistry, ProviderOperationStartGate, ProviderReasonTranslator, ProviderNextStepsRegistry, TenantPermissionCheckClusters, TenantRequiredPermissionsViewModelBuilder, ProviderConnectionSurfaceSummary, and RequiredPermissionsLinks
  • New abstraction introduced? why?: one small registry plus evaluator namespace is expected because multiple existing shared consumers need one stable capability contract and no current shared abstraction owns that concept yet
  • Why the existing abstraction was sufficient or insufficient: existing abstractions already own provider connection state, permission evidence, or blocked-operation explanation, but none of them own the workflow-capability concept across all current consumers
  • Bounded deviation / spread control: provider-owned Graph permission names, Intune RBAC detail, consent links, and portal metadata remain nested evidence only and must not define the shared capability contract

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: yes
  • Central contract reused: ProviderOperationStartGate, ProviderOperationStartResultPresenter, OperationUxPresenter, and the current OperationRunService lifecycle path
  • Delegated UX behaviors: blocked-versus-started behavior, queued intent messaging, run links, capability-driven remediation hints, and provider-safe follow-up routing stay delegated to the existing shared provider-operation path
  • Surface-owned behavior kept local: ProviderConnectionResource and ManagedTenantOnboardingWizard keep only initiation affordances, summary placement, and assist entry points
  • Queued DB-notification policy: N/A
  • Terminal notification path: existing central lifecycle mechanism
  • Exception path: none

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes
  • Provider-owned seams: raw Graph permission names, Intune RBAC remediation detail, admin-consent links, required-permissions URLs, portal links, and any provider-specific troubleshooting metadata
  • Platform-core seams: capability definitions, capability status evaluation, operation-to-capability mapping, blocked-operation capability summary, shared capability labels, and shared diagnostic grouping
  • Neutral platform terms / contracts preserved: provider capability, capability key, capability status, provider connection, managed environment, target workflow, and provider requirement
  • Retained provider-specific semantics and why: the current Microsoft provider still needs its own raw permission names, consent guidance, and Intune RBAC detail for operators to fix blockers. Those details remain explicit provider-owned evidence.
  • Bounded extraction or follow-up path: no broader taxonomy or multi-provider framework work in this slice; that remains with Specs 284 through 287

Constitution Check

GATE: Must pass before implementation begins and again after design artifacts are complete.

  • Inventory-first / snapshot truth: PASS. The slice derives capability truth from existing provider and permission evidence.
  • Read/write separation: PASS. No new remote-write workflow is introduced.
  • Graph contract path: PASS. No new Graph endpoint or contract-registry work is added.
  • Deterministic capabilities: PASS with implementation condition. Capability evaluation must be testable and deterministic from the existing provider and permission inputs.
  • RBAC-UX plane separation: PASS. /admin versus /system remains unchanged.
  • Workspace isolation: PASS. Workspace membership remains the first boundary.
  • Managed-environment isolation: PASS. Managed-environment entitlement remains the second boundary.
  • Destructive action discipline: PASS by preservation. Existing confirmation-protected provider-connection mutations remain unchanged.
  • Global search safety: PASS. ProviderConnectionResource stays non-globally-searchable.
  • OperationRun / Ops-UX: PASS. The slice reuses the shared provider-operation start path and changes only its prerequisite contract.
  • Data minimization: PASS. No new persistence or provider-capability ledger is introduced.
  • Test governance: PASS. Proof stays bounded to unit, feature, and one browser smoke.
  • Proportionality / no premature abstraction: PASS with implementation condition. Any new support namespace must stay narrow and current-release only.
  • Persisted truth / behavioral state: PASS. One new derived state family is introduced; no new persistence is introduced.
  • UI semantics / shared pattern first / Filament-native UI: PASS. Existing resource, page, and wizard surfaces remain the primary operator paths.
  • Provider boundary: PASS with implementation condition. Raw provider requirement detail must remain secondary evidence rather than reappearing as the primary shared label set.

Gate evaluation: PASS.

Post-design re-check: PASS while research.md, data-model.md, quickstart.md, contracts/provider-capability-registry.logical.openapi.yaml, and checklists/requirements.md stay aligned on the same capability keys, status family, derived-truth posture, and proof commands.

Test Governance Check

  • Test purpose / classification by changed surface: Unit, Feature, Browser
  • Affected validation lanes: fast-feedback, confidence, browser
  • Why this lane mix is the narrowest sufficient proof: the registry and evaluator logic are pure derivation and deserve unit proof; the shared start gate, provider-connections, onboarding, required-permissions page, and support translation need feature proof; one browser smoke is enough to prove the real operator path from provider connection or onboarding into the diagnostic page
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Unit/Providers/ProviderCapabilityRegistryTest.php tests/Unit/Verification/TenantPermissionCapabilityMappingTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/Providers/ProviderCapabilityEvaluationTest.php tests/Feature/Providers/ProviderOperationCapabilityGateTest.php tests/Feature/Filament/ProviderConnectionCapabilitySummaryTest.php tests/Feature/Onboarding/ManagedTenantOnboardingCapabilityAssistTest.php tests/Feature/RequiredPermissions/RequiredPermissionsCapabilityGroupingTest.php tests/Feature/SupportDiagnostics/ProviderCapabilityReasonTranslationTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec283ProviderCapabilityRegistrySmokeTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail bin pint --dirty --format agent)
  • Fixture / helper / factory / seed / context cost risks: moderate because proof needs workspace, managed environment, provider connection, permission evidence, and blocked-operation context without widening shared fixture defaults
  • Expensive defaults or shared helper growth introduced?: no; any new capability test helper should stay feature-local and opt-in
  • Heavy-family additions, promotions, or visibility changes: none beyond one bounded browser smoke
  • Surface-class relief / special coverage rule: standard-native-filament relief for provider-connections and required-permissions; one workflow-hub smoke for onboarding continuity
  • Closing validation and reviewer handoff: rerun the commands above, verify that no provider-capability table appears, verify that the registry stays current-release-only, verify that provider-connections and onboarding use the same capability labels, verify that the required-permissions page groups evidence under those same labels, verify that blocked-operation context carries capability information, and verify that raw Graph permission names remain secondary evidence
  • Budget / baseline / trend follow-up: contained feature-local increase only
  • Review-stop questions: did the implementation add persistence, did it invent future-facing capability keys, did it widen into RBAC or taxonomy work, did any touched surface keep a page-local permission vocabulary, did blocked-operation context stay on reason-only or raw permission language
  • Escalation path: reject-or-split if implementation introduces persistence, a provider framework, a user RBAC rewrite, or broader cutover work from adjacent specs
  • Active feature PR close-out entry: Guardrail
  • Why no dedicated follow-up spec is needed: adjacent follow-up work already exists as Specs 284 through 287; 283 only needs the bounded capability slice itself

Review Checklist Status

  • Review checklist artifact: checklists/requirements.md
  • Review outcome class: implementation-ready
  • Workflow outcome: keep
  • Test-governance outcome: keep
  • Escalation rule: if implementation adds persistence, broad future-facing keys, or adjacent-spec scope, flip the workflow outcome to split or reject-or-split

Rollout Considerations

  • Land the registry definitions, evaluator, and shared consumer updates as one bounded slice so provider-connections, onboarding, required-permissions diagnostics, and provider-operation blocking all converge atomically.
  • Update the shared evaluation and translation seams before polishing page copy so the touched surfaces inherit the same contract rather than reformatting raw evidence separately.
  • Keep provider-owned evidence and remediation nested from the start; otherwise the later UI pass will still have to untangle raw Microsoft-first summaries.
  • Keep dashboard or support surfaces limited to consumers of the shared capability contract and do not let them grow into a separate productization initiative in this slice.

Risk Controls

  • Reject any implementation that introduces a provider-capability table, ledger, or snapshot model.
  • Reject any implementation that creates a future-facing provider framework or tries to solve multi-provider routing, taxonomy, or UI copy in the same slice.
  • Reject any implementation that leaves provider-connections, onboarding, and required-permissions diagnostics on different capability or requirement vocabularies.
  • Reject any implementation that collapses user RBAC capability failures into provider application capability failures.
  • Reject any implementation that promotes raw Graph permission names back into the primary operator summary on touched surfaces.

Research & Design Outputs

  • research.md records the bounded derivation decisions, the capability-key inventory, the provider-owned evidence rules, and the rejected alternatives.
  • data-model.md captures the derived capability definition, result, evidence, grouped diagnostic view, and operation-gate contracts.
  • quickstart.md gives reviewers the bounded proof flow and exact commands.
  • contracts/provider-capability-registry.logical.openapi.yaml models the logical capability summary, diagnostic grouping, onboarding assist, support explanation, and operation-start contract.
  • checklists/requirements.md records package readiness, boundedness, and outcome state.

Project Structure

Documentation (this feature)

specs/283-provider-capability-registry/
├── checklists/
│   └── requirements.md
├── contracts/
│   └── provider-capability-registry.logical.openapi.yaml
├── data-model.md
├── plan.md
├── quickstart.md
├── research.md
├── spec.md
└── tasks.md

Source Code (expected implementation surfaces)

apps/platform/
├── app/
│   ├── Filament/
│   │   ├── Pages/
│   │   │   ├── TenantRequiredPermissions.php
│   │   │   └── Workspaces/
│   │   │       └── ManagedTenantOnboardingWizard.php
│   │   └── Resources/
│   │       └── ProviderConnectionResource.php
│   ├── Models/
│   │   └── ProviderConnection.php
│   ├── Services/
│   │   ├── Intune/
│   │   │   └── TenantRequiredPermissionsViewModelBuilder.php
│   │   └── Providers/
│   │       ├── ProviderOperationRegistry.php
│   │       └── ProviderOperationStartGate.php
│   └── Support/
│       ├── Links/
│       │   └── RequiredPermissionsLinks.php
│       ├── ProductKnowledge/
│       │   ├── ContextualHelpCatalog.php
│       │   └── ContextualHelpResolver.php
│       ├── Providers/
│       │   ├── ProviderNextStepsRegistry.php
│       │   ├── ProviderReasonCodes.php
│       │   ├── ProviderReasonTranslator.php
│       │   ├── TargetScope/
│       │   │   └── ProviderConnectionSurfaceSummary.php
│       │   └── Capabilities/
│       │       ├── ProviderCapabilityRegistry.php
│       │       ├── ProviderCapabilityResult.php
│       │       ├── ProviderCapabilityStatus.php
│       │       └── ProviderCapabilityEvaluator.php
│       ├── TenantDashboard/
│       │   └── TenantDashboardSummaryBuilder.php
│       └── Verification/
│           └── TenantPermissionCheckClusters.php
├── config/
│   ├── intune_permissions.php
│   └── provider_boundaries.php
└── tests/
    ├── Browser/
    │   └── Spec283ProviderCapabilityRegistrySmokeTest.php
    ├── Feature/
    │   ├── Filament/
    │   │   └── ProviderConnectionCapabilitySummaryTest.php
    │   ├── Onboarding/
    │   │   └── ManagedTenantOnboardingCapabilityAssistTest.php
    │   ├── Providers/
    │   │   ├── ProviderCapabilityEvaluationTest.php
    │   │   └── ProviderOperationCapabilityGateTest.php
    │   ├── RequiredPermissions/
    │   │   └── RequiredPermissionsCapabilityGroupingTest.php
    │   └── SupportDiagnostics/
    │       └── ProviderCapabilityReasonTranslationTest.php
    └── Unit/
        ├── Providers/
        │   └── ProviderCapabilityRegistryTest.php
        └── Verification/
            └── TenantPermissionCapabilityMappingTest.php

Structure Decision: keep the implementation inside apps/platform and add only one small support namespace for provider capability definitions and derived evaluation. Reuse existing Filament, provider-operation, required-permissions, and support-diagnostic seams instead of creating a new module or package.