TenantAtlas/specs/188-provider-connection-state-cleanup/data-model.md
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

12 KiB

Phase 1 Data Model: Canonical Provider Connection State Cleanup

Overview

This feature removes the persisted legacy provider-state columns status and health_status and replaces their last remaining behavioral responsibility with one explicit lifecycle truth: is_enabled. Consent and verification remain on their existing canonical enums. No new table, no new persisted summary artifact, and no new cross-domain provider-state framework are introduced.

Persistent Source Truths

ProviderConnection

Purpose: Canonical tenant-owned provider integration record for provider lifecycle, consent, verification, and supporting diagnostics.

Key fields:

  • id
  • tenant_id
  • workspace_id
  • provider
  • entra_tenant_id
  • display_name
  • is_default
  • connection_type
  • is_enabled
  • consent_status
  • consent_granted_at
  • consent_last_checked_at
  • consent_error_code
  • consent_error_message
  • verification_status
  • migration_review_required
  • migration_reviewed_at
  • scopes_granted
  • last_health_check_at
  • last_error_reason_code
  • last_error_message
  • metadata

Removed fields:

  • status
  • health_status

Relationships:

  • ProviderConnection belongs to Tenant
  • ProviderConnection belongs to Workspace
  • ProviderConnection has one ProviderCredential

Validation rules:

  • is_enabled is the only persisted lifecycle truth and defaults to true for newly created connections.
  • consent_status remains the only consent truth.
  • verification_status remains the only verification truth.
  • status and health_status are not recreated through accessors, casts, appended attributes, helper arrays, or audit context shims.
  • Existing unique constraints on tenant, provider, and Entra tenant identity remain unchanged.
  • Existing indexes for consent_status and verification_status remain. Legacy indexes tied to removed columns are dropped. No new lifecycle index is introduced unless implementation profiling proves it necessary.

ProviderCredential

Purpose: Encrypted credential material associated with a provider connection.

Key fields:

  • provider_connection_id
  • encrypted credential payload fields

Relationships:

  • ProviderCredential belongs to ProviderConnection

Validation rules:

  • This feature does not change credential storage or encryption.
  • Credential presence remains a diagnostic or blocker input for lifecycle-safe verification semantics, not a fourth provider-state dimension.

HealthResult (internal contract)

Purpose: Canonical result object emitted by provider health checks before persistence.

Key fields:

  • verification_status
  • reason_code
  • message
  • meta

Validation rules:

  • The contract must not carry legacy status or health_status fields.
  • The contract may carry only the minimum data needed to derive canonical consent and verification outcomes plus diagnostics.

Canonical State Families

Lifecycle (derived from is_enabled)

Persisted form:

  • is_enabled = true
  • is_enabled = false

Operator-facing form:

  • Enabled
  • Disabled

Rule:

  • Lifecycle answers only whether the connection is administratively allowed to operate.
  • Lifecycle does not imply consent and does not imply the latest verification outcome.

ProviderConsentStatus

Values:

  • unknown
  • required
  • granted
  • failed
  • revoked

Rule:

  • Consent answers only whether required provider consent currently exists and whether consent-specific failure or revocation was detected.
  • Consent does not imply lifecycle and does not imply technical verification success.

ProviderVerificationStatus

Values:

  • unknown
  • pending
  • healthy
  • degraded
  • blocked
  • error

Rule:

  • Verification answers only what the latest verification attempt or local canonical blocker currently proves.
  • Verification does not imply lifecycle and does not rewrite consent.

State Transitions

Lifecycle transitions

From To Trigger Notes
true false Explicit disable action Consent and verification remain intact as separate truths.
false true Explicit enable action Lifecycle resumes without fabricating consent or a healthy verification state.
From To Trigger Notes
unknown or required granted Successful admin consent callback or successful verification that proves consent still exists Does not change lifecycle.
granted revoked Verification detects consent was revoked Verification may become blocked, but lifecycle remains separate.
any failed Consent-specific failure during callback or verification Does not disable the connection.

Verification transitions

From To Trigger Notes
unknown pending Verification start Existing start surfaces remain authoritative.
pending healthy, degraded, blocked, or error Verification completion Derived from the health-check result and current consent truth.
any unknown New connection creation, successful enable with present credentials, or credential mutation that invalidates prior verification but does not prove a blocker Keeps stale success from surviving a material configuration change.
any blocked Local or remote evidence proves a blocker, such as missing credentials, invalid connection type, consent missing, or review-required state Blocked remains a verification consequence, not a lifecycle substitute.

Mutation Rules

Connection creation and onboarding bootstrap

Expected persisted shape:

  • is_enabled = true
  • consent_status = required for pre-consent onboarding starts
  • verification_status = unknown
  • diagnostics cleared or initialized from the current event

Expected persisted shape:

  • lifecycle unchanged or initialized to is_enabled = true
  • consent_status updated from callback outcome
  • verification_status = unknown
  • consent and error timestamps refreshed

Verification start

Expected persisted shape:

  • lifecycle unchanged
  • consent unchanged
  • verification_status = pending
  • legacy status or health projections are not written

Verification completion / health check

Expected persisted shape:

  • lifecycle unchanged
  • consent_status updated only when the result proves a consent consequence
  • verification_status updated from the canonical health result
  • error and recency diagnostics refreshed

Enable and disable actions

Expected persisted shape:

  • Disable writes only is_enabled = false plus any audit metadata that records the lifecycle change.
  • Enable writes is_enabled = true and resets stale verification truth without fabricating a healthy state.
  • When enabling reveals a local blocker such as missing credentials, verification_status becomes blocked and the blocker reason is recorded in diagnostics.

Credential-source and mutation-service changes

Expected persisted shape:

  • lifecycle unchanged unless the operator explicitly disables the connection
  • consent unchanged unless the mutation directly changes consent evidence
  • verification reset to unknown or blocked depending on whether the mutation invalidates prior verification or proves a blocker

Derived Surface Contracts

Canonical provider state bundle

Purpose: Shared derived state used by provider detail, edit, tenant summaries, and system read-only views.

Field Type Required Description
lifecycle string yes enabled or disabled, derived from is_enabled
isEnabled boolean yes Raw persisted lifecycle truth
consentStatus string nullable yes Canonical consent status
verificationStatus string nullable yes Canonical verification status
connectionType string yes Platform or dedicated
isDefault boolean yes Default designation
lastCheckedAt string nullable no Stored verification recency
lastErrorReasonCode string nullable no Latest diagnostic reason
lastErrorMessage string nullable no Latest diagnostic message
migrationReviewRequired boolean yes Existing migration-review diagnostic

Validation rules:

  • No helper array or view model may expose status or health_status keys.
  • Diagnostics remain subordinate to lifecycle, consent, and verification.

Tenant provider summary

Purpose: Compact provider summary rendered from TenantResource::providerConnectionState() and the shared provider-state Blade entry.

Field Type Required Description
state string yes missing, configured, or default_configured
cta_url string yes Canonical provider-connections destination
needs_default_connection boolean yes Signals action needed without inventing provider health
display_name string nullable no Chosen connection display name
provider string nullable no Provider label
is_enabled boolean nullable no Raw lifecycle truth when a connection exists
consent_status string nullable no Canonical consent status
verification_status string nullable no Canonical verification status
last_health_check_at string nullable no Stored verification recency
last_error_reason_code string nullable no Latest diagnostic reason

Validation rules:

  • Missing or non-default states must never read as healthy or ready.
  • The summary must not return removed legacy keys for compatibility.

System tenant health rollup

Purpose: Read-only system-directory aggregate used to derive SystemHealth badges.

Field Type Required Description
tenantStatus string yes Existing tenant lifecycle/status input
criticalProviderCount integer yes Count of each tenant's default Microsoft provider connection when its verification_status is blocked or error
warningProviderCount integer yes Count of each tenant's default Microsoft provider connection when its verification_status = degraded
missingPermissionCount integer yes Existing tenant-permission signal

Validation rules:

  • System health rollups must not query removed health_status values.
  • Tenant-directory list rollups count only the default Microsoft provider connection for each tenant. Non-default connections may still appear on the system tenant detail page, but they do not affect list rollup counts.
  • The same provider connection must produce semantically aligned admin and system readings.

Removal Rules

  1. status and health_status are removed from the database schema, Eloquent model interactions, helper arrays, Blade views, badge domains, query filters, and audit metadata.
  2. ProviderConnection::classificationProjection() and any equivalent projection helpers no longer emit removed fields.
  3. Runtime gates read lifecycle from is_enabled, not from any verification or consent surrogate.
  4. Diagnostics remain stored and displayed only as supporting facts, not as another state language.

No New Persistence Beyond The Narrow Lifecycle Field

  • No new table is introduced.
  • No new provider-readiness summary is persisted.
  • No new enum class is introduced for lifecycle; the persisted truth is a boolean and the operator-facing labels are derived.
  • The cleanup removes more persisted truth than it adds: two legacy columns out, one lifecycle column in.