# Data Model: Canonical Operation Type Source of Truth ## Overview This feature adds no new table, no new persisted entity, and no new status family. It tightens one existing platform-core contract: the dotted canonical `operation_type` definitions already described by `OperationCatalog` become the only normative write-time and registry-time truth for the touched slice. Historical aliases remain derived and read-side only during rollout. ## Entity: CanonicalOperationType - **Type**: existing derived contract from `App\Support\OperationCatalog` - **Purpose**: names one operation family consistently across writes, provider bindings, onboarding state, filters, audit metadata, related references, and operator labels. ### Identity - `canonical_code` — stable dotted identifier such as `inventory.sync` or `backup.schedule.execute` ### Core Fields | Field | Type | Notes | |-------|------|-------| | `canonical_code` | string | Primary platform contract for touched writes and read models. | | `display_label` | string | Operator-facing label resolved from `OperationCatalog`. | | `domain_key` | string nullable | Existing domain grouping metadata; unchanged by this slice. | | `expected_duration_seconds` | integer nullable | Existing Ops UX timing metadata; unchanged by this slice. | | `supports_operator_explanation` | boolean | Existing explanation behavior; unchanged by this slice. | | `alias_retirement_policy` | string | Read-side-only rollout seam for historical aliases. | ### Validation Rules - All new or updated in-scope writes MUST emit `canonical_code` directly. - Unknown values MUST stay explicitly unknown and MUST NOT inherit a nearby canonical label. - Canonical dotted codes that already contain underscore segments remain valid current-release truth and MUST NOT be renamed by this feature. ### Explicit Canonical Codes That Stay Unchanged - `backup_set.update` - `directory.role_definitions.sync` - `tenant.review_pack.generate` - `tenant.evidence.snapshot.generate` - `entra.admin_roles.scan` - `rbac.health_check` ## Entity: HistoricalOperationTypeAlias - **Type**: existing derived compatibility entry from `OperationCatalog` - **Purpose**: maps a legacy raw value such as `inventory_sync` or `baseline_capture` to one canonical operation family for historical reads only. ### Identity - `raw_value` — stored or historical identifier encountered in rows, fixtures, or persisted onboarding drafts ### Core Fields | Field | Type | Notes | |-------|------|-------| | `raw_value` | string | Historical storage or fixture value. | | `canonical_code` | string | Canonical dotted operation family. | | `alias_status` | string | Existing statuses such as `canonical` or `legacy_alias`. | | `write_allowed` | boolean | `false` for legacy aliases after this feature on touched write paths. | | `retirement_note` | string nullable | Existing contributor-facing retirement guidance. | | `match_surfaces` | array | Bounded to `operation_runs.type` historical rows and onboarding draft state for this slice. | ### Validation Rules - Legacy aliases MAY be resolved only on read paths. - New or updated write-time callers MUST NOT emit legacy aliases. - Alias support MUST remain removable and MUST NOT require dual-write behavior. ## Entity: OperationRun - **Type**: existing persisted model - **Purpose in this feature**: remains the canonical operational record while its `type` field is hardened toward canonical dotted values for new writes and still resolves historical aliases on read. ### Relevant Fields | Field | Type | Notes | |-------|------|-------| | `type` | string | Existing persisted field; new in-scope writes must use canonical dotted codes. Historical rows may still contain legacy aliases during rollout. | | `run_identity_hash` | string | Dedupe identity; unchanged. | | `context` | json/array | Existing metadata surface; touched summaries and audit-adjacent payloads should emit canonical `operation_type`. | | `summary_counts` | json/array | Existing Ops-UX counters; unchanged. | | `status` | string | Lifecycle remains service-owned and unchanged. | | `outcome` | string | Lifecycle remains service-owned and unchanged. | ### Relationships | Relationship | Target | Purpose | |--------------|--------|---------| | `workspace` | `Workspace` | Keeps workspace isolation explicit. | | `tenant` | `Tenant` | Keeps tenant scope explicit for filters, triage, and onboarding. | | `resolvedOperationType()` | `OperationTypeResolution` | Existing read-path resolution that must remain the only compatibility seam for legacy aliases. | ### Feature-Specific Invariants - `resolvedOperationType()` and `canonicalOperationType()` remain the read-path truth for historical rows. - Touched write owners must stop relying on `OperationRunType::canonicalCode()` as a second-step translation. - Type-specific branches that currently compare raw aliases should compare canonical truth or canonical literals after the write owners converge. ## Entity: OnboardingBootstrapSelection - **Type**: existing persisted workflow state inside `managed_tenant_onboarding_sessions.state` - **Purpose in this feature**: holds selected bootstrap operation types and started bootstrap run references for the onboarding wizard. ### Relevant Fields | Field | Type | Notes | |-------|------|-------| | `bootstrap_operation_types[]` | array | Load path may encounter legacy aliases; save and start paths must persist canonical dotted codes only after this feature. | | `bootstrap_operation_runs` | map | Keys should follow the same canonical operation codes used by `bootstrap_operation_types`. | | `started_operation_type` | string nullable | Audit-adjacent summary field that should emit canonical dotted code for new writes. | ### Validation Rules - Resume behavior may normalize historical aliases. - Persisted selections after any touched save or start action MUST be canonical dotted codes only. - Unknown bootstrap values MUST be dropped or remain explicitly unsupported; they MUST NOT map to a nearby canonical action silently. ## Entity: ProviderOperationDefinition - **Type**: existing shared provider registry definition - **Purpose in this feature**: declares provider-backed operation metadata while consuming canonical platform-owned `operation_type` values. ### Relevant Fields | Field | Type | Notes | |-------|------|-------| | `operation_type` | string | Must be canonical dotted code after this feature. | | `module` | string | Existing provider module grouping; unchanged. | | `label` | string | Existing operator-facing label; unchanged. | | `required_capability` | string | Existing capability binding; unchanged. | | `provider_binding.provider` | string | Provider-owned runtime binding; unchanged in concept. | ### Validation Rules - Registry definitions and provider bindings MUST reference the same canonical dotted `operation_type`. - Provider binding MUST remain provider-owned, while `operation_type` remains platform-core. - Unsupported combinations still block explicitly; this feature does not weaken start-gate safety. ## Entity: OperationTypeMetadataPayload - **Type**: existing derived metadata shape across audit, triage, alert, and reference contexts - **Purpose in this feature**: ensures operator-adjacent payloads stop copying raw `run->type` as current-release truth. ### Relevant Fields | Field | Type | Notes | |-------|------|-------| | `operation_type` | string | Should emit canonical dotted code in touched metadata payloads. | | `operation_run_id` | integer | Existing reference to the underlying run. | | `summary_counts` | array | Existing flat metrics; unchanged. | | `scope` / `target_scope` | array or string | Existing scope context; unchanged. | ### Validation Rules - Operator-adjacent metadata MUST not reintroduce legacy raw aliases as first-class truth. - Storage-oriented raw `type` MAY remain in the row itself during rollout, but touched metadata should emit canonical `operation_type`. ## Relationships - One `CanonicalOperationType` may have many `HistoricalOperationTypeAlias` entries. - One `OperationRun` resolves to exactly one `CanonicalOperationType` on read via `OperationCatalog`. - One `OnboardingBootstrapSelection` stores many canonical operation codes and may map each selected canonical code to one run ID. - One `ProviderOperationDefinition` references exactly one `CanonicalOperationType` and may have one active provider binding in the current release. - One `OperationTypeMetadataPayload` should mirror the canonical operation identity for the underlying `OperationRun` without becoming a second source of truth. ## Rollout / Lifecycle Rules ### Write-time truth - New or updated in-scope writes use canonical dotted `operation_type` values directly. - Legacy aliases are not permitted as new current-release truth on touched writers, registries, config keys, or onboarding persistence. ### Read-time compatibility - Historical `operation_runs.type` values and historical onboarding draft selections may still resolve through the alias map during rollout. - Filter query expansion may continue to use `rawValuesForCanonical()` only where historical rows must still be matched. ### Unknown handling - Unknown values remain explicitly unknown and never auto-normalize to a nearby canonical family. ### Seam retirement - Once historical rows and fixtures are no longer needed, alias entries and onboarding normalization fallbacks can be removed without changing the canonical contract. ## Persistence Impact - **Schema changes**: None - **New tables**: None - **Backfill jobs or migrations**: None planned in this slice - **Config updates**: Existing keys update in place to canonical dotted values where touched