## 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
5.6 KiB
Research: Tenant Selector and Remembered Context Enforcement
Decision 1: Keep active-lane selector eligibility anchored in TenantOperabilityService
Decision: Use App\Services\Tenants\TenantOperabilityService as the single authoritative rule for standard selector membership and require the header selector, choose-tenant page, and selection controllers to converge on that service.
Rationale: The service already encapsulates lifecycle-aware operability and exposes canSelectAsContext() and filterSelectable(). Spec 147 needs consistency, not a second selector rule. Reusing this service directly keeps Spec 143 lifecycle semantics intact and avoids per-surface drift.
Alternatives considered:
- Build a new selector-specific eligibility service: rejected because it would duplicate
canSelectAsContext()semantics and create a new place for lifecycle drift. - Let each surface query
status = activeindependently: rejected because it would bypass existing operability semantics and repeat the exact fragmentation this spec is intended to remove.
Decision 2: Centralize remembered tenant revalidation in WorkspaceContext
Decision: Make App\Support\Workspaces\WorkspaceContext the authoritative place that validates, returns, clears, and invalidates remembered tenant context for active-lane use.
Rationale: WorkspaceContext already owns the session map keyed by workspace and already clears stale remembered IDs when workspace or lifecycle checks fail. It is the natural home for enforcing the workspace-scoped preference contract required by Spec 147.
Alternatives considered:
- Revalidate remembered tenant context separately in controllers, Livewire pages, and Blade partials: rejected because it preserves scattered raw-session truth assumptions.
- Move remembered-context validation into
OperateHubShell: rejected because shell resolution should consume validated context, not become the storage owner.
Decision 3: Keep the custom workspace-first shell instead of reverting to Filament's default tenant switcher
Decision: Continue using the existing custom context bar and custom choose-tenant page, but align both to one active-lane rule set.
Rationale: The product architecture is explicitly workspace first. Filament's tenant switcher is tenant-menu-centric, while this app already uses a workspace-scoped shell, explicit workspace switching, and canonical workspace-level routes. Docs confirm Filament tenant menu customization exists, but the custom shell is already the correct product pattern here.
Alternatives considered:
- Replace the custom selector with Filament's built-in tenant switcher: rejected because it would pull the product back toward a tenant-first mental model and would not solve the workspace-scoped remembered-context rules already present in the app.
- Keep the custom shell but allow it to diverge from the choose-tenant page: rejected because Spec 147 explicitly requires shared semantics between both selector surfaces.
Decision 4: Treat route authority and shell context as separate layers
Decision: Preserve route-authority semantics for tenant-bound and canonical routes and use shell context only for convenience, not for legitimacy.
Rationale: OperationRunPolicy already authorizes canonical runs by workspace membership, tenant entitlement, and capability checks. TenantlessOperationRunViewer already renders non-blocking context banners. Spec 147 should extend this pattern consistently through shell resolution instead of reintroducing tenant-context-coupled validity at the page level.
Alternatives considered:
- Use selected tenant equality as a fast validity shortcut for tenant-bound or canonical pages: rejected because it conflicts with Specs 143 and 144 and causes false invalidity.
- Auto-switch the selected tenant to match the route record: rejected because it makes page rendering mutate user context implicitly and hides the distinction between route subject and current active lane.
Decision 5: “No tenant selected” is a first-class valid shell state
Decision: Treat no-selected-tenant as the correct fallback state for workspace-level pages after invalidation, workspace switch, or explicit clear.
Rationale: Workspace-level surfaces such as /admin and /admin/operations are legitimate without an active tenant. Spec 147 requires graceful degradation to a valid workspace-wide view instead of hidden failure when remembered context becomes stale.
Alternatives considered:
- Force tenant reselection before allowing workspace-level pages: rejected because it would contradict the workspace-first architecture.
- Keep stale remembered tenant until the user manually clears it: rejected because it would preserve hidden authority and inconsistent page behavior.
Decision 6: No schema change is required for this feature
Decision: Implement Spec 147 as behavior consolidation over existing session-backed workspace tenant preferences and existing user preference persistence paths.
Rationale: The app already stores current workspace in session and last tenant IDs in a workspace-keyed session map, with supplemental persistence through users.last_tenant_id or user_tenant_preferences. The problem is validation and consumption semantics, not missing storage.
Alternatives considered:
- Introduce a new tenant-context table or richer persisted context model: rejected because the spec explicitly says remembered tenant context is a convenience preference, not a durable parallel ownership model.
- Persist no tenant preference at all: rejected because remembered context remains a supported convenience feature within the workspace-scoped shell.