# Tasks: Operations Tenantless Canonical Migration

**Input**: Design documents from `/specs/078-operations-tenantless-canonical/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/

**Tests**: Tests are REQUIRED (Pest). This feature changes runtime routing and rendering behavior.
**Operations**: No new operations introduced. Existing `OperationRun` model is read-only in this feature.
**RBAC**: Authorization unchanged — workspace membership enforced via `OperationRunPolicy::view()`. Non-member gets 404 (deny-as-not-found). No new capabilities, no destructive actions.
**Badges**: No badge changes. Existing `BadgeRenderer` reused via shared infolist schema.

**Organization**: Tasks are grouped by user story. US1 (P1) is the MVP. US2/US3 (P2) can proceed in parallel after foundational phase. US4 (P3) is independent.

## Format: `[ID] [P?] [Story] Description`

- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: Which user story this task belongs to (US1, US2, US3, US4)
- Exact file paths included in descriptions

## Path Conventions

- Standard Laravel monolith: `app/`, `resources/`, `routes/`, `tests/`, `config/`
- New spec tests: `tests/Feature/078/`

---

## Phase 1: Setup

**Purpose**: Create test directory and verify branch readiness

- [X] T001 Create spec test directory `tests/Feature/078/`
- [X] T002 Verify branch is clean and on `078-operations-tenantless-canonical`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Make `OperationRunResource` headless, delete decommissioned pages, delete dead code, and fix all existing tests that reference deleted classes/routes. This phase MUST complete before any user story work.

**Why blocking**: US1 needs ViewOperationRun deleted so infolist lives on standalone page. US2 needs routes removed. US3 needs the viewer to be the sole detail surface. All existing tests must pass after this phase.

- [X] T003 Change `getPages()` to return `[]` in `app/Filament/Resources/OperationRunResource.php`
- [X] T004 [P] Delete `app/Filament/Resources/OperationRunResource/Pages/ViewOperationRun.php`
- [X] T005 [P] Delete `app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php`
- [X] T006 [P] Delete `app/Livewire/Monitoring/OperationsDetail.php` (dead code)
- [X] T007 [P] Delete `resources/views/livewire/monitoring/operations-detail.blade.php` (dead code)
- [X] T008 Update `tests/Feature/Verification/VerificationAuthorizationTest.php` — replace `OperationRunResource::getUrl('view', ...)` with `route('admin.operations.view', ...)` and replace `ViewOperationRun` Livewire mount with `TenantlessOperationRunViewer`
- [X] T009 [P] Update `tests/Feature/OpsUx/FailureSanitizationTest.php` — replace `OperationRunResource::getUrl('view', ...)` with canonical route; replace `ViewOperationRun` mount with `TenantlessOperationRunViewer`
- [X] T010 [P] Update `tests/Feature/OpsUx/CanonicalViewRunLinksTest.php` — update guard regex to account for headless resource (no `getUrl` calls exist)
- [X] T011 [P] Update `tests/Feature/Verification/VerificationReportViewerDbOnlyTest.php` — replace `ViewOperationRun` mount with `TenantlessOperationRunViewer`
- [X] T012 [P] Update `tests/Feature/Verification/VerificationReportRedactionTest.php` + `tests/Feature/Verification/VerificationReportMissingOrMalformedTest.php` — replace `ViewOperationRun` mounts with `TenantlessOperationRunViewer`
- [X] T013 [P] Update `tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php` — remove `ListOperationRuns` test block; add route-not-registered assertion for `filament.admin.resources.operations.index`
- [X] T014 [P] Update `tests/Feature/Monitoring/OperationsTenantScopeTest.php` — remove `ListOperationRuns` reference
- [X] T015 Run `grep -r "ViewOperationRun\|ListOperationRuns" app/ tests/ resources/` to verify no stale references remain
- [X] T016 Run existing test suite for affected files: `vendor/bin/sail artisan test --compact tests/Feature/Verification/ tests/Feature/OpsUx/ tests/Feature/Monitoring/ tests/Feature/Operations/`

**Checkpoint**: All existing tests pass. Resource is headless. Dead code removed. No stale references.

---

## Phase 3: User Story 1 — View operation run via canonical URL (Priority: P1) MVP

**Goal**: The canonical detail page at `/admin/operations/{run}` renders with full Filament infolist (summary, target scope, verification report, counts, context JSON) — replacing the current hand-coded Blade with `OperationRunResource::infolist()` reuse via the unified schema system.

**Independent Test**: Create runs with and without `tenant_id`, visit `/admin/operations/{run}`, assert all infolist sections render.

### Tests for User Story 1

- [X] T017 [P] [US1] Write test T-078-001 (canonical detail renders with tenant_id) in `tests/Feature/078/CanonicalDetailRenderTest.php` — create OperationRun with tenant_id, visit canonical URL as workspace member, assert 200 + infolist sections visible (status badge, outcome, timestamps, target scope, summary counts)
- [X] T018 [P] [US1] Write test T-078-001 (canonical detail renders without tenant_id) in `tests/Feature/078/CanonicalDetailRenderTest.php` — create OperationRun with tenant_id=null, visit canonical URL, assert 200 + graceful rendering ("No target scope details")
- [X] T019 [P] [US1] Write test T-078-001 (non-member gets 404) in `tests/Feature/078/CanonicalDetailRenderTest.php` — visit as non-member, assert 404
- [X] T020 [P] [US1] Write test T-078-008 (verification report renders tenantless) in `tests/Feature/078/VerificationReportTenantlessTest.php` — create run with verification_report in context, visit canonical URL, assert verification section renders with badges and acknowledgements
- [X] T021 [P] [US1] Write test T-078-007 (DB-only rendering) in `tests/Feature/078/CanonicalDetailRenderTest.php` — assert canonical detail rendering does not dispatch jobs or HTTP calls

### Implementation for User Story 1

- [X] T022 [US1] Add `public function infolist(Schema $schema): Schema` to `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` — delegates to `OperationRunResource::infolist($schema)`
- [X] T023 [US1] Add `public function defaultInfolist(Schema $schema): Schema` to `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` — sets `->record($this->run)->columns(2)`
- [X] T024 [US1] Add `public bool $opsUxIsTabHidden = false` property to `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` (required for polling callback in infolist)
- [X] T025 [US1] Add `public function content(Schema $schema): Schema` to `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` returning `EmbeddedSchema::make('infolist')`
- [X] T026 [US1] Replace hand-coded HTML in `resources/views/filament/pages/operations/tenantless-operation-run-viewer.blade.php` with infolist render (`{{ $this->infolist }}`)
- [X] T027 [US1] Run tests: `vendor/bin/sail artisan test --compact tests/Feature/078/CanonicalDetailRenderTest.php tests/Feature/078/VerificationReportTenantlessTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php`

**Checkpoint**: Canonical detail at `/admin/operations/{run}` renders full Filament infolist. Runs with and without tenant_id render correctly. MVP is functional.

---

## Phase 4: User Story 2 — Legacy tenant-scoped detail URLs return 404 (Priority: P2)

**Goal**: Verify that decommissioned tenant-scoped routes naturally return 404. No redirect handlers, no existence leakage.

**Independent Test**: Hit legacy detail URLs; assert 404 for all users.

**Note**: The actual route removal was done in Phase 2 (Foundational). This phase adds the spec-required tests that validate the behavior.

### Tests for User Story 2

- [X] T028 [P] [US2] Write test T-078-002 (legacy detail URL returns 404) in `tests/Feature/078/LegacyRoutesReturnNotFoundTest.php` — visit `/admin/t/{tenant}/operations/r/{record}` as any user, assert 404
- [X] T029 [P] [US2] Write test T-078-002 (slug variant returns 404) in `tests/Feature/078/LegacyRoutesReturnNotFoundTest.php` — visit `/admin/operations/r/{record}`, assert 404
- [X] T030 [P] [US2] Write test T-078-004 (auto-generated route names not registered) in `tests/Feature/078/LegacyRoutesReturnNotFoundTest.php` — assert `Route::has('filament.admin.resources.operations.view')` is false and `Route::has('filament.admin.resources.operations.index')` is false

### Implementation for User Story 2

> No additional implementation needed — routes were removed in Phase 2.

- [X] T031 [US2] Run tests: `vendor/bin/sail artisan test --compact tests/Feature/078/LegacyRoutesReturnNotFoundTest.php`

**Checkpoint**: All legacy URLs return 404. Route names are not registered. No existence leakage.

---

## Phase 5: User Story 3 — Contextual navigation from run detail (Priority: P2)

**Goal**: Replace the "Admin details" button on canonical detail with `OperationRunLinks::related()` action group in header actions — providing richer contextual navigation (Operations index, Policy, Backup Set, Restore Run, etc.).

**Independent Test**: Create runs of different types, verify related links appear in header actions and "Admin details" link is absent.

### Tests for User Story 3

- [X] T032 [P] [US3] Write test T-078-010 (related links appear) in `tests/Feature/078/RelatedLinksOnDetailTest.php` — create run of type `restore.execute` with `restore_run_id` in context, visit canonical detail, assert header actions include "Restore Run" link
- [X] T033 [P] [US3] Write test T-078-010 (generic links for tenantless run) in `tests/Feature/078/RelatedLinksOnDetailTest.php` — create run with tenant_id=null, assert only generic links (Operations index) appear
- [X] T034 [P] [US3] Write tests T-078-005 + T-078-012 in `tests/Feature/078/RelatedLinksOnDetailTest.php` — assert canonical detail does not render `/admin/t/.../operations/r/...` links and uses exact CTA label "View run" (legacy "Admin details" absent)

### Implementation for User Story 3

- [X] T035 [US3] Add `getHeaderActions()` method to `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` — return actions from `OperationRunLinks::related($this->run, $this->run->tenant)`
- [X] T036 [US3] Remove "Admin details" button code (~line 61) from `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`
- [X] T037 [US3] Run tests: `vendor/bin/sail artisan test --compact tests/Feature/078/RelatedLinksOnDetailTest.php`

**Checkpoint**: Header shows contextual "Open" action group. "Admin details" link is gone. Related links vary by run type.

---

## Phase 6: User Story 4 — Operations list remains workspace-scoped (Priority: P3)

**Goal**: Ensure no regression on `/admin/operations` list. KPI header hidden in tenantless mode. 302 redirect for decommissioned list URL.

**Independent Test**: Visit `/admin/operations` with and without tenant context; verify workspace scoping, KPI behavior, and redirect.

### Tests for User Story 4

- [X] T038 [P] [US4] Write test T-078-006 (KPI header hidden without tenant) in `tests/Feature/078/KpiHeaderTenantlessTest.php` — visit `/admin/operations` without tenant context, assert KPI stats are not rendered (empty array from `getStats()`)
- [X] T039 [P] [US4] Write test T-078-009 (tenant-scoped list redirect) in `tests/Feature/078/TenantListRedirectTest.php` — visit `/admin/t/{tenant}/operations`, assert 302 redirect to `/admin/operations`
- [X] T048 [P] [US4] Write test T-078-011 (tenantless list query safety) in `tests/Feature/078/OperationsListTenantlessSafetyTest.php` — visit `/admin/operations` with and without tenant context, assert runs including `tenant_id = null` render without errors

### Implementation for User Story 4

- [X] T040 [US4] Add tenant-null guard in `getStats()` of `app/Filament/Widgets/Operations/OperationsKpiHeader.php` — if `Filament::getTenant()` is null, return `[]`
- [X] T041 [US4] Add 302 redirect route in `routes/web.php` — `/admin/t/{tenant}/operations` redirects to `/admin/operations` (FR-078-012)
- [X] T042 [US4] Run tests: `vendor/bin/sail artisan test --compact tests/Feature/078/KpiHeaderTenantlessTest.php tests/Feature/078/TenantListRedirectTest.php tests/Feature/078/OperationsListTenantlessSafetyTest.php`

**Checkpoint**: Operations list works in workspace mode. KPI hidden without tenant. List redirect works.

---

## Phase 7: Polish & Cross-Cutting Concerns

**Purpose**: Final validation, formatting, and stale reference sweep

- [X] T043 Run `grep -r "ViewOperationRun\|ListOperationRuns\|OperationsDetail" app/ tests/ resources/` — verify zero stale references across entire codebase
- [X] T044 Run `vendor/bin/sail bin pint --dirty` — fix any formatting issues
- [X] T049 Remove obsolete temporary layout `resources/views/filament/layouts/topbar-only.blade.php`
- [X] T045 Run focused test pack: `vendor/bin/sail artisan test --compact tests/Feature/078/ tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationReportViewerDbOnlyTest.php tests/Feature/Verification/VerificationReportRedactionTest.php tests/Feature/Verification/VerificationReportMissingOrMalformedTest.php tests/Feature/OpsUx/FailureSanitizationTest.php tests/Feature/OpsUx/CanonicalViewRunLinksTest.php`
- [X] T046 Run quickstart.md validation: `vendor/bin/sail artisan route:list --name=filament.admin.resources.operations` — assert empty output
- [X] T047 Ask user if they want to run the full test suite: `vendor/bin/sail artisan test --compact`

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)**: No dependencies — start immediately
- **Foundational (Phase 2)**: Depends on Phase 1 — BLOCKS all user stories
- **US1 (Phase 3)**: Depends on Phase 2 completion (resource must be headless first)
- **US2 (Phase 4)**: Depends on Phase 2 completion (routes must be removed)
- **US3 (Phase 5)**: Depends on Phase 3 completion (viewer must have infolist before adding header actions)
- **US4 (Phase 6)**: Depends on Phase 2 completion only (independent of US1/US2/US3)
- **Polish (Phase 7)**: Depends on all desired user stories being complete

### User Story Dependencies

- **US1 (P1)**: After Phase 2 — no other story dependencies. **This is the MVP.**
- **US2 (P2)**: After Phase 2 — independent of US1 (tests only, implementation in Phase 2)
- **US3 (P2)**: After US1 (Phase 3) — needs infolist on viewer before adding header actions
- **US4 (P3)**: After Phase 2 — independent of US1/US2/US3

### Within Each User Story

- Tests written FIRST, verified to FAIL before implementation
- Implementation follows test guidance
- Story checkpoint validates independently

### Parallel Opportunities

After Phase 2 completes:
- **US1 and US2 and US4** can proceed in parallel (different files, no dependencies)
- US3 must wait for US1 (same file: TenantlessOperationRunViewer.php)

Within Phase 2:
- T004, T005, T006, T007 (file deletions) can all run in parallel
- T008-T014 (test file updates) can all run in parallel

Within Phase 3 (US1):
- T017-T021 (test writing) can all run in parallel
- T022-T026 (implementation) are sequential (same file)

---

## Parallel Example: After Phase 2

```
# US1 (developer/agent A):
T017-T021: Write US1 tests (parallel)
T022-T026: Implement infolist reuse (sequential, same file)
T027: Run US1 tests

# US2 (developer/agent B — can run simultaneously):
T028-T030: Write US2 tests (parallel)
T031: Run US2 tests

# US4 (developer/agent C — can run simultaneously):
T038-T039: Write US4 tests (parallel)
T040-T041: Implement KPI guard + redirect
T042: Run US4 tests

# US3 (must wait for US1):
T032-T034: Write US3 tests (parallel)
T035-T036: Implement related links
T037: Run US3 tests
```

---

## Implementation Strategy

### MVP First (User Story 1 Only)

1. Complete Phase 1: Setup
2. Complete Phase 2: Foundational (headless resource + dead code + test fixes)
3. Complete Phase 3: User Story 1 (infolist reuse on canonical detail)
4. **STOP and VALIDATE**: Canonical detail renders full infolist for all run types
5. This is deployable as MVP — core value delivered

### Incremental Delivery

1. Setup + Foundational -> Foundation ready (all existing tests pass)
2. Add US1 -> Canonical detail has full infolist (MVP!)
3. Add US2 -> Legacy URLs confirmed 404 (validation tests)
4. Add US3 -> Related links replace "Admin details" button
5. Add US4 -> KPI hidden + list redirect
6. Polish -> Full sweep, formatting, final validation

---

## Notes

- All tests use Pest (not PHPUnit class syntax)
- Use existing `OperationRun` factory for test data setup
- `TenantlessOperationRunViewer` is the sole detail surface after migration
- `OperationRunResource` retained as headless utility (provides `::table()` and `::infolist()`)
- No new migrations, no new models, no new dependencies
- Total: 47 tasks across 7 phases
