TenantAtlas/specs/188-provider-connection-state-cleanup/contracts/provider-connection-state-cleanup.openapi.yaml
ahmido 1655cc481e Spec 188: canonical provider connection state cleanup (#219)
## Summary
- migrate provider connections to the canonical three-dimension state model: lifecycle via `is_enabled`, consent via `consent_status`, and verification via `verification_status`
- remove legacy provider status and health badge paths, update admin and system directory surfaces, and align onboarding, consent callback, verification, resolver, and mutation flows with the new model
- add the Spec 188 artifact set, schema migrations, guard coverage, and expanded provider-state tests across admin, system, onboarding, verification, and rendering paths

## Verification
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Auth/SystemPanelAuthTest.php tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php tests/Feature/ProviderConnections/ProviderConnectionEnableDisableTest.php tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php`
- integrated browser smoke: validated admin provider list/detail/edit, tenant provider summary, system directory tenant detail, provider-connection search exclusion, and cleaned up the temporary smoke record afterward

## Filament / implementation notes
- Livewire v4.0+ compliance: preserved; this change targets Filament v5 on Livewire v4 and does not introduce older APIs
- Provider registration location: unchanged; Laravel 11+ panel providers remain registered in `bootstrap/providers.php`
- Globally searchable resources: `ProviderConnectionResource` remains intentionally excluded from global search; tenant global search remains enabled and continues to resolve to view pages
- Destructive actions: no new destructive action surface was introduced without confirmation or authorization; existing capability checks continue to gate provider mutations
- Asset strategy: unchanged; no new Filament assets were added, so deploy behavior for `php artisan filament:assets` remains unchanged
- Testing plan covered: system auth, tenant global search, provider lifecycle enable/disable behavior, and provider truth cleanup cutover behavior

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #219
2026-04-10 11:22:56 +00:00

431 lines
13 KiB
YAML

openapi: 3.1.0
info:
title: Canonical Provider Connection State Cleanup Internal Contract
version: 0.1.0
summary: Internal planning contract for the hard cut from legacy provider status and health to canonical lifecycle, consent, and verification
description: |
This contract is an internal planning artifact for Spec 188. The affected
routes continue to render HTML. The schemas below describe the canonical
provider-state contract that must be derivable before rendering and before
runtime gates execute. Legacy `status` and `health_status` do not appear in
any schema because the feature requires a hard cut with no compatibility
payload.
servers:
- url: /internal
x-cutover-order:
- runtime_readers
- runtime_writers
- shared_presenters_and_badges
- factories_helpers_and_tests
- schema_removal
x-runtime-readers:
- surface: resolver_and_runtime_gates
guardScope:
- app/Services/Providers/ProviderConnectionResolver.php
- app/Jobs/ScanEntraAdminRolesJob.php
- app/Filament/Resources/ProviderConnectionResource.php
expectedContract:
- lifecycle_reads_is_enabled_only
- consent_inference_reads_consent_status_only
- verification_inference_reads_verification_status_only
- surface: tenant_and_system_summaries
guardScope:
- app/Filament/Resources/TenantResource.php
- resources/views/filament/infolists/entries/provider-connection-state.blade.php
- app/Filament/System/Pages/Directory/Tenants.php
- app/Filament/System/Pages/Directory/ViewTenant.php
- resources/views/filament/system/pages/directory/view-tenant.blade.php
expectedContract:
- helper_payload_has_no_legacy_status_keys
- system_rollups_derive_from_verification_and_permission_truth
- admin_and_system_planes_tell_the_same_provider_state_story
x-runtime-writers:
- surface: onboarding_and_consent_bootstrap
writerScope:
- app/Http/Controllers/TenantOnboardingController.php
- app/Http/Controllers/AdminConsentCallbackController.php
- app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php
expectedContract:
- initialize_is_enabled
- initialize_or_update_consent_status
- initialize_verification_status_without_legacy_projection
- surface: verification_and_health_check
writerScope:
- app/Services/Verification/StartVerification.php
- app/Jobs/ProviderConnectionHealthCheckJob.php
- app/Services/Providers/Contracts/HealthResult.php
- app/Services/Providers/MicrosoftProviderHealthCheck.php
- app/Services/Providers/ProviderConnectionStateProjector.php
expectedContract:
- verification_contract_uses_canonical_status
- verification_writes_update_only_canonical_fields_and_diagnostics
- surface: provider_mutations
writerScope:
- app/Services/Providers/ProviderConnectionMutationService.php
- app/Filament/Resources/ProviderConnectionResource.php
expectedContract:
- enable_disable_mutations_write_lifecycle_not_legacy_status
- credential_mutations_reset_or_block_verification_without_legacy_projection
x-legacy-removal-scope:
removed_columns:
- provider_connections.status
- provider_connections.health_status
removed_badge_domains:
- provider_connection.status
- provider_connection.health
removed_helper_keys:
- status
- health_status
paths:
/admin/provider-connections:
get:
summary: Render the canonical provider-connections list with lifecycle, consent, and verification as the only state axes
operationId: viewCanonicalProviderConnections
responses:
'200':
description: Provider-connections list rendered from canonical provider-state truth only
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.provider-connections-canonical+json:
schema:
$ref: '#/components/schemas/ProviderConnectionListTruthBundle'
'302':
description: Existing admin tenant-scoping redirects still apply
'404':
description: Workspace or tenant scope is outside entitlement
/admin/provider-connections/{record}:
get:
summary: Render canonical provider connection detail without legacy provider status or health
operationId: viewCanonicalProviderConnection
parameters:
- name: record
in: path
required: true
schema:
type:
- integer
- string
responses:
'200':
description: Provider connection detail rendered from lifecycle, consent, verification, and diagnostics only
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.provider-connection-canonical+json:
schema:
$ref: '#/components/schemas/ProviderConnectionDetailTruthModel'
'403':
description: Actor is in scope but lacks the capability required for protected actions
'404':
description: Provider connection is outside entitlement scope
/admin/provider-connections/{record}/edit:
get:
summary: Render canonical provider connection edit context with lifecycle, consent, and verification
operationId: editCanonicalProviderConnection
parameters:
- name: record
in: path
required: true
schema:
type:
- integer
- string
responses:
'200':
description: Provider connection edit rendered without legacy status or health fields
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.provider-connection-edit-canonical+json:
schema:
$ref: '#/components/schemas/ProviderConnectionDetailTruthModel'
'403':
description: Actor is in scope but lacks manage capability
'404':
description: Provider connection is outside entitlement scope
/admin/tenants/{tenant}:
get:
summary: Render tenant detail provider summary from canonical provider-state truth only
operationId: viewCanonicalTenantProviderSummary
parameters:
- name: tenant
in: path
required: true
schema:
type: string
responses:
'200':
description: Tenant detail rendered with a canonical provider summary and no legacy provider-state payload
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.tenant-provider-summary-canonical+json:
schema:
$ref: '#/components/schemas/TenantProviderSummary'
'404':
description: Tenant is outside entitlement scope
/system/directory/tenants:
get:
summary: Render system tenant directory with health rollups derived from canonical verification and permission truth
operationId: viewSystemTenantDirectoryCanonicalHealth
responses:
'200':
description: System directory list rendered without legacy provider health aggregation
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.system-tenant-directory-canonical+json:
schema:
$ref: '#/components/schemas/SystemTenantDirectoryTruthBundle'
'404':
description: Platform actor lacks directory access or tenant scope is unavailable
/system/directory/tenants/{tenant}:
get:
summary: Render system tenant detail with canonical provider rows
operationId: viewSystemTenantCanonicalProviderRows
parameters:
- name: tenant
in: path
required: true
schema:
type:
- integer
- string
responses:
'200':
description: System tenant detail rendered with canonical provider-state rows only
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.system-tenant-canonical-provider-rows+json:
schema:
$ref: '#/components/schemas/SystemTenantDetailTruthModel'
'404':
description: Platform actor lacks directory access or tenant is unavailable
components:
schemas:
LifecycleState:
type: string
enum:
- enabled
- disabled
ProviderConsentState:
type: string
enum:
- unknown
- required
- granted
- failed
- revoked
ProviderVerificationState:
type: string
enum:
- unknown
- pending
- healthy
- degraded
- blocked
- error
ProviderDiagnostics:
type: object
required:
- migrationReviewRequired
properties:
lastCheckedAt:
type:
- string
- 'null'
lastErrorReasonCode:
type:
- string
- 'null'
lastErrorMessage:
type:
- string
- 'null'
consentErrorCode:
type:
- string
- 'null'
consentErrorMessage:
type:
- string
- 'null'
migrationReviewRequired:
type: boolean
CanonicalProviderConnectionState:
type: object
required:
- lifecycle
- isEnabled
- consentStatus
- verificationStatus
- diagnostics
properties:
lifecycle:
$ref: '#/components/schemas/LifecycleState'
isEnabled:
type: boolean
consentStatus:
oneOf:
- $ref: '#/components/schemas/ProviderConsentState'
- type: 'null'
verificationStatus:
oneOf:
- $ref: '#/components/schemas/ProviderVerificationState'
- type: 'null'
diagnostics:
$ref: '#/components/schemas/ProviderDiagnostics'
ProviderConnectionRow:
type: object
required:
- id
- displayName
- provider
- connectionType
- isDefault
- state
properties:
id:
type: integer
tenantLabel:
type:
- string
- 'null'
displayName:
type: string
provider:
type: string
connectionType:
type: string
isDefault:
type: boolean
state:
$ref: '#/components/schemas/CanonicalProviderConnectionState'
ProviderConnectionListTruthBundle:
type: object
required:
- rows
properties:
rows:
type: array
items:
$ref: '#/components/schemas/ProviderConnectionRow'
ProviderConnectionDetailTruthModel:
type: object
required:
- id
- displayName
- provider
- connectionType
- isDefault
- state
properties:
id:
type: integer
displayName:
type: string
provider:
type: string
connectionType:
type: string
isDefault:
type: boolean
effectiveAppId:
type:
- string
- 'null'
effectiveAppSource:
type:
- string
- 'null'
state:
$ref: '#/components/schemas/CanonicalProviderConnectionState'
TenantProviderSummary:
type: object
required:
- state
- ctaUrl
- needsDefaultConnection
properties:
state:
type: string
enum:
- missing
- configured
- default_configured
ctaUrl:
type: string
needsDefaultConnection:
type: boolean
displayName:
type:
- string
- 'null'
provider:
type:
- string
- 'null'
canonicalState:
oneOf:
- $ref: '#/components/schemas/CanonicalProviderConnectionState'
- type: 'null'
SystemTenantDirectoryRow:
type: object
required:
- tenantId
- tenantLabel
- workspaceLabel
- providerConnectionsCount
- criticalProviderCount
- warningProviderCount
- missingPermissionCount
- systemHealth
properties:
tenantId:
type: integer
tenantLabel:
type: string
workspaceLabel:
type: string
providerConnectionsCount:
type: integer
criticalProviderCount:
type: integer
warningProviderCount:
type: integer
missingPermissionCount:
type: integer
systemHealth:
type: string
SystemTenantDirectoryTruthBundle:
type: object
required:
- rows
properties:
rows:
type: array
items:
$ref: '#/components/schemas/SystemTenantDirectoryRow'
SystemTenantDetailTruthModel:
type: object
required:
- tenantId
- tenantLabel
- providerConnections
properties:
tenantId:
type: integer
tenantLabel:
type: string
providerConnections:
type: array
items:
$ref: '#/components/schemas/ProviderConnectionRow'