## Summary - unify provider-backed action starts behind the shared provider dispatch gate and shared start-result presenter - align tenant, onboarding, provider-connection, restore, directory, and monitoring surfaces with the same blocked, deduped, scope-busy, and accepted semantics - include the spec kit artifacts for spec 216 and the regression fixes that brought the full suite back to green ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RestoreRunIdempotencyTest.php tests/Feature/ExecuteRestoreRunJobTest.php tests/Feature/Restore/RestoreRunProviderStartTest.php tests/Feature/Hardening/ExecuteRestoreRunJobGateTest.php tests/Feature/Hardening/BlockedWriteAuditLogTest.php tests/Feature/Onboarding/OnboardingDraftLifecycleTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec177InventoryCoverageTruthSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact` ## Notes - branch: `216-provider-dispatch-gate` - commit: `34230be7` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #255
9.8 KiB
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
ProviderConnectionplusProviderConnectionResolver - 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:
idtenant_idproviderentra_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:
idtenant_idtypestatusoutcomereason_codecontextsummary_countsstarted_atcompleted_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:
idworkspace_idtenant_id(nullable until attached)current_stepstate
- Existing state keys used by this feature:
bootstrap_operation_typesbootstrap_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:
idtenant_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:
blockedmust never dispatch a background job.accepted,deduped, andscope_busymust point to the canonical run the operator should inspect.acceptedmust carry the explicitprovider_connection_idinto 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_idthat 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
OperationUxPresentertoast 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
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
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