TenantAtlas/specs/147-tenant-selector-remembered-context-enforcement/tasks.md
ahmido 73a879d061 feat: implement spec 147 tenant context enforcement (#176)
## 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
2026-03-16 22:52:58 +00:00

16 KiB

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.

  • T001 [P] Create the remembered-context unit test target in tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php
  • T002 [P] Create the choose-tenant and workspace-fallback feature test target in tests/Feature/Workspaces/ChooseTenantPageTest.php
  • 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
  • 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

  • T005 Implement validated remembered-tenant lookup, invalidation, and explicit clear helpers in app/Support/Workspaces/WorkspaceContext.php
  • T006 [P] Refactor shell active-tenant resolution states for workspace-level, tenant-bound, and canonical routes in app/Support/OperateHub/OperateHubShell.php
  • T007 [P] Align active-lane selector eligibility helpers across app/Services/Tenants/TenantOperabilityService.php and app/Models/User.php
  • T008 Remove hidden tenant-required gating from workspace-safe middleware and routes in app/Support/Middleware/EnsureFilamentTenantSelected.php and routes/web.php
  • 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
  • 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 ⚠️

  • T011 [P] [US1] Add choose-tenant eligibility and empty-state assertions in tests/Feature/Workspaces/ChooseTenantPageTest.php
  • T012 [P] [US1] Add header selector scope and non-active exclusion assertions in tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php
  • T013 [P] [US1] Add managed-tenant discoverability assertions for onboarding and archived records in tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php

Implementation for User Story 1

  • T014 [US1] Refactor active-lane tenant retrieval and selection handling in app/Filament/Pages/ChooseTenant.php
  • T015 [US1] Align POST tenant selection enforcement with the shared selector rule in app/Http/Controllers/SelectTenantController.php
  • 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
  • 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
  • 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 ⚠️

  • T019 [P] [US2] Add remembered-context invalidation scenarios for workspace mismatch, missing tenant, and selector-ineligible lifecycle in tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php
  • 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

  • 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
  • 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
  • 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 ⚠️

  • T024 [P] [US3] Add positive and negative tenant-bound route-authority assertions in tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php and tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php
  • 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

  • T026 [US3] Refine route-authoritative tenant resolution for admin-panel pages in app/Support/OperateHub/OperateHubShell.php and app/Filament/Concerns/ResolvesPanelTenantContext.php
  • 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
  • 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.

  • 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
  • T030 Run formatting for touched files with vendor/bin/sail bin pint --dirty --format agent
  • 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

# 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

# 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

# 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