--- description: "Task list for Spec 086 implementation" --- # Tasks: Retire Legacy Runs Into Operation Runs (086) **Input**: Design documents from `specs/086-retire-legacy-runs-into-operation-runs/` **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/, quickstart.md **Tests**: REQUIRED (Pest) — runtime behavior changes must be covered. ## Phase 1: Setup (Shared Infrastructure) - [x] T001 Confirm baseline green test subset via `tests/Feature/Operations/TenantlessOperationRunViewerTest.php`, `tests/Feature/Monitoring/OperationsDbOnlyTest.php`, and `tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php` - [x] T002 Confirm Filament v5 + Livewire v4 constraints are respected for any touched pages/resources in `app/Filament/**` --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Shared primitives required by all stories. - [x] T003 Add centralized “run type → required capability” resolver in `app/Support/Operations/OperationRunCapabilityResolver.php` - [x] T004 Update `app/Policies/OperationRunPolicy.php` to enforce clarified 404/403 semantics (non-member 404; member missing capability 403) using T003 - [x] T005 [P] Add/extend operation type registry for new types in `app/Support/OperationRunType.php` - [x] T006 [P] Add/extend operation labels/catalog entries in `app/Support/OperationCatalog.php` - [x] T007 Add tests covering view authorization semantics in `tests/Feature/Operations/TenantlessOperationRunViewerTest.php` (404 vs 403 + capability-gated view) **Checkpoint**: Canonical viewer authorization matches spec; new run types exist in registries. --- ## Phase 3: User Story 1 — Start an operation with an immediate canonical run link (Priority: P1) **Goal**: All start surfaces create an `operation_runs` record at dispatch time; no job fallback-create; “View run” link is stable. **Independent Test**: Start each in-scope operation and assert the `operation_runs` row exists before work begins, with correct type/identity/context and a stable tenantless view URL. ### Tests (US1) - [x] T008 [P] [US1] Add/extend tests for OperationRun dispatch-time creation in `tests/Feature/OperationRunServiceTest.php` - [X] T009 [P] [US1] Add/extend tests for start-surface authorization (403 prevents run creation) in `tests/Feature/RunStartAuthorizationTest.php` ### Implementation (US1) - [X] T010 [US1] Ensure inventory sync start surface creates OperationRun before dispatch and uses canonical link in `app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php` - [X] T011 [US1] Ensure directory groups sync start surface creates OperationRun before dispatch and uses canonical link in `app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php` - [X] T012 [US1] Ensure backup schedule manual run-now creates OperationRun before dispatch with unique-per-click identity (nonce) in `app/Filament/Resources/BackupScheduleResource.php` - [X] T013 [US1] Ensure backup schedule retry creates OperationRun before dispatch with unique-per-click identity (nonce) in `app/Filament/Resources/BackupScheduleResource.php` - [x] T014 [US1] Ensure scheduled backup dispatcher creates OperationRun before dispatch with strict identity by (schedule_id, scheduled_for) and type `backup_schedule.scheduled` in `app/Services/BackupScheduling/BackupScheduleDispatcher.php` - [x] T014a [US1] Enforce strict scheduled backup idempotency (at most one canonical run ever per schedule_id + intended fire-time), using an explicit DB constraint and/or lock strategy aligned with `OperationRunService` identities - [x] T015 [US1] Enforce “no job fallback-create” by validating required OperationRun context is present; fail fast if missing in `app/Jobs/RunInventorySyncJob.php`, `app/Jobs/EntraGroupSyncJob.php`, and `app/Jobs/RunBackupScheduleJob.php` ### Restore (US1) - [X] T015a [P] [US1] Add/extend tests that starting a restore execution creates an OperationRun at dispatch time (target existing restore execution tests under `tests/Feature/RestoreRunWizardExecuteTest.php` and/or `tests/Feature/ExecuteRestoreRunJobTest.php`) - [X] T015b [US1] Ensure the restore execution start surface creates OperationRun before dispatch and surfaces the stable canonical “View run” link (adjust the Filament restore execution action/page used in the wizard flow) - [x] T015c [US2] Ensure restore domain records link to canonical OperationRuns for observability (align with FR-014; no legacy fallback-create) **Checkpoint**: Starting operations always yields a stable `/admin/operations/{run}` link immediately. --- ## Phase 4: User Story 2 — Monitor executions from a single canonical viewer (Priority: P2) **Goal**: Canonical viewer and Monitoring pages remain DB-only; legacy run history pages are read-only and redirect only when a deterministic mapping exists. **Independent Test**: Load canonical viewer and legacy view pages while asserting no outbound Graph calls occur during render/search/label callbacks. ### Tests (US2) - [X] T016 [P] [US2] Add tests asserting Monitoring pages render DB-only (no Graph calls) in `tests/Feature/Monitoring/MonitoringOperationsTest.php` - [X] T017 [P] [US2] Add tests for legacy-to-canonical redirect when mapping exists and no redirect when mapping absent in `tests/Feature/Operations/` (new file: `tests/Feature/Operations/LegacyRunRedirectTest.php`) ### Implementation (US2) - [X] T018 [US2] Add nullable `operation_run_id` mapping column + FK/index to legacy table `inventory_sync_runs` (new migration in `database/migrations/**_add_operation_run_id_to_inventory_sync_runs_table.php`) - [X] T019 [US2] Add nullable `operation_run_id` mapping column + FK/index to legacy table `entra_group_sync_runs` (new migration in `database/migrations/**_add_operation_run_id_to_entra_group_sync_runs_table.php`) - [X] T020 [US2] Add nullable `operation_run_id` mapping column + FK/index to legacy table `backup_schedule_runs` (new migration in `database/migrations/**_add_operation_run_id_to_backup_schedule_runs_table.php`) - [x] T021 [US2] Stop writing NEW legacy run rows for inventory sync and use `operation_runs` only for execution tracking (adjust service + callers in `app/Services/Inventory/InventorySyncService.php` and any start surfaces) - [x] T022 [US2] Stop writing NEW legacy run rows for directory group sync and use `operation_runs` only for execution tracking (adjust service + callers in `app/Services/Directory/EntraGroupSyncService.php` and any start surfaces) - [x] T023 [US2] Stop writing NEW legacy run rows for backup schedule executions and use `operation_runs` only for execution tracking; keep legacy table strictly read-only history for existing rows (adjust dispatcher and UI surfaces in `app/Services/BackupScheduling/BackupScheduleDispatcher.php` and `app/Filament/Resources/BackupScheduleResource.php`) - [x] T023a [US2] Update Backup Schedule UI to show new executions from `operation_runs` (query by type + context like schedule_id) and link to canonical viewer; legacy runs list remains history-only - [X] T024 [US2] Implement deterministic redirect on legacy “view” pages when `operation_run_id` exists in `app/Filament/Resources/InventorySyncRunResource/Pages/ViewInventorySyncRun.php` and `app/Filament/Resources/EntraGroupSyncRunResource/Pages/ViewEntraGroupSyncRun.php` - [x] T025 [US2] Ensure legacy run history pages remain strictly read-only (remove/disable start/retry actions) in `app/Filament/Resources/InventorySyncRunResource.php`, `app/Filament/Resources/EntraGroupSyncRunResource.php`, and `app/Filament/Resources/BackupScheduleResource/RelationManagers/BackupScheduleRunsRelationManager.php` **Checkpoint**: Canonical viewer is the only execution-tracker UI; legacy is view-only and redirects only when mapped. --- ## Phase 5: User Story 3 — Use cached directory data in forms without blocking calls (Priority: P3) **Goal**: Tenant configuration selectors use cached directory groups + cached role definitions; “Sync now” triggers async sync with an immediate canonical run link; no outbound calls during render/search/label callbacks. **Independent Test**: Render Tenant configuration forms and exercise search/label callbacks while asserting Graph client is not called. ### Tests (US3) - [x] T026 [P] [US3] Add tests that TenantResource role definition selectors render/search DB-only (no Graph calls) in `tests/Feature/Filament/` (new file: `tests/Feature/Filament/TenantRoleDefinitionsSelectorDbOnlyTest.php`) - [x] T027 [P] [US3] Add tests that “Sync now” creates an OperationRun and returns a canonical view link in `tests/Feature/DirectoryGroups/` or `tests/Feature/TenantRBAC/` (choose closest existing folder) - [x] T027a [P] [US3] Add tests that directory group selectors render/search DB-only (no Graph calls) and use cached DB tables (new file under `tests/Feature/DirectoryGroups/` or `tests/Feature/Filament/`) ### Implementation (US3) - [x] T028 [US3] Create cached role definitions table + model + factory (new migration in `database/migrations/**_create_entra_role_definitions_table.php`, model in `app/Models/EntraRoleDefinition.php`, factory in `database/factories/EntraRoleDefinitionFactory.php`) - [x] T029 [US3] Add “role definitions sync” operation type `directory_role_definitions.sync` to `app/Support/OperationRunType.php` and label in `app/Support/OperationCatalog.php` (if not already completed in T005/T006) - [x] T030 [US3] Implement role definitions sync service + job that updates the cache and records progress/failures on the OperationRun (service in `app/Services/Directory/RoleDefinitionsSyncService.php`, job in `app/Jobs/SyncRoleDefinitionsJob.php`) - [x] T030a [US3] Register/verify Graph contract entries required for role definitions sync in `config/graph_contracts.php` and ensure the sync uses `GraphClientInterface` only (no ad-hoc endpoints) - [x] T031 [US3] Update `app/Filament/Resources/TenantResource.php` roleDefinitions search/label callbacks to query cached DB tables only (remove Graph calls from callbacks) - [x] T032 [US3] Add a non-destructive “Sync now” Filament action that dispatches `directory_role_definitions.sync` and provides a canonical “View run” link (in `app/Filament/Resources/TenantResource.php`) **Checkpoint**: Tenant configuration selectors are DB-only; cache sync is async and observable via canonical run. --- ## Phase 6: Polish & Cross-Cutting Concerns - [x] T033 Ensure new/modified destructive-like actions (if any) use `Action::make(...)->action(...)->requiresConfirmation()` and are authorized server-side (audit existing touched Filament actions under `app/Filament/**`) - [x] T034 Run Pint on changed files via `vendor/bin/sail bin pint --dirty` - [x] T035 Run targeted test subset per quickstart: `vendor/bin/sail artisan test --compact --filter=OperationRun` and the new/changed test files - [x] T036 Allow re-running onboarding verification while status is `in_progress` (prevents dead-end when a prior run is stuck and the current connection would immediately block with next steps) in `app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php` - [x] T037 Auto-fail stale queued provider operation runs to allow rerun (prevents permanent dedupe when a worker isn’t running) in `app/Services/Providers/ProviderOperationStartGate.php` --- ## Dependencies & Execution Order ### Phase Dependencies - Setup (Phase 1) → Foundational (Phase 2) → US1 (Phase 3) → US2 (Phase 4) → US3 (Phase 5) → Polish (Phase 6) ### User Story Dependencies - US1 is the MVP: it enables stable canonical run creation + links. - US2 depends on Foundational + US1 (viewer/auth semantics), but can be implemented in parallel once viewer auth is stable. - US3 depends on Foundational + cache primitives, but can proceed after Foundational even if US2 is in progress. ### Parallel Execution Examples - US1 parallelizable: T008 + T009 (tests) can be written in parallel; start-surface patches T010–T014 can be split across different files. - US2 parallelizable: migrations T018–T020 can be done in parallel; legacy resource updates T024–T025 can be split by resource. - US3 parallelizable: schema/model/factory T028 can be done while tests T026–T027 are being drafted.