# Tasks: Tenant Selector and Remembered Context Enforcement **Input**: Design documents from `/specs/147-tenant-selector-remembered-context-enforcement/` **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/ **Tests**: For runtime behavior changes in this repo, tests are REQUIRED (Pest). **Operations**: This feature does not introduce long-running, remote, queued, or scheduled work. No `OperationRun` creation or lifecycle changes are required. **RBAC**: This feature hardens admin-plane context behavior without changing the capability model. Tasks MUST preserve `404` for non-members or non-entitled actors and `403` for in-scope capability denial, and MUST include positive and negative authorization regression coverage for tenant-bound and canonical routes. **UI Naming**: Copy must preserve the distinction between `workspace`, `selected tenant`, `no tenant selected`, `viewed tenant`, and `run tenant`, and must not imply that selected tenant state determines route legitimacy. **Filament UI Action Surfaces**: This feature does not add new actions. Existing header, row, bulk, and empty-state action inventories must remain consistent while selector and shell semantics are hardened. **Filament UI UX-001 (Layout & IA)**: Existing layouts remain intact. Tasks below only adjust selector semantics, workspace-safe fallback behavior, and informational mismatch messaging within current screens. **Badges**: Existing centralized lifecycle presentation from Spec 146 must continue to be used anywhere lifecycle or selector availability is shown. **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story. ## Format: `[ID] [P?] [Story] Description` - **[P]**: Can run in parallel (different files, no dependencies) - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3) - Include exact file paths in descriptions ## Phase 1: Setup (Shared Infrastructure) **Purpose**: Prepare the focused regression targets and implementation surfaces for context-enforcement work. - [X] T001 [P] Create the remembered-context unit test target in `tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php` - [X] T002 [P] Create the choose-tenant and workspace-fallback feature test target in `tests/Feature/Workspaces/ChooseTenantPageTest.php` - [X] T003 [P] Review and extend the existing shell and canonical route regression targets in `tests/Feature/OpsUx/OperateHubShellTest.php` and `tests/Feature/Operations/TenantlessOperationRunViewerTest.php` - [X] T004 [P] Create the workspace-context global-search safety regression target in `tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php` --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Centralize selector eligibility, remembered-context validation, and workspace-safe no-tenant fallback before any story-specific surface work begins. **⚠️ CRITICAL**: No user story work can begin until this phase is complete - [X] T005 Implement validated remembered-tenant lookup, invalidation, and explicit clear helpers in `app/Support/Workspaces/WorkspaceContext.php` - [X] T006 [P] Refactor shell active-tenant resolution states for workspace-level, tenant-bound, and canonical routes in `app/Support/OperateHub/OperateHubShell.php` - [X] T007 [P] Align active-lane selector eligibility helpers across `app/Services/Tenants/TenantOperabilityService.php` and `app/Models/User.php` - [X] T008 Remove hidden tenant-required gating from workspace-safe middleware and routes in `app/Support/Middleware/EnsureFilamentTenantSelected.php` and `routes/web.php` - [X] T009 Audit and harden workspace-context global-search scoping so remembered tenant state cannot leak tenant-owned results in `app/Filament/Resources/TenantResource.php`, `app/Filament/Resources/OperationRunResource.php`, and `tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php` - [X] T010 Add foundational coverage for shared remembered-context and shell-resolution behavior in `tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php` and `tests/Feature/OpsUx/OperateHubShellTest.php` **Checkpoint**: Foundation ready. Selector and shell rules are centralized, and user stories can proceed against one shared context model. --- ## Phase 3: User Story 1 - Trust The Active Tenant Selector (Priority: P1) 🎯 MVP **Goal**: Make the standard header selector and choose-tenant page represent only the normal active operating lane. **Independent Test**: In one workspace with active, draft, onboarding, and archived tenants, verify that only active tenants appear as selectable choices in both the header selector and choose-tenant page while non-active tenants remain discoverable elsewhere. ### Tests for User Story 1 ⚠️ - [X] T011 [P] [US1] Add choose-tenant eligibility and empty-state assertions in `tests/Feature/Workspaces/ChooseTenantPageTest.php` - [X] T012 [P] [US1] Add header selector scope and non-active exclusion assertions in `tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php` - [X] T013 [P] [US1] Add managed-tenant discoverability assertions for onboarding and archived records in `tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php` ### Implementation for User Story 1 - [X] T014 [US1] Refactor active-lane tenant retrieval and selection handling in `app/Filament/Pages/ChooseTenant.php` - [X] T015 [US1] Align POST tenant selection enforcement with the shared selector rule in `app/Http/Controllers/SelectTenantController.php` - [X] T016 [US1] Update choose-tenant copy and card behavior to reflect the active-lane-only selector meaning in `resources/views/filament/pages/choose-tenant.blade.php` - [X] T017 [US1] Update the header context-bar tenant list and selector affordances to use the same active-lane semantics in `resources/views/filament/partials/context-bar.blade.php` - [X] T018 [US1] Preserve intentional non-active tenant discoverability in workspace admin surfaces in `app/Filament/Pages/Workspaces/ManagedTenantsLanding.php` and `app/Filament/Resources/TenantResource.php` **Checkpoint**: User Story 1 is complete when both selector surfaces expose the same active-only operating lane and no contradictory inactive choices remain. --- ## Phase 4: User Story 2 - Recover Safely From Stale Remembered Context (Priority: P2) **Goal**: Revalidate remembered tenant context on use and degrade cleanly to a legitimate no-tenant workspace state when it becomes stale or invalid. **Independent Test**: Persist remembered context for an active tenant, then invalidate it by lifecycle change, entitlement loss, or workspace switch and verify the shell falls back to no selected tenant while `/admin`, `/admin/operations`, and `/admin/tenants` remain usable. ### Tests for User Story 2 ⚠️ - [X] T019 [P] [US2] Add remembered-context invalidation scenarios for workspace mismatch, missing tenant, and selector-ineligible lifecycle in `tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php` - [X] T020 [P] [US2] Add workspace switch, explicit clear, and no-selected-tenant fallback assertions in `tests/Feature/Workspaces/ChooseTenantPageTest.php`, `tests/Feature/Workspaces/ChooseWorkspaceRedirectsToChooseTenantTest.php`, and `tests/Feature/Rbac/TenantResourceAuthorizationTest.php` ### Implementation for User Story 2 - [X] T021 [US2] Update workspace-switch and explicit-clear flows to preserve workspace-safe no-tenant fallback in `app/Http/Controllers/SwitchWorkspaceController.php` and `app/Http/Controllers/ClearTenantContextController.php` - [X] T022 [US2] Remove workspace-page assumptions that an active tenant is required in `app/Filament/Pages/Monitoring/Operations.php`, `app/Filament/Resources/TenantResource.php`, and `app/Support/Middleware/EnsureFilamentTenantSelected.php` - [X] T023 [US2] Update shell and chooser copy to present “no tenant selected” as a legitimate workspace state in `resources/views/filament/partials/context-bar.blade.php` and `resources/views/filament/pages/choose-tenant.blade.php` **Checkpoint**: User Story 2 is complete when stale remembered context clears deterministically and workspace-level pages continue working without selected tenant state. --- ## Phase 5: User Story 3 - Keep Route Legitimacy Separate From Header Context (Priority: P3) **Goal**: Preserve route-authoritative behavior for tenant-bound pages and canonical run viewers even when selected tenant context differs, is empty, or has just been cleared. **Independent Test**: Open authorized tenant-bound and canonical run routes with mismatched, stale, or empty selected tenant state and verify the pages still resolve while non-member or non-entitled access keeps `404` semantics and capability denial keeps `403` semantics. ### Tests for User Story 3 ⚠️ - [X] T024 [P] [US3] Add positive and negative tenant-bound route-authority assertions in `tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php` and `tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php` - [X] T025 [P] [US3] Add canonical run mismatch, no-selected-tenant, entitlement, and capability assertions in `tests/Feature/Operations/TenantlessOperationRunViewerTest.php` ### Implementation for User Story 3 - [X] T026 [US3] Refine route-authoritative tenant resolution for admin-panel pages in `app/Support/OperateHub/OperateHubShell.php` and `app/Filament/Concerns/ResolvesPanelTenantContext.php` - [X] T027 [US3] Preserve mismatch-as-information behavior for canonical run viewing in `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` and `resources/views/filament/pages/operations/tenantless-operation-run-viewer.blade.php` - [X] T028 [US3] Audit workspace and canonical route integration so selected tenant never acts as a legitimacy shortcut in `routes/web.php` and `app/Policies/OperationRunPolicy.php` **Checkpoint**: User Story 3 is complete when route identity and policy fully outrank selected tenant state on tenant-bound and canonical pages. --- ## Phase 6: Polish & Cross-Cutting Concerns **Purpose**: Final regression validation, formatting, and manual verification across all stories. - [X] T029 [P] Run focused Pest suites and render/query-safety validation covering `tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php`, `tests/Feature/Workspaces/ChooseTenantPageTest.php`, `tests/Feature/Workspaces/ChooseWorkspaceRedirectsToChooseTenantTest.php`, `tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php`, `tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php`, `tests/Feature/Rbac/TenantResourceAuthorizationTest.php`, `tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php`, `tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php`, `tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php`, `tests/Feature/OpsUx/OperateHubShellTest.php`, and `tests/Feature/Operations/TenantlessOperationRunViewerTest.php` - [X] T030 Run formatting for touched files with `vendor/bin/sail bin pint --dirty --format agent` - [X] T031 [P] Validate the manual verification checklist in `specs/147-tenant-selector-remembered-context-enforcement/quickstart.md` and review `/admin/tenants` plus `/admin/operations` against `docs/product/standards/list-surface-review-checklist.md` --- ## Dependencies & Execution Order ### Phase Dependencies - **Setup (Phase 1)**: No dependencies, can start immediately - **Foundational (Phase 2)**: Depends on Setup completion and blocks all story work - **User Story 1 (Phase 3)**: Depends on Foundational completion - **User Story 2 (Phase 4)**: Depends on Foundational completion and benefits from US1 surface alignment - **User Story 3 (Phase 5)**: Depends on Foundational completion and is safest after US1 and US2 because it hardens route behavior on the same shell/context surfaces - **Polish (Phase 6)**: Depends on all desired user stories being complete ### User Story Dependencies - **User Story 1 (P1)**: No dependency on other stories after Foundational; this is the MVP slice - **User Story 2 (P2)**: Depends on the shared context-validation foundation and uses the selector semantics stabilized in US1 - **User Story 3 (P3)**: Depends on the shared context-validation foundation and benefits from the no-tenant and selector semantics stabilized in US1 and US2 ### Within Each User Story - Tests for the story should be written first and fail before implementation - Shared support-layer changes should land before UI surface refinements - Route and middleware behavior should be stabilized before final mismatch-copy adjustments - Story-specific validation should pass before moving to the next priority story ### Parallel Opportunities - T001, T002, T003, and T004 can run in parallel because they prepare separate test targets - T006 and T007 can run in parallel after T005 defines the shared remembered-context contract - T011, T012, and T013 can run in parallel because they cover different selector and discoverability surfaces - T019 and T020 can run in parallel because they cover different fallback paths - T024 and T025 can run in parallel because they cover different route categories - T029 and T031 can run in parallel after implementation is complete --- ## Parallel Example: User Story 1 ```bash # Launch the P1 selector regression tasks together: Task: "Add choose-tenant eligibility and empty-state assertions in tests/Feature/Workspaces/ChooseTenantPageTest.php" Task: "Add header selector scope and non-active exclusion assertions in tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php" Task: "Add managed-tenant discoverability assertions in tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php" ``` ## Parallel Example: User Story 2 ```bash # After the foundation is in place, stale-context validation and workspace-switch coverage can proceed together: Task: "Add remembered-context invalidation scenarios in tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php" Task: "Add workspace switch and no-selected-tenant fallback assertions in tests/Feature/Workspaces/ChooseTenantPageTest.php, tests/Feature/Workspaces/ChooseWorkspaceRedirectsToChooseTenantTest.php, and tests/Feature/Rbac/TenantResourceAuthorizationTest.php" ``` ## Parallel Example: User Story 3 ```bash # Route-authority coverage can split by tenant-bound and canonical routes: Task: "Add tenant-bound route-authority assertions in tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php and tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php" Task: "Add canonical run mismatch and authorization assertions in tests/Feature/Operations/TenantlessOperationRunViewerTest.php" ``` --- ## 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. **Stop and validate** the selector semantics independently on the header selector and choose-tenant page 5. Demo or merge the MVP slice if acceptable ### Incremental Delivery 1. Complete Setup + Foundational to establish one shared context model 2. Deliver User Story 1 to make the active selector trustworthy 3. Deliver User Story 2 to make stale remembered context recover safely 4. Deliver User Story 3 to harden tenant-bound and canonical route authority 5. Finish with Polish for regression validation and formatting ### Team Strategy 1. One engineer owns the support-layer foundation in `WorkspaceContext`, `OperateHubShell`, and middleware/routes 2. A second engineer prepares selector-surface and fallback regression coverage in parallel once the shared context contract is clear 3. Route-authority hardening for canonical and tenant-bound pages can proceed as a separate stream after the foundation is merged --- ## Notes - [P] tasks touch separate files and can be worked in parallel without blocking each other - Every user story remains independently testable after the foundational phase - This feature does not add schema changes, Graph calls, or new operations workflows - Keep route legitimacy tied to route subject and policy, never to raw remembered tenant session state