## 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
25 KiB
Feature Specification: Tenant Selector and Remembered Context Enforcement
Feature Branch: 147-tenant-selector-remembered-context-enforcement
Created: 2026-03-16
Status: Draft
Input: User description: "Spec 147 — Tenant Selector and Remembered Context Enforcement"
Spec Scope Fields (mandatory)
- Scope: workspace + tenant selector + remembered tenant context + canonical-view-adjacent shell behavior
- Primary Routes:
/admin/admin/choose-workspace/admin/choose-tenant/admin/tenants/admin/tenants/{tenant}/admin/operations/admin/operations/{run}- Any in-scope route that reads, restores, or reacts to remembered tenant context
- Data Ownership:
- Workspaces remain the primary context and ownership boundary.
- Tenants remain workspace-owned records.
- Remembered tenant context remains a workspace-scoped operator preference rather than a durable authorization or ownership object.
- Canonical workspace-level records such as operation runs remain authoritative on their own routes even when they reference a tenant.
- This feature does not introduce a new tenant data model and does not change workspace or tenant ownership boundaries.
- RBAC:
- Authorization planes involved: tenant/admin
/adminworkspace-scoped routes, tenant-bound routes, and canonical workspace record viewers that may reference a tenant. - Non-members or users lacking tenant entitlement for a route-resolved record remain deny-as-not-found.
- In-scope members lacking a required capability for an otherwise visible page or action remain forbidden.
- Remembered tenant context must not substitute for membership, entitlement, capability checks, or route identity.
- Authorization planes involved: tenant/admin
For canonical-view specs, the spec MUST define:
- Default filter behavior when tenant-context is active: Workspace-level indexes such as
/adminand/admin/operationsmay prefilter to the currently selected tenant as a convenience, but clearing, invalidating, or mismatching remembered tenant context must fall back to a valid no-tenant workspace view rather than changing route legitimacy. - Explicit entitlement checks preventing cross-tenant leakage: Canonical and tenant-bound pages must resolve access from workspace relationship, route record identity, and referenced tenant entitlement where applicable. Remembered tenant context may inform copy or filters but must never reveal unauthorized tenant details or become a shortcut for record visibility.
User Scenarios & Testing (mandatory)
User Story 1 - Trust The Active Tenant Selector (Priority: P1)
As a workspace operator, I need the standard tenant selector and choose-tenant flow to show only valid active-lane tenants, so that selecting a tenant always feels like entering a safe normal operating context rather than a hidden trap.
Why this priority: The selector is top-level shell behavior. If it offers ineligible tenants, the rest of the workspace feels structurally unreliable.
Independent Test: Can be fully tested by preparing active, draft, onboarding, and archived tenants in one workspace and confirming that only active tenants are selectable through the header selector and choose-tenant page while non-active tenants remain discoverable elsewhere.
Acceptance Scenarios:
- Given a workspace contains active, draft, onboarding, and archived tenants, When the operator opens the standard tenant selector, Then only active tenants are presented as selectable active operating context.
- Given a workspace contains onboarding or archived tenants, When the operator opens the choose-tenant page, Then those tenants are not offered as active-lane choices and the page uses the same selection meaning as the header selector.
- Given a tenant is excluded from the standard selector because of lifecycle, When the operator visits the correct management, onboarding, audit, or canonical-view surface, Then the tenant remains discoverable there rather than disappearing from the product.
User Story 2 - Recover Safely From Stale Remembered Context (Priority: P2)
As a workspace operator, I need remembered tenant context to be revalidated and cleared gracefully when it becomes stale or ineligible, so that workspace-level pages stay usable and the shell does not route me into confusing breakage.
Why this priority: Stale remembered context is the main way hidden shell state turns into false invalidity. Safe fallback is necessary for a trustworthy workspace-first model.
Independent Test: Can be fully tested by storing remembered context for an active tenant, changing that tenant to an ineligible lifecycle or switching workspaces, and confirming the shell falls back to a legitimate no-tenant state without breaking workspace-level routes.
Acceptance Scenarios:
- Given remembered tenant context points to a tenant that becomes archived, When the operator next opens an in-scope workspace page, Then the system clears or ignores the stale remembered tenant for active-lane use and presents a valid fallback workspace view.
- Given remembered tenant context belongs to a different workspace, When the operator switches workspaces, Then the previous workspace's remembered tenant does not bleed into the new workspace as active context.
- Given remembered tenant context refers to a tenant the operator is no longer entitled to view, When an in-scope page reads active context, Then the UI recovers to a no-tenant or cleared-filter state instead of treating the stale preference as authoritative.
- Given no tenant is currently selected, When the operator opens a workspace-level managed-tenant surface such as
/admin/tenants, Then the page remains usable as a workspace-scoped administrative surface rather than failing because active tenant context is absent.
User Story 3 - Keep Route Legitimacy Separate From Header Context (Priority: P3)
As a workspace operator, I need tenant-bound pages and canonical record viewers to remain valid based on the route record and policy even when the selected tenant differs or is missing, so that direct links and investigations behave predictably.
Why this priority: Route legitimacy is a core architectural promise from earlier specs. This feature needs to harden that promise at the shell level so the selector stops acting like a hidden validity gate.
Independent Test: Can be fully tested by opening tenant detail pages and canonical operation-run pages while remembered tenant context is mismatched, invalid, or empty and verifying that authorized routes still resolve while mismatch is treated as informational only.
Acceptance Scenarios:
- Given an operator opens an authorized tenant detail route for a tenant that is not currently selected, When the page loads, Then the tenant-bound page remains valid because the route tenant is authoritative.
- Given an operator opens an authorized canonical operation viewer while selected tenant context points at another tenant or no tenant is selected, When the page loads, Then the canonical page renders successfully and any mismatch is presented as non-blocking context.
- Given a route resolves to a record the operator is not entitled to inspect, When the operator opens the route, Then normal authorization semantics apply and remembered tenant context does not broaden access or alter the failure class.
Edge Cases
- A remembered tenant was valid when stored but later becomes
draft,onboarding, orarchived; the shell must stop using it as active context without making workspace routes feel broken. - A remembered tenant no longer exists, no longer belongs to the current workspace, or is outside the operator's entitled tenant set; the stale value must be ignored or cleared deterministically.
- An operator arrives on a canonical record page through a deep link before choosing any tenant; no-tenant-selected must remain a legitimate shell state.
- An authorized operator opens a tenant-bound page for an onboarding or archived tenant; the route remains valid even though that tenant is excluded from the standard active selector.
- Search or direct navigation can find a tenant that does not appear in the standard selector; that difference must remain consistent with lifecycle and context semantics rather than feeling accidental.
Requirements (mandatory)
Constitution alignment (required): This feature introduces no new Microsoft Graph calls, new write workflows, or new queued or scheduled work. It hardens selector semantics, remembered-context validity, and route-legitimacy rules so later implementation can centralize them without creating a parallel tenant model.
Constitution alignment (OPS-UX): This feature does not create a new OperationRun type and does not change service ownership of run status or outcome. It reaffirms that canonical operation viewers remain workspace-level pages whose legitimacy comes from the record, workspace, and authorization rules rather than remembered tenant context.
Constitution alignment (RBAC-UX): This feature changes authorization-adjacent shell behavior inside the admin plane. Non-members or non-entitled actors remain deny-as-not-found. Members lacking an otherwise required capability remain forbidden. Server-side enforcement must continue to rely on workspace membership, tenant entitlement, route record identity, and canonical capability policy checks rather than raw remembered session state. No new destructive action is introduced by this spec.
This feature MUST also preserve RBAC-UX global-search safety: when no active tenant is selected, remembered tenant context must not cause tenant-owned global-search results or hints to appear in workspace context.
Constitution alignment (OPS-EX-AUTH-001): Not applicable. This feature does not alter authentication handshakes or synchronous auth exceptions.
Constitution alignment (BADGE-001): This feature reuses the tenant lifecycle presentation semantics defined by Spec 146 wherever lifecycle is shown in selectors, context surfaces, mismatch messaging, or tenant references. No ad hoc lifecycle mappings may be introduced for selector exclusion or stale-context messaging.
Constitution alignment (UI-NAMING-001): The target objects are workspace context, active tenant context, and route-resolved tenant references. Operator-facing copy must preserve the distinction between workspace, selected tenant, active tenant context, and viewed tenant or run tenant. Messaging must avoid implying that a missing or mismatched selected tenant invalidates an otherwise legitimate route.
Constitution alignment (Filament Action Surfaces): This feature changes the meaning and trust model of existing Filament and shell-adjacent surfaces rather than adding new mutation actions. The Action Surface Contract remains satisfied because no new destructive behavior or hidden action inventory is introduced. The UI Action Matrix below records the affected surfaces and their semantic constraints.
Constitution alignment (UX-001 — Layout & Information Architecture): This feature primarily changes information architecture semantics and recovery behavior rather than screen layout. Existing in-scope pages must preserve current layout rules while presenting no-tenant-selected as a legitimate workspace state and any selected-tenant mismatch as non-blocking informational context.
Constitution alignment (UI-STD-001 list surfaces): Because this feature modifies workspace-level list surfaces such as /admin/tenants and /admin/operations, implementation and verification MUST reference docs/product/standards/list-surface-review-checklist.md so selector and no-context semantics do not regress established table, filter, empty-state, and inspection-affordance standards.
Functional Requirements
- FR-147-001: The system MUST define the standard tenant selector as the selector for normal active operating tenant context within the current workspace.
- FR-147-002: The standard tenant selector and choose-tenant page MUST include only tenants eligible for normal active operating context.
- FR-147-003: For the lifecycle model already established by Spec 143,
activetenants MUST be eligible for the standard active selector, whiledraft,onboarding, andarchivedtenants MUST NOT be eligible. - FR-147-004: The header selector, choose-tenant page, and any equivalent standard active-lane tenant picker within this scope MUST share the same selector eligibility semantics.
- FR-147-005: The system MUST preserve deliberate discoverability for non-active tenants through the correct non-selector surfaces, including managed-tenant, onboarding, administrative, audit, archive, restore, and canonical record routes where authorized.
- FR-147-006: Remembered tenant context MUST be treated as a workspace-scoped preference for active-lane convenience rather than as an authorization primitive, ownership model, or route-legitimacy requirement.
- FR-147-007: Whenever remembered tenant context is read for active-lane use, the system MUST revalidate that the tenant still belongs to the current workspace, remains visible to the actor where required, remains eligible for normal active operating context, and still exists.
- FR-147-008: If remembered tenant context fails revalidation, the system MUST clear or ignore it for active-context behavior and MUST recover to a deterministic fallback such as no selected tenant or a cleared tenant filter.
- FR-147-009: Workspace changes MUST trigger re-evaluation of remembered tenant context so that tenant preferences do not bleed across workspaces.
- FR-147-010: Workspace-level pages within this scope MUST remain valid and usable without any selected tenant context unless a page is explicitly defined elsewhere as tenant-required.
- FR-147-011: On workspace-level pages, selected tenant context MAY act as a default filter, scope suggestion, or shortcut affordance, but it MUST NOT act as a route-validity precondition.
- FR-147-012: Tenant-bound pages MUST derive legitimacy from the route tenant record, workspace relationship, entitlement, and page semantics rather than from header tenant equality.
- FR-147-013: Canonical workspace-level record viewers, including
/admin/operations/{run}, MUST remain valid when authorized regardless of remembered tenant mismatch, absence, or stale remembered tenant state. - FR-147-014: The system MUST NOT select a tenant into the standard active lane through one path and then immediately invalidate that choice downstream because later logic applies stricter active-lane rules.
- FR-147-015: No selected tenant MUST be treated as a legitimate shell state for workspace-first operation rather than as an error condition by default.
- FR-147-016: Where selected tenant context and a viewed tenant or referenced run tenant differ, the UI MAY explain the mismatch, but the mismatch MUST remain informational and MUST NOT masquerade as missing data or route invalidity.
- FR-147-017: In-scope code MUST move toward one authoritative selector eligibility rule and one authoritative remembered-context validation rule rather than duplicating ad hoc checks in multiple pages or helpers.
- FR-147-018: In-scope code MUST NOT treat raw stored remembered tenant state as durable truth without revalidation.
- FR-147-019: Exclusion from the standard active selector MUST NOT be interpreted as non-existence of the tenant record, and UI copy or navigation within scope MUST reinforce that distinction.
- FR-147-020: Regression coverage for this feature MUST include active tenant visible in selector, onboarding tenant excluded, archived tenant excluded, stale remembered tenant invalidation, workspace switch re-evaluation, no-selected-tenant workspace usage, tenant-bound route with mismatched selected tenant, and canonical operation viewer with mismatched selected tenant.
- FR-147-021: In workspace context with no selected tenant, global search MUST remain workspace-safe and MUST NOT use remembered tenant context to surface tenant-owned results or hints.
UI Action Matrix (mandatory when Filament is changed)
If this feature adds/modifies any Filament Resource / RelationManager / Page, fill out the matrix below.
For each surface, list the exact action labels, whether they are destructive (confirmation? typed confirmation?), RBAC gating (capability + enforcement helper), and whether the mutation writes an audit log.
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Workspace shell and header context bar | /admin shared shell |
Existing workspace and tenant context affordances only | Not applicable | None introduced by this spec | None | Existing workspace chooser CTA unchanged | Not applicable | Not applicable | No direct mutation | This feature changes context semantics and empty-state legitimacy, not mutation inventory. |
| Choose-tenant page | /admin/choose-tenant |
None beyond existing navigation | Existing tenant inspection or selection affordance | Select tenant for eligible active-lane tenants only |
None | Existing fallback CTA to managed-tenant or workspace surfaces may remain | Not applicable | Not applicable | No direct mutation | The page must not become a backdoor for selecting non-active tenants into active context. |
| Managed tenants index and tenant detail | /admin/tenants, /admin/tenants/{tenant} |
Existing header actions remain | Existing row click and route record inspection remain | Existing lifecycle-safe row actions unchanged by this spec | Existing grouped bulk actions unchanged | Existing empty-state CTA unchanged | Existing detail header actions unchanged | Existing save and cancel behavior unchanged | Existing audit rules unchanged | Route legitimacy is route-record-driven even when selected tenant differs or is empty. |
| Operations index and canonical run viewer | /admin/operations, /admin/operations/{run} |
Existing navigation and filter affordances remain | Existing run inspection links remain | View run remains canonical inspect action |
Existing grouped bulk actions unchanged | Existing empty-state CTA unchanged | Existing run-view actions unchanged | Not applicable | Existing audit rules unchanged | Selected tenant is a convenience filter on the index only and must not become a hidden validity gate for the canonical viewer. |
Key Entities (include if feature involves data)
- Workspace Context: The primary operating boundary that owns tenants and canonical workspace records.
- Standard Active Tenant Selector: The shared product affordance used to choose the current active operating tenant context within a workspace.
- Remembered Tenant Context: A workspace-scoped preference storing the operator's last active-lane tenant choice for convenience only.
- Tenant Route Subject: A route-resolved tenant record whose legitimacy comes from route identity and policy rather than current header selection.
- Canonical Workspace Record: A workspace-owned record such as an operation run that may reference a tenant but remains authoritative on its own route.
Success Criteria (mandatory)
Measurable Outcomes
- SC-147-001: In focused regression coverage, 100% of tenants shown in the standard selector are eligible for normal active operating context and 0 ineligible tenants are selectable there.
- SC-147-002: In focused regression coverage, 100% of stale remembered-tenant scenarios recover to a deterministic fallback state without producing false not-found behavior or unusable workspace-level pages.
- SC-147-003: In focused regression coverage, 100% of authorized tenant-bound and canonical record routes remain accessible when selected tenant context is mismatched, empty, or cleared.
- SC-147-004: In focused regression coverage, 100% of workspace switches clear or re-evaluate remembered tenant context so that no previous workspace tenant is reused as active context in another workspace.
- SC-147-005: In focused UX validation across covered pages, operators can complete workspace-level work with no selected tenant and receive explicit, non-blocking mismatch messaging instead of ambiguous failure.
- SC-147-006: In covered paths for onboarding and archived tenants, exclusion from the standard selector does not remove those tenants from their intended management, onboarding, audit, or canonical-view discovery surfaces.
- SC-147-007: In focused validation, workspace-context global search with no selected tenant does not reveal tenant-owned results or hints through remembered tenant state.
Summary
The product already follows a workspace-first architecture, but tenant selector behavior and remembered tenant context still act too much like hidden authority. This makes non-active tenants leak into active-lane selection, lets stale remembered state survive too long, and causes operators to misread header state as a determinant of whether pages or records are legitimate.
This feature defines the standard tenant selector as the normal active operating lane only, and defines remembered tenant context as a revalidated workspace-scoped preference for that lane rather than a source of truth. It hardens the shell so selector trust, route legitimacy, and non-active tenant discoverability stay aligned.
Goals
- Define the standard tenant selector as the normal active-lane selector for workspace-scoped operations.
- Ensure only eligible active-lane tenants are selectable through the standard selector surfaces.
- Define remembered tenant context as revalidated convenience state rather than route or authorization truth.
- Protect workspace-level pages, tenant-bound pages, and canonical record viewers from false invalidity caused by stale or mismatched selected tenant state.
- Preserve intentional discoverability for onboarding and archived tenants outside the standard selector.
- Reduce scattered selector-membership and remembered-context validity logic in favor of shared semantics.
Non-Goals
- This feature does not redesign the visual style of the header bar beyond what is necessary to communicate safe context semantics.
- This feature does not redefine canonical operation viewing beyond reinforcing the rules already established by Spec 144.
- This feature does not introduce a new tenant ownership model, a new tenant lifecycle model, or a parallel second selector system.
- This feature does not require non-active tenants to appear in the standard selector.
- This feature does not remove onboarding or archived tenants from the product.
- This feature does not implement the full future central operability policy, though it is intended to align with that direction.
Assumptions
- Spec 143 remains the source of truth for workspace-first context semantics, canonical lifecycle values, and page-type distinctions.
- Spec 144 remains the source of truth for canonical operation viewer legitimacy under tenant-context mismatch.
- Spec 146 remains the source of truth for how lifecycle is presented wherever lifecycle state is shown.
- The current product already has legitimate non-selector surfaces where onboarding, archived, administrative, and canonical tenant references can remain discoverable.
- This feature is implemented as consolidation and enforcement of existing architectural intent rather than as a new context model.
Risks
- Removing non-active tenants from the selector without preserving clear discovery paths could make those records feel lost.
- Leaving selector eligibility or remembered-context validation fragmented would allow contradictions to reappear under different pages or helper flows.
- Treating no-selected-tenant as an implicit error would make graceful invalidation feel like breakage rather than safe recovery.
- Over-coupling this feature to a future operability abstraction could delay necessary correctness work on current shell behavior.
Follow-Up Dependencies
- Spec 148 — Central Tenant Operability Policy
- Future shell polish and information-architecture specs for mismatch messaging, no-context framing, and operator guidance
- Future governance, customer-view, and workspace-summary specs that must not reintroduce selector-driven route ambiguity
Final Direction
The product must stay workspace first, tenant context second. The standard selector is only for the normal active operating lane, remembered tenant is a revalidated preference rather than truth, and route identity plus policy remain the source of legitimacy for tenant-bound and canonical pages. Onboarding and archived tenants remain real records, but they stay discoverable through the right surfaces instead of leaking into the active-lane selector.