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:
idtenant_idworkspace_idproviderentra_tenant_iddisplay_nameis_defaultconnection_typeis_enabledconsent_statusconsent_granted_atconsent_last_checked_atconsent_error_codeconsent_error_messageverification_statusmigration_review_requiredmigration_reviewed_atscopes_grantedlast_health_check_atlast_error_reason_codelast_error_messagemetadata
Removed fields:
statushealth_status
Relationships:
ProviderConnectionbelongs toTenantProviderConnectionbelongs toWorkspaceProviderConnectionhas oneProviderCredential
Validation rules:
is_enabledis the only persisted lifecycle truth and defaults totruefor newly created connections.consent_statusremains the only consent truth.verification_statusremains the only verification truth.statusandhealth_statusare 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_statusandverification_statusremain. 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:
ProviderCredentialbelongs toProviderConnection
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_statusreason_codemessagemeta
Validation rules:
- The contract must not carry legacy
statusorhealth_statusfields. - 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 = trueis_enabled = false
Operator-facing form:
EnabledDisabled
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:
unknownrequiredgrantedfailedrevoked
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:
unknownpendinghealthydegradedblockederror
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. |
Consent transitions
| 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 = trueconsent_status = requiredfor pre-consent onboarding startsverification_status = unknown- diagnostics cleared or initialized from the current event
Consent callback
Expected persisted shape:
- lifecycle unchanged or initialized to
is_enabled = true consent_statusupdated from callback outcomeverification_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_statusupdated only when the result proves a consent consequenceverification_statusupdated from the canonical health result- error and recency diagnostics refreshed
Enable and disable actions
Expected persisted shape:
- Disable writes only
is_enabled = falseplus any audit metadata that records the lifecycle change. - Enable writes
is_enabled = trueand resets stale verification truth without fabricating a healthy state. - When enabling reveals a local blocker such as missing credentials,
verification_statusbecomesblockedand 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
unknownorblockeddepending 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
statusorhealth_statuskeys. - 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_statusvalues. - 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
statusandhealth_statusare removed from the database schema, Eloquent model interactions, helper arrays, Blade views, badge domains, query filters, and audit metadata.ProviderConnection::classificationProjection()and any equivalent projection helpers no longer emit removed fields.- Runtime gates read lifecycle from
is_enabled, not from any verification or consent surrogate. - 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.