---

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 (3–5 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 3000–5000ms

### 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 T004–T013.
- **US2 (P2)** depends on Phase 2 (Foundational) tasks T004–T013.
- **US3 (P3)** depends on Phase 2 (Foundational) tasks T004–T013.
- **US4 (P4)** depends on completion of US1–US3 (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: T005–T007 can be done in parallel.
- US1 migration tasks T016–T025 are parallelizable (different files).
- US4 tests T044–T046 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.
