# Implementation Plan: Retire Legacy Runs Into Operation Runs **Branch**: `086-retire-legacy-runs-into-operation-runs` | **Date**: 2026-02-10 | **Spec**: `specs/086-retire-legacy-runs-into-operation-runs/spec.md` **Input**: Feature specification from `specs/086-retire-legacy-runs-into-operation-runs/spec.md` ## Summary Retire legacy “run tracking” tables as the primary execution tracker for in-scope operations (inventory sync, directory groups sync, backup schedule runs, restore execution, and directory role definitions sync) and make `operation_runs` the canonical source of truth. Key implementation approach: - Use the existing tenantless canonical viewer `/admin/operations/{run}` (Filament page `TenantlessOperationRunViewer`) and ensure it remains DB-only at render time. - Enforce the clarified 404/403 semantics for run viewing: non-members 404, members missing capability 403, where the view capability equals the start capability. - Enforce dispatch-time OperationRun creation for every start surface; jobs never fallback-create. - Apply explicit run identity rules per operation type (dedupe vs unique-per-click vs strict schedule dedupe), including strict scheduled backup idempotency: at most one canonical run ever per (schedule_id, intended fire-time). - Remove Graph calls from UI render/search/label callbacks by using cached directory data (groups + role definitions) and “Sync now” operations. ## Technical Context **Language/Version**: PHP 8.4.15 **Primary Dependencies**: Laravel 12, Filament v5, Livewire v4 **Storage**: PostgreSQL (via Sail) **Testing**: Pest v4 (PHPUnit v12 runner) **Target Platform**: Web application (Laravel + Filament admin panel) **Project Type**: web **Performance Goals**: Operations viewer + Monitoring pages render from DB state only; canonical viewer loads in ~2s under normal conditions **Constraints**: No outbound HTTP in Monitoring/Operations rendering/search/label callbacks (OPS-EX-AUTH-001); dispatch-time OperationRun creation; jobs must never fallback-create; strict 404/403 isolation semantics **Scale/Scope**: TenantPilot admin workflows; multiple operation families; staged cutover with legacy history preserved ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: No change to the meaning of inventory vs snapshots/backups; this spec only changes execution tracking. - Read/write separation: Start surfaces remain enqueue-only; destructive-like actions are not added here; audit logging remains required for mutations. - Graph contract path: UI render/search/label callbacks must become DB-only; any remaining Graph calls stay behind `GraphClientInterface` + `config/graph_contracts.php`. - Deterministic capabilities: Run viewing must be capability-gated using the existing capability registry (no raw strings). - RBAC-UX: Enforce clarified semantics for run viewing: non-members 404, members missing capability 403; authorization enforced server-side via Policy/Gate. - Workspace isolation: Canonical tenantless `/admin/operations/{run}` continues to enforce workspace membership (deny-as-not-found). - Global search: `OperationRunResource` stays non-globally-searchable; no new global-search surfaces introduced. - Run observability: All in-scope long-running/scheduled/remote operations are tracked via `OperationRun`; Monitoring pages remain DB-only. - Automation: Scheduled backup run creation uses strict idempotency per schedule + intended fire-time. - Badge semantics (BADGE-001): Run status/outcome badges already use `BadgeRenderer`; do not introduce ad-hoc mappings. - Filament UI Action Surface Contract: Legacy resources remain read-only; canonical operations pages already define inspection affordances. ## Project Structure ### Documentation (this feature) ```text specs/086-retire-legacy-runs-into-operation-runs/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── README.md └── tasks.md # To be created by /speckit.tasks ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ ├── Pages/ │ │ ├── Monitoring/ │ │ └── Operations/ │ └── Resources/ ├── Http/ │ └── Middleware/ ├── Jobs/ ├── Models/ ├── Policies/ ├── Services/ └── Support/ config/ ├── graph.php └── graph_contracts.php database/ └── migrations/ tests/ ├── Feature/ └── Unit/ ``` **Structure Decision**: Laravel web application (monolith) with Filament admin panel. ## Complexity Tracking No constitution violations are required for this feature. ## Phase Plan Phase 0/1 deliverables are already captured in: - `specs/086-retire-legacy-runs-into-operation-runs/research.md` - `specs/086-retire-legacy-runs-into-operation-runs/data-model.md` - `specs/086-retire-legacy-runs-into-operation-runs/contracts/README.md` - `specs/086-retire-legacy-runs-into-operation-runs/quickstart.md` Phase 2 (tasks) will be produced via `/speckit.tasks` and should slice work by operation family: 1) Authorization: capability-gate canonical run viewing (404 vs 403 semantics). 2) Backup schedules: add `backup_schedule.scheduled` + strict idempotency; make manual runs unique-per-click. 3) Directory groups: stop writing legacy rows; keep legacy pages read-only; ensure dispatch-time OperationRun creation. 4) Inventory sync: stop writing legacy rows; ensure dispatch-time OperationRun creation and no UI Graph calls. 5) Tenant configuration: remove Graph calls from render/search/labels; add role definitions cache + “Sync now” operation. 6) Restore: ensure execution tracking uses OperationRun only; legacy restore domain records remain as domain entities.