# Tasks: Tenant-Owned Query Canon and Wrong-Tenant Guards

**Input**: Design documents from `/specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

**Tests**: Tests are REQUIRED for this feature because it changes runtime authorization, query scoping, relation-manager behavior, and global-search safety in a Laravel/Pest codebase.

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Establish the rollout inventory and shared scaffolding used by all stories.

- [X] T001 Create the first-slice tenant-owned family inventory in `app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php`
- [X] T002 [P] Create shared tenant-owned scope scaffolding in `app/Support/WorkspaceIsolation/TenantOwnedQueryScope.php` and `app/Support/WorkspaceIsolation/TenantOwnedRecordResolver.php`
- [X] T003 [P] Create the feature guard baseline in `tests/Feature/Guards/TenantOwnedQueryGuardTest.php`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Put the canonical query and exception model in place before resource-specific work starts.

**⚠️ CRITICAL**: No user story work can begin until this phase is complete.

- [X] T004 Implement the canonical tenant-owned query contract in `app/Support/WorkspaceIsolation/TenantOwnedQueryScope.php`, `app/Support/WorkspaceIsolation/TenantOwnedRecordResolver.php`, and `app/Filament/Concerns/InteractsWithTenantOwnedRecords.php`
- [X] T005 [P] Wire panel-aware tenant resolution into the shared helper in `app/Filament/Concerns/ResolvesPanelTenantContext.php` and `app/Support/OperateHub/OperateHubShell.php`
- [X] T006 [P] Encode first-slice search posture and explicit scope exceptions in `app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php`, `app/Support/WorkspaceIsolation/TenantOwnedTables.php`, and `app/Filament/Concerns/ScopesGlobalSearchToTenant.php`
- [X] T007 Update the architectural guard allowlists for the shared tenant-owned helper entry points in `tests/Feature/Guards/AdminTenantResolverGuardTest.php` and `tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php`

**Checkpoint**: The shared tenant-owned query canon exists, first-slice search posture and exceptions are explicit, and user story work can proceed in parallel.

---

## Phase 3: User Story 1 - Trust tenant-owned lists and detail links (Priority: P1) 🎯 MVP

**Goal**: Ensure tenant-owned lists, detail pages, direct links, and safe-search paths only resolve records from the entitled tenant scope.

**Independent Test**: An operator entitled to two tenants can list and open only the correct tenant's records on covered surfaces, while wrong-tenant direct links resolve as 404 without leaking data.

### Tests for User Story 1

- [X] T008 [P] [US1] Extend Entra group index, detail, and safe-search wrong-tenant coverage in `tests/Feature/Filament/EntraGroupAdminScopeTest.php`
- [X] T009 [P] [US1] Add covered tenant-owned list, detail, and persisted tenant-state parity tests in `tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php`, `tests/Feature/Filament/CanonicalAdminTenantFilterStateTest.php`, and `tests/Feature/Rbac/InventoryItemResourceAuthorizationTest.php`
- [X] T010 [P] [US1] Extend policy and policy-version search posture coverage in `tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php` and `tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php`

### Implementation for User Story 1

- [X] T011 [US1] Migrate `app/Filament/Resources/EntraGroupResource.php` to the shared tenant-owned query and explicit record-resolution helper, and keep tenant-sensitive persisted table state aligned through `app/Support/Filament/CanonicalAdminTenantFilterState.php`
- [X] T012 [P] [US1] Migrate `app/Filament/Resources/BackupScheduleResource.php`, `app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php`, and `app/Filament/Resources/BackupSetResource.php` to the shared tenant-owned query helper and canonical tenant-state sync
- [X] T013 [P] [US1] Migrate `app/Filament/Resources/RestoreRunResource.php` and `app/Filament/Resources/InventoryItemResource.php` to the shared tenant-owned query helper while preserving tenant-safe persisted filter/search state through `app/Support/Filament/CanonicalAdminTenantFilterState.php`
- [X] T014 [P] [US1] Migrate `app/Filament/Resources/FindingResource.php`, `app/Filament/Resources/FindingResource/Pages/ListFindings.php`, `app/Filament/Resources/PolicyResource.php`, `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php`, and `app/Filament/Resources/PolicyVersionResource.php` to the shared tenant-owned query, search-posture helper, and tenant-state-safe list behavior
- [X] T015 [US1] Harden covered direct-view and canonical-viewer lookup paths in `routes/web.php`, `app/Policies/EntraGroupPolicy.php`, `app/Policies/BackupSchedulePolicy.php`, and `app/Policies/FindingPolicy.php`

**Checkpoint**: Covered tenant-owned list, detail, and direct-link paths are consistent and independently testable without story 2 or story 3 work.

---

## Phase 4: User Story 2 - Block wrong-tenant mutations and bulk actions (Priority: P1)

**Goal**: Ensure row actions, bulk actions, and relation-manager actions cannot target foreign-tenant records, even with forged identifiers.

**Independent Test**: Covered relation managers and protected actions reject foreign-tenant record IDs with no side effects, while in-scope capability denials still return 403 semantics.

### Tests for User Story 2

- [X] T016 [P] [US2] Extend backup item row-action and bulk-action wrong-tenant coverage in `tests/Feature/Rbac/BackupItemsRelationManagerSemanticsTest.php` and `tests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php`
- [X] T017 [P] [US2] Extend relation-manager and protected-action wrong-tenant coverage in `tests/Feature/Rbac/PolicyVersionsRestoreToIntuneUiEnforcementTest.php` and `tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php`
- [X] T018 [P] [US2] Add representative protected-action 404 and 403 parity tests in `tests/Feature/Findings/FindingRbacTest.php` and `tests/Feature/RunAuthorizationTenantIsolationTest.php`

### Implementation for User Story 2

- [X] T019 [US2] Anchor `app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php` actions to owner-record scope and reject forged foreign-tenant record IDs
- [X] T020 [P] [US2] Anchor `app/Filament/Resources/PolicyResource/RelationManagers/VersionsRelationManager.php` and `app/Filament/Resources/BackupScheduleResource/RelationManagers/BackupScheduleOperationRunsRelationManager.php` to owner-record scope and action-target congruence
- [X] T021 [US2] Normalize covered 404 versus 403 semantics in `app/Policies/BackupSchedulePolicy.php`, `app/Policies/FindingPolicy.php`, and `app/Policies/EntraGroupPolicy.php`
- [X] T022 [US2] Route covered protected mutation entry points through the shared tenant-owned record resolver in `app/Filament/Resources/BackupScheduleResource.php`, `app/Filament/Resources/FindingResource.php`, and `app/Filament/Resources/RestoreRunResource.php`

**Checkpoint**: Covered mutation and relation-manager flows are independently safe against wrong-tenant execution and can be validated without story 3 guardrail work.

---

## Phase 5: User Story 3 - Maintain one canonical query pattern (Priority: P2)

**Goal**: Make the tenant-owned query canon reusable and enforceable for future resource work.

**Independent Test**: The shared guardrail fails on new forbidden tenant-owned query patterns, and the covered resource families share the same regression matrix for index, detail, action, relation-manager, and search posture behavior.

### Tests for User Story 3

- [X] T023 [P] [US3] Implement the tenant-owned query architectural guard in `tests/Feature/Guards/TenantOwnedQueryGuardTest.php`
- [X] T024 [P] [US3] Extend cross-resource wrong-tenant matrix and Filament action-surface contract coverage in `tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php`, `tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php`, and `tests/Feature/Guards/ActionSurfaceContractTest.php`

### Implementation for User Story 3

- [X] T025 [US3] Finalize the reusable tenant-owned family registry, scope-exception map, and residual rollout inventory in `app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php`, `app/Support/WorkspaceIsolation/TenantOwnedTables.php`, and `specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/data-model.md`
- [X] T026 [US3] Integrate safe-search posture, Action Surface Contract verification, and explicit action-surface exemptions with `app/Filament/Concerns/ScopesGlobalSearchToTenant.php`, `app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php`, and `tests/Feature/Guards/ActionSurfaceContractTest.php`
- [X] T027 [US3] Remove remaining first-slice ad hoc tenant-owned query paths and align tenant-sensitive list-page state sync in `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php`, `app/Filament/Resources/FindingResource/Pages/ListFindings.php`, and `app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php`

**Checkpoint**: The canonical query pattern is reusable, guard-protected, and independently enforceable for future resource rollouts.

---

## Phase 6: Polish & Cross-Cutting Concerns

**Purpose**: Validate the completed rollout and keep the branch releasable.

- [X] T028 [P] Run the focused validation suite from `specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/quickstart.md`
- [X] T029 Run formatting with `vendor/bin/sail bin pint --dirty --format agent`

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)**: No dependencies, can start immediately.
- **Foundational (Phase 2)**: Depends on Setup completion and blocks all user stories.
- **User Story 1 (Phase 3)**: Starts after Foundational completion.
- **User Story 2 (Phase 4)**: Starts after Foundational completion and may reuse the shared helper from US1, but remains independently testable.
- **User Story 3 (Phase 5)**: Starts after Foundational completion and should land after at least one representative family uses the shared canon.
- **Polish (Phase 6)**: Runs after the desired story phases are complete.

### User Story Dependencies

- **US1**: No dependency on other stories. This is the MVP slice.
- **US2**: Depends only on the foundational helper layer, not on US1 completion for conceptual correctness.
- **US3**: Depends on the foundational helper layer and benefits from US1 and US2 landing first so the guard inventory reflects real adoption.

### Within Each User Story

- Tests MUST be written and fail before implementation.
- Shared helper adoption and tenant-sensitive persisted-state handling must happen before resource-specific cleanups.
- Policy and action-target hardening must happen before relation-manager or bulk-action cleanup is considered complete.

### Parallel Opportunities

- T002 and T003 can run in parallel.
- T005 and T006 can run in parallel.
- US1 test tasks T008, T009, and T010 can run in parallel.
- US1 implementation tasks T012, T013, and T014 can run in parallel after T004 through T006.
- US2 test tasks T016, T017, and T018 can run in parallel.
- US2 implementation tasks T020 and T021 can run in parallel after T019 begins anchoring the relation-manager pattern.
- US3 tasks T023 and T024 can run in parallel.

---

## Parallel Example: User Story 1

```bash
# Launch the first-slice list/detail regressions together:
Task: "Extend Entra group index, detail, and safe-search wrong-tenant coverage in tests/Feature/Filament/EntraGroupAdminScopeTest.php"
Task: "Add covered tenant-owned list, detail, and persisted tenant-state parity tests in tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php, tests/Feature/Filament/CanonicalAdminTenantFilterStateTest.php, and tests/Feature/Rbac/InventoryItemResourceAuthorizationTest.php"
Task: "Extend policy and policy-version search posture coverage in tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php and tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php"

# Then migrate independent resource groups in parallel:
Task: "Migrate app/Filament/Resources/BackupScheduleResource.php, app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php, and app/Filament/Resources/BackupSetResource.php to the shared tenant-owned query helper and canonical tenant-state sync"
Task: "Migrate app/Filament/Resources/RestoreRunResource.php and app/Filament/Resources/InventoryItemResource.php to the shared tenant-owned query helper while preserving tenant-safe persisted filter/search state"
Task: "Migrate app/Filament/Resources/FindingResource.php, app/Filament/Resources/FindingResource/Pages/ListFindings.php, app/Filament/Resources/PolicyResource.php, app/Filament/Resources/PolicyResource/Pages/ListPolicies.php, and app/Filament/Resources/PolicyVersionResource.php to the shared tenant-owned query, search-posture helper, and tenant-state-safe list behavior"
```

---

## Implementation Strategy

### MVP First (User Story 1 Only)

1. Complete Phase 1: Setup.
2. Complete Phase 2: Foundational.
3. Complete Phase 3: User Story 1.
4. Validate the covered list, detail, direct-link, and safe-search paths.

### Incremental Delivery

1. Ship the shared helper and first-slice list/detail adoption.
2. Add protected-action and relation-manager hardening.
3. Add the architectural guard, action-surface verification, and residual inventory so future tenant-owned surfaces inherit the pattern.

### Team Strategy

1. One developer lands the foundational helper layer.
2. A second developer can migrate covered resources for US1 while another hardens relation managers for US2.
3. A final pass lands the guard, action-surface verification, and residual inventory once real first-slice adoption is in place.

## Notes

- [P] tasks are limited to work on different files with no incomplete dependency overlap.
- US1 is the recommended MVP because it proves the canonical lookup rule on visible read paths first.
- US2 focuses on side-effect safety after the read-path canon exists.
- US3 converts the implementation into a reusable and enforceable repository pattern.