## Summary
- implement Spec 147 for workspace-first tenant selector and remembered tenant context enforcement
- harden canonical and tenant-bound route behavior so selected tenant mismatch stays informational
- fix drift finding subject fallback for workspace-safe RBAC identifiers and centralize finding subject resolution
## Testing
- vendor/bin/sail artisan test --compact tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingsListDefaultsTest.php
- vendor/bin/sail bin pint --dirty --format agent
## Notes
- branch pushed at de0679cd8b
- includes the spec artifacts under specs/147-tenant-selector-remembered-context-enforcement/
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #176
219 lines
16 KiB
Markdown
219 lines
16 KiB
Markdown
# 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 |