# Data Model: Provider-Backed Action Preflight and Dispatch Gate Unification ## Overview This feature does not introduce new persisted entities or tables. It extends the existing provider-backed start contract around `ProviderConnection`, `OperationRun`, and onboarding draft state so every covered operator-triggered start follows the same queue-admission, conflict-protection, and operator-feedback rules. The key design constraint is that start truth remains service-owned and derived from existing runtime records: - provider readiness and connection identity from `ProviderConnection` plus `ProviderConnectionResolver` - accepted or prevented work truth from `OperationRun` - click-time queue-admission decisions from `ProviderOperationStartGate` - onboarding bootstrap continuity from existing `TenantOnboardingSession.state` - operator feedback from existing Ops UX helpers plus one thin shared provider-start presentation helper ## Existing Persistent Inputs ### 1. ProviderConnection - Purpose: Tenant-owned provider credential and readiness record that defines which delegated connection a provider-backed operation can use. - Key persisted fields used by this feature: - `id` - `tenant_id` - `provider` - `entra_tenant_id` - Existing runtime facts consumed through current services: - default-vs-explicit selection - consent readiness - credential usability - provider identity and scope targeting - Relationships used by this feature: - owning tenant - related operation runs through `OperationRun.context.provider_connection_id` ### 2. OperationRun - Purpose: Canonical operational truth for queued or executed provider-backed work and for blocked preflight attempts that must remain observable. - Key persisted fields used by this feature: - `id` - `tenant_id` - `type` - `status` - `outcome` - `reason_code` - `context` - `summary_counts` - `started_at` - `completed_at` - Existing relationships or references used by this feature: - initiator user - tenant scope - canonical Monitoring → Operations detail route ### 3. TenantOnboardingSession - Purpose: Workspace-owned onboarding workflow record that already stores onboarding progress and step state, including bootstrap operation selections and related run references. - Key persisted fields used by this feature: - `id` - `workspace_id` - `tenant_id` (nullable until attached) - `current_step` - `state` - Existing state keys used by this feature: - `bootstrap_operation_types` - `bootstrap_operation_runs` - Relationships used by this feature: - workspace - attached tenant when present ### 4. RestoreRun - Purpose: Tenant-owned restore execution record whose execute action becomes part of the canonical provider-backed start contract. - Key persisted fields used by this feature: - `id` - `tenant_id` - restore configuration and preview state already captured before execution - Relationships used by this feature: - tenant - backup source records and restore safety flow already owned by existing restore logic ## Existing Service-Owned Inputs ### A. ProviderOperationRegistry Entry This is an existing logical definition, not a new persisted entity. | Field | Meaning | |---|---| | `operationType` | The write-time operation type string admitted by the gate | | `module` | Current operation family/module metadata used in run context | | `dispatcher` | The queue-dispatch callback or equivalent start hook | | `requiredCapability` | Capability gate required to start the operation | | `providerScopeExpectation` | Whether the operation is connection-scoped and therefore protected by click-time conflict rules | Rules: - First-slice coverage adds entries for every covered action host instead of introducing a second registry. - This feature does not normalize historical operation names; registry entries follow current write-time operation types. ### B. ProviderConnectionResolution Logical output of `ProviderConnectionResolver` and `ProviderOperationStartGate`. | Field | Meaning | |---|---| | `providerConnectionId` | Explicit resolved connection identity when admission is possible | | `provider` | Provider family used for copy and downstream dispatch | | `targetScope` | Current tenant/provider scope metadata for run context | | `reasonCode` | Stable blocked reason when admission is prevented | | `reasonMeta` | Sanitized structured detail for translation and next steps | | `nextSteps` | Resolution path metadata used by the shared presentation helper | Rules: - Every accepted covered start must resolve this state before queue admission. - Blocked resolutions never admit a queued job. ## Derived Coordination Entities ### 1. ProtectedProviderScope This is a logical concurrency boundary, not a new table. | Field | Meaning | |---|---| | `tenantId` | Tenant boundary for the operation | | `providerConnectionId` | Connection boundary for provider-backed conflict protection | | `activeRunId` | Existing queued or running run occupying the scope, when present | | `activeOperationType` | Operation currently using the protected scope | Rules: - At most one provider-backed operation may be accepted at a time for one protected scope. - If the same operation type is already active on the scope, the result is `deduped`. - If a different covered operation is already active on the same scope, the result is `scope_busy`. ### 2. PreflightStartResult This is the shared logical result of a covered start attempt. | Field | Meaning | Source | |---|---|---| | `internalState` | Current service-owned state such as `started`, `deduped`, `scope_busy`, or `blocked` | `ProviderOperationStartResult` | | `operatorState` | Operator-facing vocabulary: `accepted`, `deduped`, `scope_busy`, `blocked` | shared presenter | | `operationType` | Covered operation being started | action host + registry | | `runId` | Canonical run for accepted, deduped, or scope-busy results, and optionally for blocked truth where already created | `OperationRun` | | `providerConnectionId` | Resolved connection identity when known | gate + resolver | | `reasonCode` | Stable problem class for blocked or other directed outcomes | current reason system | | `nextSteps` | Structured resolution or follow-up guidance | next-step registry / helper | Validation rules: - `blocked` must never dispatch a background job. - `accepted`, `deduped`, and `scope_busy` must point to the canonical run the operator should inspect. - `accepted` must carry the explicit `provider_connection_id` into accepted-work context. ### 3. AcceptedProviderBackedRunContext Logical accepted-work context persisted inside `OperationRun.context`. | Field | Meaning | |---|---| | `provider_connection_id` | Explicit connection identity used by the accepted run | | `provider` | Provider family label for display and diagnostics | | `target_scope` | Sanitized tenant/provider scope metadata | | `source_surface` | Action-host family such as tenant detail, provider connection, onboarding, restore, or directory sync | | `initiator_user_id` | Starting actor | | `operation_specific_context` | Existing per-operation context already needed by downstream jobs | Rules: - Jobs must receive the same `provider_connection_id` that was accepted at click time. - Monitoring and notifications explain accepted work using this context rather than a runtime default connection lookup. ### 4. ProviderStartPresentation Logical derived presentation output returned by the shared start-result helper. | Field | Meaning | |---|---| | `title` | Standardized accepted/deduped/scope-busy/blocked headline | | `body` | Short reason or queue message aligned with the spec vocabulary | | `statusStyle` | Existing toast/notification severity | | `viewRunAction` | Canonical open-run action when a run exists | | `nextStepActions[]` | Optional resolution actions or follow-up links | | `domainVerb` | Local action verb preserved from the host surface | | `domainTarget` | Local object noun preserved from the host surface | Rules: - Accepted and deduped outcomes continue to use the existing `OperationUxPresenter` toast style. - Blocked and scope-busy outcomes must no longer rely on page-local copy branches. - Domain verb and target remain local so the shared contract does not flatten the product vocabulary into generic verbs. ### 5. OnboardingBootstrapAdmission Logical onboarding-only coordination model built from existing draft state. | Field | Meaning | |---|---| | `selectedOperationTypes[]` | Bootstrap operations the operator selected | | `acceptedOperationType` | The one operation type admitted for the current submission | | `pendingOperationTypes[]` | Remaining selected types still waiting to run | | `runIdsByOperationType` | Existing and newly accepted run references persisted in draft state | Rules: - A bootstrap submission may accept at most one provider-backed operation for the protected scope. - Remaining bootstrap selections stay in existing draft state rather than spawning a new orchestration entity. - The onboarding step continues to be the only primary context; operators should not need a new workflow page to understand the pending follow-up. ## State Transitions ### Covered Start Attempt ```text requested -> blocked (no queue admission, blocked truth retained where applicable) -> deduped (existing same-operation active run reused) -> scope_busy (existing different-operation active run reused) -> accepted (run admitted, queued job dispatched with explicit provider connection) ``` ### Onboarding Bootstrap Submission ```text selected operations -> blocked / deduped / scope_busy (no new run admitted) -> accepted operation + pending operations retained in draft state ``` ## Non-Goals In The Data Model - No new provider-start table or umbrella batch entity - No new persisted summary or presentation artifact - No platform-wide operation-type rename or taxonomy rewrite - No new status family beyond the shared start-result vocabulary already required by the spec