# Implementation Plan: Canonical Operation Type Source of Truth

**Branch**: `239-canonical-operation-type-source-of-truth` | **Date**: 2026-04-25 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/239-canonical-operation-type-source-of-truth/spec.md`

**Note**: This plan keeps the slice intentionally tight around one platform-core contract: dotted canonical `operation_type` codes become the only normative truth for touched writes and shared read models. No application code is implemented here.

## Summary

Promote the existing dotted `OperationCatalog` codes to the single normative `operation_type` contract, converge current write owners such as `OperationRunType`, provider operation definitions, onboarding bootstrap persistence, and lifecycle policy config to emit canonical dotted values directly, and keep only one bounded read-side compatibility seam for historical `operation_runs.type` rows and persisted onboarding draft state. The implementation stays inside the existing catalog, provider-start, onboarding, monitoring, and audit seams, avoids new tables or new abstraction layers, and uses focused unit, feature, Livewire, and architecture coverage to block raw alias drift from returning.

## Technical Context

**Language/Version**: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4  
**Primary Dependencies**: existing `App\Support\OperationCatalog`, `App\Support\OperationRunType`, `App\Services\OperationRunService`, `App\Services\Providers\ProviderOperationRegistry`, `App\Services\Providers\ProviderOperationStartGate`, `App\Filament\Resources\OperationRunResource`, `App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard`, `App\Support\Filament\FilterOptionCatalog`, `App\Support\OpsUx\OperationUxPresenter`, `App\Support\References\Resolvers\OperationRunReferenceResolver`, `App\Services\Audit\AuditEventBuilder`, Pest v4  
**Storage**: PostgreSQL via existing `operation_runs.type` and `managed_tenant_onboarding_sessions.state->bootstrap_operation_types`, plus config-backed `tenantpilot.operations.lifecycle.covered_types` and `tenantpilot.platform_vocabulary`; no new tables  
**Testing**: Pest unit, feature, Filament Livewire, and existing heavy-governance tests run through Laravel Sail  
**Validation Lanes**: `fast-feedback`, `confidence`, `heavy-governance`  
**Target Platform**: Laravel monolith in `apps/platform` with native Filament v5 admin and system surfaces plus queue-backed `OperationRun` workflows  
**Project Type**: Monorepo with one Laravel runtime in `apps/platform`; `apps/website` is out of scope  
**Performance Goals**: keep canonical resolution deterministic and in-process, preserve DB-only monitoring render paths, preserve existing filter/query shape efficiency, and avoid new query fan-out or render-time remote work  
**Constraints**: no new table, no new abstraction framework, no new operation family, no monitoring IA redesign, no broader governed-subject cleanup outside `operation_type`, no dual-write compatibility path, no change to authorization semantics, and no provider-neutral rename sweep  
**Scale/Scope**: one existing canonical catalog, one enum-backed contract hotspot, one provider registry/start gate seam, one onboarding bootstrap state seam, one lifecycle config seam, several read-model/presenter consumers, and focused guard coverage

## Filament v5 Implementation Contract

- **Livewire v4.0+ compliance**: Preserved. Touched admin and system surfaces remain native Filament v5 / Livewire v4 surfaces and no legacy Livewire or Filament APIs are introduced.
- **Provider registration location**: Unchanged. Panel providers remain registered in `apps/platform/bootstrap/providers.php`.
- **Global search impact**: No new globally searchable surface is introduced. `OperationRunResource` currently registers no pages (`getPages(): []`), so this slice keeps operation-type hardening outside global search rather than adding View/Edit pages just for searchability.
- **Destructive actions**: No new destructive action is planned. Existing destructive actions touched indirectly remain server-authorized and keep `Action::make(...)->action(...)->requiresConfirmation()` where already required; onboarding completion confirmation stays unchanged.
- **Asset strategy**: No new assets or panel registrations are planned. Deployment expectations remain unchanged; if later UI work adds registered assets, deploy still runs `cd apps/platform && php artisan filament:assets`.
- **Testing plan**: Prove canonical contract convergence with focused unit coverage for resolution or write-truth, focused feature and Livewire coverage for operations filters, onboarding resume/start behavior, tenant-safe detail navigation, and DB-only rendering, plus existing heavy-governance coverage for platform vocabulary and raw-alias or non-leakage drift.

## UI / Surface Guardrail Plan

- **Guardrail scope**: changed surfaces
- **Native vs custom classification summary**: native Filament surfaces plus existing shared presenters/helpers
- **Shared-family relevance**: operations list/detail/filter family, onboarding bootstrap launch family, related-navigation/reference family, audit-adjacent summaries
- **State layers in scope**: page, detail, URL-query
- **Handling modes by drift class or surface**: raw write-time aliases are `hard-stop-candidate`; read-side compatibility is `review-mandatory`
- **Repository-signal treatment**: `future hard-stop candidate`
- **Special surface test profiles**: `monitoring-state-page`, `standard-native-filament`, `shared-detail-family`
- **Required tests or manual smoke**: `functional-core`, `state-contract`
- **Exception path and spread control**: one named read-side compatibility boundary limited to historical `operation_runs.type` rows and persisted onboarding draft state; no write-side exception
- **Active feature PR close-out entry**: `Guardrail`

## Shared Pattern & System Fit

- **Cross-cutting feature marker**: yes
- **Systems touched**: `OperationCatalog`, `OperationRunType`, `ProviderOperationRegistry`, `ProviderOperationStartGate`, `FilterOptionCatalog`, `OperationRunResource`, `ManagedTenantOnboardingWizard`, `OperationRunLinks`, `OperationUxPresenter`, `OperationRunReferenceResolver`, `AuditEventBuilder`, `OperationRunService`, `OperationRunTriageService`, `FindingsLifecycleBackfillRunbookService`, and `tenantpilot` vocabulary/lifecycle config
- **Shared abstractions reused**: `OperationCatalog` as the sole canonical contract, `OperationRunService` for run identity and lifecycle, `ProviderOperationStartGate` for shared start UX, `FilterOptionCatalog` for canonical filter options, and existing presenter/reference builders for operator labels
- **New abstraction introduced? why?**: none planned. If a tiny helper is needed, it must replace duplicated raw comparisons inside existing seams rather than create a new registry or translator family.
- **Why the existing abstraction was sufficient or insufficient**: `OperationCatalog` is already the correct label and canonical-code source, but it is insufficient while enum-backed writes, provider registry definitions, onboarding selections, lifecycle config, and raw type-specific branches still treat aliases as peer truths.
- **Bounded deviation / spread control**: compatibility stays read-side only inside the existing catalog and onboarding-normalization path; no dual-write, fallback writer, or long-lived alias preservation layer is allowed.

## OperationRun UX Impact

- **Touches OperationRun start/completion/link UX?**: yes
- **Central contract reused**: shared OperationRun UX layer via `ProviderOperationStartGate`, `ProviderOperationStartResultPresenter`, `OperationRunService`, and `OperationUxPresenter`
- **Delegated UX behaviors**: queued toast, run link, run-enqueued browser event, dedupe-or-scope-busy messaging, queued DB-notification decision, tenant/workspace-safe URL resolution
- **Surface-owned behavior kept local**: onboarding keeps only bootstrap selection and provider-connection inputs; operations surfaces remain read-only label/filter consumers
- **Queued DB-notification policy**: explicit opt-in, unchanged
- **Terminal notification path**: central lifecycle mechanism, unchanged
- **Exception path**: none. The only bounded exception is read-side alias compatibility, not a local UX contract bypass.

## Provider Boundary & Portability Fit

- **Shared provider/platform boundary touched?**: yes
- **Provider-owned seams**: current provider bindings and provider-specific operation families behind `ProviderOperationRegistry` / `ProviderOperationStartGate`, plus provider-specific canonical families such as `directory.groups.sync` and `entra.admin_roles.scan`
- **Platform-core seams**: `operation_type` vocabulary, `OperationCatalog`, filter-option convergence, monitoring/read-model summaries, onboarding persisted selection truth, audit metadata, and lifecycle policy config
- **Neutral platform terms / contracts preserved**: `operation`, `operation_type`, `canonical operation code`, `operation catalog`, `operation label`
- **Retained provider-specific semantics and why**: current dotted provider-owned codes remain valid canonical codes when the operation itself is provider-specific; the slice tightens truth ownership without renaming all provider-domain vocabulary
- **Bounded extraction or follow-up path**: `follow-up-spec` for broader governed-subject or provider/domain vocabulary cleanup once `operation_type` truth is stable

## Constitution Check

*GATE: Passed before Phase 0 research. Re-check after Phase 1 design: still passes with one bounded read-side compatibility seam and no new persistence or framework.*

| Gate | Status | Plan Notes |
|------|--------|------------|
| Inventory-first / read-write separation | PASS | The feature hardens an existing operational contract. No new mutable workflow beyond canonical identifier replacement is introduced. |
| Workspace + tenant isolation / RBAC-UX | PASS | No new route, plane, or capability family is added. Existing `/admin`, tenant-context, and `/system` semantics remain unchanged while filter/read models stay entitlement-safe. |
| Run observability / Ops-UX lifecycle | PASS | Existing `OperationRun` creation, dedupe, `summary_counts`, and notification behavior remain service-owned. Only `operation_type` identity entering those paths changes. |
| Shared pattern first (XCUT-001) | PASS | The plan reuses `OperationCatalog`, `OperationRunService`, `ProviderOperationStartGate`, and existing presenter/reference helpers rather than introducing a new translation framework. |
| Provider boundary (PROV-001) | PASS | Platform-core `operation_type` truth is tightened while provider-specific operation families remain bounded at provider-owned seams. |
| Proportionality / no premature abstraction | PASS | The implementation converges on existing catalog/config/service seams and rejects a new registry/resolver layer. |
| Persisted truth / behavioral state | PASS | No new table, entity, or status family is added. Existing rows and onboarding drafts remain readable only through the bounded rollout seam. |
| LEAN-001 compatibility bias | PASS with explicit spec exception | Pre-production replacement remains the default. The spec explicitly allows only a narrow read-side seam for historical rows and persisted draft state; no write-side preservation is allowed. |
| Filament v5 + Livewire v4 contract | PASS | Touched surfaces stay native Filament v5/Livewire v4, panel provider registration remains unchanged, and no new global-search surface is introduced. |
| Destructive action safety | PASS | No new destructive action is added. Existing confirmation and authorization requirements remain unchanged. |
| Asset strategy | PASS | No asset change is planned. |
| Test governance | PASS | Coverage stays in focused unit and feature lanes plus existing heavy-governance families; no browser lane or new heavy-governance family is required for proof. |

## Test Governance Check

- **Test purpose / classification by changed surface**: `Unit` for canonical resolution and write-truth enforcement, `Feature` for onboarding and operations filter or detail behavior, `Feature/Livewire` for Filament filter/state flows, `Heavy-Governance` for platform vocabulary and anti-drift or non-leakage guards
- **Affected validation lanes**: `fast-feedback`, `confidence`, `heavy-governance`
- **Why this lane mix is the narrowest sufficient proof**: the risk is semantic drift at shared contract boundaries, operations-surface entitlement leakage, and accidental render-path regression, not browser rendering. Unit tests prove canonical identity rules and registry writes; focused feature/Livewire tests prove the main operator surfaces, tenant-safe detail navigation, and DB-only rendering; existing heavy-governance guards catch reintroduced raw alias writers and workspace or tenant leakage.
- **Narrowest proving commands**:
  - `Current repo baseline before implementation: export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OperationTypeResolutionTest.php tests/Unit/Providers/ProviderOperationStartGateTest.php tests/Unit/Onboarding/OnboardingDraftResolverTest.php`
    - `Post-implementation expanded unit proof after the planned canonical-contract tests land: export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OperationTypeResolutionTest.php tests/Unit/Support/OperationRunTypeCanonicalContractTest.php tests/Unit/Providers/ProviderOperationRegistryCanonicalTypeTest.php tests/Unit/Providers/ProviderOperationStartGateTest.php tests/Unit/Onboarding/OnboardingDraftResolverTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/OperationRunListFiltersTest.php tests/Feature/Filament/RecentOperationsSummaryWidgetTest.php tests/Feature/Monitoring/AuditCoverageOperationsTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Monitoring/OperationsDbOnlyRenderTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/ManagedTenantOnboardingWizardTest.php tests/Feature/Workspaces/ManagedTenantOnboardingProviderStartTest.php tests/Feature/Rbac/OnboardingWizardUiEnforcementTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Architecture/PlatformVocabularyBoundaryGuardTest.php tests/Feature/Guards/OperationRunLinkContractGuardTest.php tests/Feature/OpsUx/UnknownOperationTypeLabelTest.php tests/Feature/OpsUx/NonLeakageWorkspaceOperationsTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- **Fixture / helper / factory / seed / context cost risks**: moderate. Existing factories and helpers default many runs and onboarding drafts to legacy alias strings such as `inventory_sync` or `backup_schedule_run`; the slice must update those defaults deliberately instead of adding a second helper layer.
- **Expensive defaults or shared helper growth introduced?**: no; fixture changes should reduce alias drift rather than add compatibility helpers
- **Heavy-family additions, promotions, or visibility changes**: existing heavy-governance families in `tests/Architecture` and `tests/Feature/OpsUx` are touched, but no new heavy family is introduced and no browser scope is added
- **Surface-class relief / special coverage rule**: `monitoring-state-page` and `standard-native-filament` relief are sufficient; no browser proof is required
- **Closing validation and reviewer handoff**: reviewers should confirm that touched writes emit canonical dotted codes, filter options collapse alias duplicates, onboarding resume canonicalizes historical selections, raw-type branches are replaced or deliberately bounded, operations tenant-scope and tenantless detail behavior remain entitlement-safe, DB-only rendering stays DB-only, and compatibility remains read-side only
- **Budget / baseline / trend follow-up**: none expected beyond localized fixture updates
- **Review-stop questions**: did any touched writer still emit raw aliases? Did any new compatibility path write or preserve aliases? Did the slice widen into broader vocabulary cleanup? Did fixture defaults or guard tests start treating historical aliases as acceptable new truth? Did canonicalization weaken tenant-scope or tenantless-viewer entitlement checks? Did any touched operations surface stop rendering from DB-only state?
- **Escalation path**: `document-in-feature`
- **Active feature PR close-out entry**: `Guardrail`
- **Why no dedicated follow-up spec is needed**: the remaining broader vocabulary cleanup is already out of scope and explicitly deferred; this slice can be proven inside existing lanes.

## Project Structure

### Documentation (this feature)

```text
specs/239-canonical-operation-type-source-of-truth/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── canonical-operation-type-source-of-truth.logical.openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md
```

### Source Code (repository root; target touched surfaces after implementation)

```text
apps/platform/
├── app/
│   ├── Filament/
│   │   ├── Pages/
│   │   │   └── Workspaces/
│   │   │       └── ManagedTenantOnboardingWizard.php
│   │   └── Resources/
│   │       └── OperationRunResource.php
│   ├── Models/
│   │   └── OperationRun.php
│   ├── Services/
│   │   ├── Audit/
│   │   │   └── AuditEventBuilder.php
│   │   ├── Providers/
│   │   │   ├── ProviderOperationRegistry.php
│   │   │   └── ProviderOperationStartGate.php
│   │   ├── Runbooks/
│   │   │   └── FindingsLifecycleBackfillRunbookService.php
│   │   ├── SystemConsole/
│   │   │   └── OperationRunTriageService.php
│   │   └── OperationRunService.php
│   └── Support/
│       ├── Filament/
│       │   └── FilterOptionCatalog.php
│       ├── OpsUx/
│       │   └── OperationUxPresenter.php
│       ├── References/Resolvers/
│       │   └── OperationRunReferenceResolver.php
│       ├── OperationCatalog.php
│       ├── OperationRunLinks.php
│       └── OperationRunType.php
├── config/
│   └── tenantpilot.php
└── tests/
    ├── Architecture/
    │   └── PlatformVocabularyBoundaryGuardTest.php
    ├── Feature/
    │   ├── Filament/
    │   │   ├── OperationRunListFiltersTest.php
    │   │   └── RecentOperationsSummaryWidgetTest.php
    │   ├── Guards/
    │   │   └── OperationRunLinkContractGuardTest.php
    │   ├── ManagedTenantOnboardingWizardTest.php
    │   └── Workspaces/
    │       └── ManagedTenantOnboardingProviderStartTest.php
    └── Unit/
        ├── Providers/
        │   ├── ProviderOperationRegistryCanonicalTypeTest.php
        │   └── ProviderOperationStartGateTest.php
        └── Support/
            ├── OperationRunTypeCanonicalContractTest.php
            └── OperationTypeResolutionTest.php
```

    Additional concrete seams intentionally included by the task plan inside this same boundary are `apps/platform/app/Support/Operations/OperationLifecyclePolicy.php`, `apps/platform/app/Services/Evidence/Sources/OperationsSummarySource.php`, `apps/platform/app/Services/Onboarding/OnboardingLifecycleService.php`, `apps/platform/app/Http/Controllers/AdminConsentCallbackController.php`, `apps/platform/app/Services/Inventory/InventorySyncService.php`, `apps/platform/app/Services/BackupScheduling/BackupScheduleDispatcher.php`, `apps/platform/app/Services/ReviewPackService.php`, `apps/platform/app/Services/Evidence/EvidenceSnapshotService.php`, and `apps/platform/app/Services/TenantReviews/TenantReviewService.php`.

**Structure Decision**: keep the slice entirely inside the existing Laravel runtime in `apps/platform`, extending current operation-support, provider-start, onboarding, and monitoring seams. No new top-level codebase area or secondary framework is needed.

## Complexity Tracking

No constitutional violation is planned. One bounded complexity concern is tracked because the feature explicitly preserves a temporary compatibility seam despite LEAN-001’s default replacement bias.

| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| Bounded read-side compatibility seam for historical `operation_runs.type` rows and onboarding draft arrays | Existing rows and persisted drafts already hold aliases that the spec requires to remain readable during rollout, but only on read paths | Mass rewriting historical rows and draft payloads inside this slice would broaden scope into data migration/cleanup; write-side preservation or dual-write is forbidden |

## Proportionality Review

- **Current operator problem**: the same operation family is emitted, stored, filtered, audited, and reviewed under different identifiers, which weakens operator trust and lets new drift re-enter shared seams.
- **Existing structure is insufficient because**: `OperationCatalog` already knows the canonical dotted contract, but `OperationRunType`, provider registry definitions, onboarding selections, lifecycle config, and raw branch logic still treat aliases as first-class truth.
- **Narrowest correct implementation**: make the existing catalog the only normative contract, change touched writers to emit canonical dotted codes directly, and limit compatibility to read-time alias resolution for historical rows and onboarding draft state.
- **Ownership cost created**: several focused code paths and tests must be updated together, and the bounded alias map needs explicit retirement review instead of silently becoming permanent.
- **Alternative intentionally rejected**: keeping long-lived dual semantics via `canonicalCode()` or adding a broader operation-type resolver framework. Both preserve drift and add maintenance cost without solving the underlying truth split.
- **Release truth**: current-release anti-drift hardening for a platform-core canonical noun

## Phase 0 Research Summary

- `OperationCatalog` is already the correct canonical authority; `OperationRunType::canonicalCode()` is now evidence of drift, not the desired steady-state API.
- The first-slice write owners are `OperationRunType`, `ProviderOperationRegistry` / `ProviderOperationStartGate`, onboarding bootstrap selection persistence/start, and `tenantpilot.operations.lifecycle.covered_types`.
- Raw type branches still exist in `OperationRunResource`, `OperationRunLinks`, and non-UI metadata emitters such as `OperationRunService`, `OperationRunTriageService`, and `FindingsLifecycleBackfillRunbookService`.
- Canonical dotted codes that legitimately retain underscore segments and must not trigger broader cleanup include `backup_set.update`, `directory.role_definitions.sync`, `tenant.review_pack.generate`, `tenant.evidence.snapshot.generate`, `entra.admin_roles.scan`, and `rbac.health_check`.
- The only allowed compatibility seam is read-side alias resolution for historical rows and persisted onboarding drafts.
- Focused unit, feature, Livewire, and existing heavy-governance coverage is sufficient; browser proof is not required for this slice.

## Phase 1 Design Summary

- `research.md` records contract decisions, the explicit underscore-segment exceptions that remain canonical, and the non-UI metadata sites that still surface raw `type`.
- `data-model.md` defines the canonical operation-type contract, legacy alias seam, onboarding bootstrap selection truth, and provider operation definition requirements.
- `contracts/canonical-operation-type-source-of-truth.logical.openapi.yaml` records the logical contract for resolving raw values, normalizing onboarding selections, and reading canonical filter/write metadata.
- `quickstart.md` captures the narrow implementation order and review-proof commands.
- `tasks.md` remains Phase 2 work and is not created by `/speckit.plan`.

## Phase 1 — Agent Context Update

Run after artifact generation:

- `.specify/scripts/bash/update-agent-context.sh copilot`

## Implementation Strategy

### Phase A — Collapse write-time truth onto canonical dotted codes

**Goal**: remove peer write truths before touching broader read surfaces.

- Update `app/Support/OperationRunType.php` so enum-backed producers stop relying on `canonicalCode()` translation and instead represent canonical dotted values directly or become a pure compatibility shim slated for removal.
- Align `app/Services/Providers/ProviderOperationRegistry.php`, `ProviderOperationStartGate.php`, and onboarding/bootstrap capability resolution in `ManagedTenantOnboardingWizard.php` to use canonical dotted `operation_type` values as their emitted and persisted contract.
- Convert `apps/platform/config/tenantpilot.php` operation lifecycle `covered_types` keys to canonical dotted codes and keep any historical storage compatibility bounded to read/lookup paths.
- Sweep the first-slice service/job start owners that currently use `OperationRunType::...->value` or raw aliases and make their emitted run type canonical at the source.

### Phase B — Bound compatibility to read-time resolution only

**Goal**: keep historical rows and draft state readable without preserving legacy write behavior.

- Keep alias resolution centralized in `OperationCatalog` and trim the alias inventory to explicitly read-side cases only.
- Normalize historical onboarding draft `bootstrap_operation_types` on load, save, and start so resumed drafts stop writing aliases back into session state.
- Ensure unknown or unsupported operation values remain explicitly unknown and never inherit nearby canonical labels.
- Do not add backfill migrations, dual-write logic, or fallback writers.

### Phase C — Converge read models, filters, and operator-adjacent metadata

**Goal**: make all touched consumers read through one canonical contract.

- Replace raw type comparisons in `OperationRunResource`, `OperationRunLinks`, and other touched type-specific branches with canonical checks or canonical value literals.
- Keep `FilterOptionCatalog`, `OperationUxPresenter`, `OperationRunReferenceResolver`, and `AuditEventBuilder` on `OperationCatalog` resolution, and update any remaining raw-type assumptions they rely on.
- Canonicalize operator-adjacent metadata payloads in `OperationRunService`, `OperationRunTriageService`, `FindingsLifecycleBackfillRunbookService`, and onboarding audit metadata where `operation_type` is currently copied from raw storage.
- Preserve query efficiency by using `OperationCatalog::rawValuesForCanonical()` only where historical storage rows still need to be matched.

### Phase D — Lock the boundary with focused tests and guards

**Goal**: make raw alias reintroduction fail fast.

- Extend unit coverage for canonical resolution and add guard coverage for enum/registry canonical contract behavior.
- Extend operations filter and onboarding start/resume feature coverage so one canonical selection maps to both current and historical rows without leaking cross-tenant data.
- Tighten heavy-governance guard tests so new in-scope alias writers, tenant-leakage paths, or registry entries fail review and CI.
- Update shared fixture defaults only where necessary to stop teaching aliases as normal current-release truth.

## Risks and Mitigations

- **Scope creep into broader vocabulary cleanup**: mitigate by explicitly preserving already-canonical underscore-segment codes and keeping all non-`operation_type` naming outside this spec.
- **Compatibility seam persistence**: mitigate by restricting alias handling to catalog and onboarding read normalization and rejecting any write-side fallback.
- **Partial convergence**: mitigate by sequencing write owners before read-model cleanup and using guard coverage for registry/start paths.
- **Fixture churn**: mitigate by updating only the affected factories/helpers and avoiding a second compatibility helper layer.

## Post-Design Re-check

The feature remains constitution-compliant and implementation-ready. It introduces no new table, no new abstraction framework, no new operation family, no monitoring IA redesign, no provider-platform boundary rewrite beyond the in-scope contract, and no Filament panel or asset change. The plan keeps compatibility explicitly bounded and read-side only, preserves Livewire v4 / Filament v5 conventions, keeps provider registration in `bootstrap/providers.php`, leaves global search unchanged, and centers proof on focused unit, feature, Livewire, and existing heavy-governance tests.

## Implementation Close-Out

- **Guardrail disposition**: `Guardrail` remains the active close-out entry. The implementation stayed inside `operation_type` truth and did not add a generic compatibility framework, new persistence, panel changes, assets, or global-search changes.
- **Compatibility disposition**: `document-in-feature`. Historical alias handling remains bounded to read-side resolution/filter matching, queued-run reauthorization, and onboarding draft normalization. No writer may use a legacy alias as registry or persisted current-release truth.
- **Deferred boundary**: `follow-up-spec` remains appropriate only for broader provider/domain vocabulary cleanup outside this slice, such as future governed-subject naming work.
- **Validation recorded**: focused US1, US2, US3, and affected writer/queue regression lanes ran through Sail/Pest before final validation. Final handoff requires the full quickstart command in `quickstart.md` plus dirty-file Pint.
