TenantAtlas/specs/055-ops-ux-rollout/tasks.md
2026-01-18 14:44:16 +01:00

209 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: "Task list for Ops-UX Constitution Rollout (v1.3.0 Alignment) (055)"
---
# Tasks: Ops-UX Constitution Rollout (v1.3.0 Alignment) (055)
**Input**: Design documents from `specs/055-ops-ux-rollout/`
**Tests**: REQUIRED (Pest) — runtime behavior + UX contract enforcement.
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Create a clean workspace for implementing and testing this migration.
- [x] T001 Create Ops UX test folder structure in `tests/Feature/OpsUx/`
- [x] T002 [P] Add a dedicated test file stub in `tests/Feature/OpsUx/OpsUxSmokeTest.php`
- [x] T003 [P] Add a shared test helper (factory/state helpers) in `tests/Support/OpsUxTestSupport.php`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Shared primitives (catalog/links/presenter/normalization) that every user story depends on.
- [x] T004 Update runtime unknown-type label behavior in `app/Support/OperationCatalog.php` (render `Unknown operation`, never raw type)
- [x] T005 [P] Add shared presenter for toast/widget/notification copy in `app/Support/OpsUx/OperationUxPresenter.php`
- [x] T059 Implement a single status normalization function for Ops-UX rendering
- Map legacy completed/outcome values to canonical terminal statuses (`succeeded|partial|failed`)
- Ensure widget and notifications consume normalized status
- [x] T006 [P] Add summary_counts normalizer + whitelist enforcement in `app/Support/OpsUx/SummaryCountsNormalizer.php`
- [x] T060 Consolidate allowed summary keys into one code constant/source (no duplicated lists)
- Example shape: `OperationSummaryKeys::all()` (or similar)
- Normalizer MUST reference that source
- Catalog (if it references keys) MUST reference the same source
- Add one guard test asserting the list matches spec.md canonical list
- [x] T007 [P] Add canonical “View run” URL helper wrapper in `app/Support/OpsUx/OperationRunUrl.php` (delegates to `app/Support/OperationRunLinks.php`)
- [x] T008 Update summary key whitelist consumption in `app/Support/OperationCatalog.php` to reference the consolidated source (see T060)
- [x] T009 Update summary sanitization to use shared normalizer in `app/Services/OperationRunService.php`
- [x] T010 Update OperationRun completed notification to use shared presenter + normalizer in `app/Notifications/OperationRunCompleted.php`
- [x] T011 Disable queued DB notification emission by default in `app/Services/OperationRunService.php` (align with FR-010c)
- [x] T012 Deprecate/stop using queued DB notification class in `app/Notifications/OperationRunQueued.php` (keep class but ensure no producers call it)
- [x] T013 Ensure all “View run” actions inside operation notifications use canonical URL helper in `app/Notifications/OperationRunCompleted.php`
**Checkpoint**: Shared contracts available; user story work can proceed.
---
## Phase 3: User Story 1 — Consistent “I started it” feedback (Priority: P1) 🎯 MVP
**Goal**: Starting any operation shows queued-only intent feedback with canonical “View run”.
**Independent Test**: Trigger an operation start from a Filament action; observe a queued-only toast (`{OperationLabel} queued` / `Running in the background.`) and verify no queued DB notification is created.
### Tests for User Story 1
- [x] T014 [P] [US1] Add test ensuring no queued DB notifications are emitted in `tests/Feature/OpsUx/NoQueuedDbNotificationsTest.php`
- [x] T015 [P] [US1] Add test for canonical queued toast copy builder in `tests/Feature/OpsUx/QueuedToastCopyTest.php`
- [x] T057 [US1] Enforce toast auto-dismiss duration (35 seconds) for queued intent feedback (set duration explicitly, e.g. 4000ms)
- [x] T058 [P] [US1] (Optional guard) Centralize toast duration in `OperationUxPresenter` and add a small unit test to keep it within 30005000ms
### Implementation for User Story 1
- [x] T016 [P] [US1] Migrate queued toast copy for policy operations in `app/Filament/Resources/PolicyResource.php` (use `OperationUxPresenter`)
- [x] T017 [P] [US1] Migrate queued toast copy for policy version operations in `app/Filament/Resources/PolicyVersionResource.php` (use `OperationUxPresenter`)
- [x] T018 [P] [US1] Migrate queued toast copy for restore run operations in `app/Filament/Resources/RestoreRunResource.php` (use `OperationUxPresenter`)
- [x] T019 [P] [US1] Migrate queued toast copy for backup schedule operations in `app/Filament/Resources/BackupScheduleResource.php` (use `OperationUxPresenter`)
- [x] T020 [P] [US1] Migrate queued toast copy for backup set operations in `app/Filament/Resources/BackupSetResource.php` (use `OperationUxPresenter`)
- [x] T021 [P] [US1] Migrate queued toast copy for tenant sync operations in `app/Filament/Resources/TenantResource.php` (use `OperationUxPresenter`)
- [x] T022 [P] [US1] Migrate queued toast copy for policy view page operations in `app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php` (use `OperationUxPresenter`)
- [x] T023 [P] [US1] Migrate queued toast copy for inventory landing operations in `app/Filament/Pages/InventoryLanding.php` (use `OperationUxPresenter`)
- [x] T024 [P] [US1] Migrate queued toast copy for drift landing operations in `app/Filament/Pages/DriftLanding.php` (use `OperationUxPresenter`)
- [x] T025 [P] [US1] Migrate queued toast copy for backup-set policy picker operations in `app/Livewire/BackupSetPolicyPickerTable.php` (use `OperationUxPresenter`)
**Checkpoint**: A user can start operations and get consistent queued intent feedback.
---
## Phase 4: User Story 2 — Live awareness of active operations (Priority: P2)
**Goal**: A single global widget shows only tenant-scoped queued/running runs with strict copy and canonical links.
**Independent Test**: Create active runs in the DB; the widget shows max 5 rows, each row has `Queued`/`Running` only, and terminal runs never render.
### Tests for User Story 2
- [x] T026 [P] [US2] Add widget filtering test (never show terminal) in `tests/Feature/OpsUx/ProgressWidgetFiltersTest.php`
- [x] T027 [P] [US2] Add widget max-5 + overflow link test in `tests/Feature/OpsUx/ProgressWidgetOverflowTest.php`
- [x] T062 [US2] Add restore execution → OperationRun sync regression test in `tests/Feature/OpsUx/RestoreExecutionOperationRunSyncTest.php`
### Implementation for User Story 2
- [x] T028 [US2] Migrate widget query from BulkOperationRun to OperationRun in `app/Livewire/BulkOperationProgress.php`
- [x] T029 [US2] Enforce tenant-wide scope + Monitoring access guard in `app/Livewire/BulkOperationProgress.php`
- [x] T030 [US2] Update widget UI strings + strict status text in `resources/views/livewire/bulk-operation-progress.blade.php`
- [x] T031 [US2] Implement max-5 + overflow link behavior in `resources/views/livewire/bulk-operation-progress.blade.php`
- [x] T032 [US2] Use canonical “View run” URLs in widget rows in `resources/views/livewire/bulk-operation-progress.blade.php` (via `OperationRunUrl` / `OperationRunLinks`)
- [x] T033 [US2] No % in widget; widget may show elapsed time only
- [x] T034 [US2] Implement calm polling schedule + pause rules in `resources/views/livewire/bulk-operation-progress.blade.php`
- [x] T063 [US2] Dispatch `ops-ux:run-enqueued` browser event after successful enqueue so the widget refreshes immediately
- Producers: `app/Filament/Pages/InventoryLanding.php`, `app/Filament/Pages/DriftLanding.php`, `app/Filament/Resources/BackupScheduleResource.php`, `app/Filament/Resources/PolicyResource.php`, `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php`, `app/Filament/Resources/RestoreRunResource.php`, `app/Filament/Resources/TenantResource.php`, `app/Livewire/BackupSetPolicyPickerTable.php`, `app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php`
- [x] T035 [US2] Confirm widget injection remains global and consistent in `app/Providers/Filament/AdminPanelProvider.php`
### Run detail polling (missing coverage for FR-013)
- [x] T053 [US2] Add run-detail polling controller/hook that applies calm polling while status is active (`queued|running`) (only poll when run detail is visible; stop immediately on terminal; backoff 1s (first 10s) → 5s → 10s (after 60s))
- [x] T054 [US2] Pause run-detail polling when a modal is open (global modal flag) and resume when closed (no network update spam while confirm dialogs/modals are open)
- [x] T055 [US2] Pause run-detail polling when browser tab is hidden (Page Visibility API) and resume when visible (no polling when `document.hidden = true`)
- [x] T056 [P] [US2] Add a small guard test/component test that run-detail polling is disabled once the run becomes terminal
- [x] T061 [US2] Surface elapsed time + expected duration + stuck guidance in run detail
**Checkpoint**: Widget is constitution-compliant and becomes the single active-ops surface.
---
## Phase 5: User Story 3 — Audit + outcome without spam (Priority: P3)
**Goal**: Exactly one terminal DB notification per run (initiator-only) with canonical copy, canonical link, safe summary.
**Independent Test**: Transition a run to terminal multiple times (or call completion twice); verify only one DB notification exists and it contains only whitelisted numeric summary keys.
### Tests for User Story 3
- [x] T036 [P] [US3] Add terminal notification idempotency test in `tests/Feature/OpsUx/TerminalNotificationIdempotencyTest.php`
- [x] T037 [P] [US3] Add summary whitelist + numeric-only test in `tests/Feature/OpsUx/SummaryCountsWhitelistTest.php`
- [x] T038 [P] [US3] Add canonical “View run” action test for notifications in `tests/Feature/OpsUx/NotificationViewRunLinkTest.php`
### Implementation for User Story 3
- [x] T039 [US3] Refactor terminal notification copy/title/body to use presenter in `app/Notifications/OperationRunCompleted.php`
- [x] T040 [US3] Ensure initiator-only delivery is enforced in `app/Services/OperationRunService.php`
- [x] T041 [US3] Ensure terminal notification is emitted exactly once per run in `app/Services/OperationRunService.php`
- [x] T042 [US3] Ensure notification summary renders only normalized `summary_counts` in `app/Notifications/OperationRunCompleted.php`
- [x] T043 [US3] Ensure failure message suffix is sanitized + short in `app/Notifications/OperationRunCompleted.php`
**Checkpoint**: Terminal outcomes are auditable without spam.
---
## Phase 6: User Story 4 — Regression-safe by default (Priority: P4)
**Goal**: Guards prevent drift (catalog coverage, canonical links, surface rules, summary rules).
**Independent Test**: Introduce a fake operation type in code and confirm tests fail; confirm “View run” always resolves to the canonical Monitoring run-detail destination.
### Tests for User Story 4
- [ ] T044 [P] [US4] Add catalog coverage guard test in `tests/Feature/OpsUx/OperationCatalogCoverageTest.php`
- [ ] T045 [P] [US4] Add canonical “View run” helper usage guard test in `tests/Feature/OpsUx/CanonicalViewRunLinksTest.php`
- [ ] T046 [P] [US4] Add unknown-type runtime label test in `tests/Feature/OpsUx/UnknownOperationTypeLabelTest.php`
### Implementation for User Story 4
- [x] T047 [US4] Ensure OperationRunResource type label rendering never shows raw type in `app/Filament/Resources/OperationRunResource.php`
- [x] T048 [US4] Ensure Monitoring Operations page type labels never show raw type in `app/Filament/Pages/Monitoring/Operations.php`
- [ ] T049 [US4] Ensure any remaining “View run” links use canonical helper in `app/Support/OperationRunLinks.php`
**Checkpoint**: Drift prevention is enforced in CI.
---
## Phase 7: Polish & Cross-Cutting Concerns
- [x] T050 [P] Run Pint autofix for touched files via `app/` and `tests/` (validate against `composer.json` scripts)
- [x] T051 Run targeted test suite for Ops UX via `tests/Feature/OpsUx/` (document exact filter in `specs/055-ops-ux-rollout/quickstart.md`)
- [x] T052 [P] Remove or update any stale queued-notification references in `app/Services/OperationRunService.php`
---
## Dependencies & Execution Order
### User Story Dependencies
- **US1 (P1)** depends on Phase 2 (Foundational) tasks T004T013.
- **US2 (P2)** depends on Phase 2 (Foundational) tasks T004T013.
- **US3 (P3)** depends on Phase 2 (Foundational) tasks T004T013.
- **US4 (P4)** depends on completion of US1US3 (guards should reflect final behavior).
### Recommended completion order
1. Phase 2 (Foundational)
2. US1 (queued toast + no queued DB notifications)
3. US3 (terminal notification contract)
4. US2 (widget)
5. US4 (guards)
## Parallel Opportunities
- Within Phase 2: T005T007 can be done in parallel.
- US1 migration tasks T016T025 are parallelizable (different files).
- US4 tests T044T046 can be written in parallel.
## Parallel Example: User Story 1
- Task: T016 (PolicyResource) + T017 (PolicyVersionResource) + T018 (RestoreRunResource) can run in parallel.
- Task: T019 (BackupScheduleResource) + T020 (BackupSetResource) can run in parallel.
## Implementation Strategy
### MVP scope
Ship US1 + the minimum foundational primitives (Phase 2) to guarantee:
- queued-only toast copy is consistent
- queued DB notifications are banned
- canonical “View run” destination is available
Then layer US3 (terminal notification) before US2 (widget) to ensure audit outcomes are reliable early.