Some checks failed
Main Confidence / confidence (push) Failing after 43s
## Summary - consolidate workspace and tenant shell resolution behind a canonical resolved shell context - align workspace switching, tenant selection, and tenant clearing with the new recovery and fallback rules - add focused Pest coverage for shell resolution and update root dev orchestration so platform Vite starts correctly from repo-root commands ## Testing - cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Monitoring/HeaderContextBarTest.php - cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/GlobalContextShellContractTest.php - manual integrated-browser smoke for tenant-bound shell actions and context recovery flows - validated corepack pnpm build:platform, corepack pnpm dev:platform, corepack pnpm dev:website, and corepack pnpm dev ## Notes - Livewire v4 / Filament v5 remain unchanged and provider registration stays in bootstrap/providers.php - no new globally searchable resources or destructive Filament actions were introduced Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #246
15 KiB
15 KiB
Data Model: Global Context Shell Contract
Persistence Impact
- No new database tables, columns, or persisted shell artifacts are introduced.
- Existing session-backed fields remain the only durable support state used by the contract:
current_workspace_idworkspace_intended_urlworkspace_last_tenant_ids
- Existing user-level fields such as
users.last_workspace_idandusers.last_tenant_idoruser_tenant_preferences.last_used_atremain support inputs only and do not become active shell truth.
Context Source Inventory
| Source | Context facet | Source role | Owning seam | Validation / notes |
|---|---|---|---|---|
| Explicit workspace switch request | Workspace | leading | SwitchWorkspaceController + WorkspaceContext |
Must point to an accessible, selectable workspace before it can replace current workspace truth. |
| Current session workspace | Workspace | leading | WorkspaceContext |
Remains the default current-workspace truth when no stronger explicit request exists and membership is still valid. |
| Remembered last workspace | Workspace | supporting | WorkspaceContext |
Restore-only candidate used when no valid current session workspace exists and the entry flow allows restore. |
| Route tenant parameter | Tenant | leading | Route + OperateHubShell |
Strongest tenant source on tenant-bound routes. Must belong to the resolved workspace and remain entitled. |
| Explicit tenant selection request | Tenant | leading | SelectTenantController + OperateHubShell |
Can activate tenant scope only inside an already resolved workspace. |
| Filament panel tenant state | Tenant | supporting | ResolvesPanelTenantContext |
May support resolution only after workspace compatibility and entitlement checks succeed. |
| Remembered tenant for resolved workspace | Tenant | supporting | WorkspaceContext |
Restore-only candidate on tenantless-capable workspace pages. Never valid on its own for tenant-bound routes. |
| Query hint | Workspace or Tenant | supporting only when contract explicitly allows it | OperateHubShell |
Must never become effective truth unless the contract explicitly names the query-backed flow. |
| View-local shell inference | Workspace or Tenant | never-leading | Shared shell partials and page-local views | Rendering surfaces may display resolved truth only. They cannot evaluate precedence or recovery. |
Runtime Entities
| Entity | Kind | Fields | Validation / Notes |
|---|---|---|---|
| RequestedWorkspaceContext | Derived runtime input | workspaceIdentifier, source, intendedUrl, pageCategory |
Represents a workspace request from route, explicit switch flow, or initial restore path before validation. |
| RequestedTenantContext | Derived runtime input | tenantIdentifier, source, pageCategory, requiresExplicitTenant |
Represents route tenant, explicit tenant select, query hint, Filament tenant, or remembered tenant before validation. |
| RememberedContextCandidate | Derived support state | workspaceId, tenantId, source, eligible |
Represents stored last-used tenant for the active workspace or last-used workspace during initial resolution. Never leading by itself. |
| ResolvedShellContext | Canonical request-scoped truth | workspace, tenant, pageCategory, workspaceSource, tenantSource, state, recoveryDirective, displayMode |
The only context object shell UI and server-side consumers should trust for the current request. |
| RecoveryDirective | Derived outcome | action, destination, reason, preserveIntendedUrl |
Encodes whether the request renders tenantless state, redirects, or aborts. |
| InvalidContext | Derived runtime outcome | kind, source, reason, requestedWorkspaceIdentifier, requestedTenantIdentifier |
Captures why a requested or remembered context could not become active truth. |
Supporting Enums / Value Domains
ContextSource
routeexplicit_switchexplicit_selectsession_workspacefilament_tenantrememberedquery_hintnone
ShellState
tenant_scopedtenantless_workspacemissing_workspaceinvalid_workspacemissing_tenantinvalid_tenantinaccessible_tenantincompatible_tenant
RecoveryAction
nonerender_tenantless_workspaceredirect_choose_workspaceredirect_operations_indexredirect_evidence_overviewredirect_workspace_homeredirect_workspace_managed_tenantsredirect_workspace_record_fallbackabort_not_found
PageCategory
workspace_scopedworkspace_chooser_exceptiontenant_boundtenant_scoped_evidencecanonical_workspace_record_viewer
Entity Details
RequestedWorkspaceContext
| Field | Type | Required | Notes |
|---|---|---|---|
workspaceIdentifier |
string or int | yes | May come from explicit workspace switch flow or a safe intended URL restore path. |
source |
ContextSource |
yes | explicit_switch, session_workspace, or remembered are the main inputs today. |
intendedUrl |
string or null | no | Safe /admin... path captured via WorkspaceIntendedUrl. |
pageCategory |
PageCategory |
yes | Needed to determine if a tenantless fallback is valid after workspace resolution. |
Validation rules:
- Workspace must exist.
- Workspace must not be archived or otherwise unselectable.
- User must be a member of the workspace.
- Cross-plane routes remain out of scope; only
web-guarded admin and tenant routes participate.
RequestedTenantContext
| Field | Type | Required | Notes |
|---|---|---|---|
tenantIdentifier |
string or int | yes | May come from route param, explicit tenant selection, query hint, Filament panel state, or remembered session state. |
source |
ContextSource |
yes | Route and explicit selection are strongest; remembered is weakest. |
pageCategory |
PageCategory |
yes | Determines whether tenant fallback is valid, optional, or forbidden. |
requiresExplicitTenant |
bool | yes | true for tenant-bound pages; false for workspace-scoped pages that can remain tenantless. |
Validation rules:
- Tenant must exist.
- Tenant must belong to the resolved workspace.
- User must be entitled to the tenant.
- Tenant must satisfy the relevant operability question for the current lane.
- Tenant must be compatible with the current route type.
RememberedContextCandidate
| Field | Type | Required | Notes |
|---|---|---|---|
workspaceId |
int | yes | Key for the remembered tenant map. |
tenantId |
int or null | no | Candidate tenant for restore. |
source |
ContextSource |
yes | Today this is remembered, with supporting user-level last-used values. |
eligible |
bool | yes | false once access, operability, or workspace match fails. |
invalidReason |
string or null | no | Captures why the remembered candidate became ineligible during validation or cleanup. |
Validation rules:
- Candidate tenant must still exist.
- Candidate tenant must still belong to the active workspace.
- Candidate tenant must still be accessible to the user.
- Candidate tenant must still pass
RememberedContextValidityfor the current lane. - Ineligible remembered context is cleared immediately and cannot survive as visible shell truth.
ResolvedShellContext
| Field | Type | Required | Notes |
|---|---|---|---|
workspace |
Workspace or null | yes | Null only when recovery requires chooser or not-found handling. |
tenant |
Tenant or null | yes | Null is valid only in tenantless workspace state or before a hard recovery redirect. |
pageCategory |
PageCategory |
yes | Controls whether tenantless state is valid. |
workspaceSource |
ContextSource |
yes | Records which source actually won for workspace resolution. |
tenantSource |
ContextSource |
yes | Records which source actually won for tenant resolution, or none. |
state |
ShellState |
yes | The user-visible shell state. |
recoveryDirective |
RecoveryDirective |
yes | Defines what to render or where to redirect if the request cannot continue as requested. |
displayMode |
string | yes | tenant_scoped, tenantless, or recovery. |
Invariants:
- A resolved tenant cannot exist without a resolved workspace.
- Remembered context cannot become active if a stronger valid source exists.
- The shell display must derive only from
ResolvedShellContext. - Tenant-bound pages cannot render a remembered-tenant fallback as though it were an explicit route tenant.
InvalidContext
| Field | Type | Required | Notes |
|---|---|---|---|
kind |
string | yes | workspace or tenant |
source |
ContextSource |
yes | Identifies whether route, panel, remembered, or query input failed. |
reason |
string | yes | missing, inaccessible, incompatible, not_operable, not_member, archived, or mismatched_workspace |
requestedWorkspaceIdentifier |
string or int or null | no | Included for diagnostics and testing only. |
requestedTenantIdentifier |
string or int or null | no | Included for diagnostics and testing only. |
Relationships
ResolvedShellContextis composed from zero or oneRequestedWorkspaceContext, zero or oneRequestedTenantContext, zero or oneRememberedContextCandidate, and zero or oneInvalidContextplusRecoveryDirective.RecoveryDirectiveis downstream ofResolvedShellContext.stateandPageCategory.RememberedContextCandidate.workspaceIdis always keyed to the resolved workspace candidate; it is not global across workspaces.
Resolution Rules
Workspace Resolution
- Try a valid explicit workspace request when the current entry flow provides one.
- Otherwise use the current session workspace if it remains valid.
- Otherwise allow a valid last-used workspace restore only during initial resolution.
- Otherwise emit
missing_workspaceorinvalid_workspacewith a chooser-oriented recovery directive.
Tenant Resolution
- On tenant-bound pages, validate route tenant first and fail if it is missing, inaccessible, or incompatible.
- On workspace-scoped pages, accept a valid route tenant or explicit tenant-selection request first.
- Next accept a validated query-backed tenant hint only on routes where the contract explicitly allows query-backed shell resolution.
- Next accept validated
Filament::getTenant()only if it matches the resolved workspace and remains entitled. - Next accept remembered tenant only when the page category permits tenantless fallback and no stronger valid tenant source exists.
- Otherwise resolve to tenantless workspace state or to an explicit recovery directive depending on page category.
Recovery Matrix
| Page category | Invalid workspace | Invalid explicit tenant | Missing tenant after clear | Invalid remembered tenant |
|---|---|---|---|---|
workspace_scoped |
redirect to chooser, or to admin.operations.index when cleanup is referrer-free or sentinel-driven |
render tenantless workspace or clear request | render tenantless workspace on the current route, or use admin.operations.index when no safe prior route exists |
clear remembered tenant and remain tenantless |
workspace_chooser_exception |
remain on /admin/choose-workspace |
not applicable | remain on /admin/choose-workspace until the user selects a workspace |
not applicable |
tenant_bound |
404 or redirect to chooser if no workspace can be re-established | 404 when route tenant is invalid or inaccessible | redirect to admin.workspace.managed-tenants.index for the current workspace, else admin.home |
ignored as active truth; route still governs |
tenant_scoped_evidence |
redirect to chooser if workspace truth cannot be re-established | redirect to admin.evidence.overview when tenant detail context is no longer valid |
redirect to admin.evidence.overview |
clear remembered tenant and return to admin.evidence.overview |
canonical_workspace_record_viewer |
404 if the record itself is no longer entitled | 404 if tenant-scoped record access fails | remain on admin.operations.view when it is still workspace-safe, otherwise use the documented workspace fallback |
clear remembered tenant and keep record-only rules |
Documented Recovery Destinations
| RecoveryAction | Route target | When it applies |
|---|---|---|
redirect_choose_workspace |
/admin/choose-workspace |
Missing or unrecoverable workspace truth at shell entry or restore time |
redirect_operations_index |
admin.operations.index |
External or missing referrer, clear-flow sentinel path, and generic workspace-safe fallback for tenantless monitoring entry |
redirect_evidence_overview |
admin.evidence.overview |
Tenant-scoped evidence paths that must return to a workspace-safe evidence landing |
redirect_workspace_managed_tenants |
admin.workspace.managed-tenants.index |
Tenant-bound cleanup or workspace switch flows that must return to tenant selection inside the resolved workspace |
redirect_workspace_home |
admin.home |
Tenant-bound cleanup when no current workspace truth remains available |
redirect_workspace_record_fallback |
admin.operations.view in the current-release scope |
Canonical workspace record viewers that stay in workspace scope without reviving tenant truth |
State Transitions
| Trigger | From | To | Notes |
|---|---|---|---|
| Workspace switch | tenant_scoped |
tenant_scoped or tenantless_workspace |
Existing tenant survives only after compatibility re-check in target workspace. |
| Tenant select | tenantless_workspace |
tenant_scoped |
Explicit user action; requires entitlement and operability validation. |
| Tenant clear on workspace page | tenant_scoped |
tenantless_workspace |
Valid only on workspace-scoped pages. |
| Tenant clear on tenant-bound page | tenant_scoped |
tenantless_workspace plus redirect |
Redirect destination depends on page category and route family. |
| Remembered tenant invalidation | tenant_scoped or restore candidate |
tenantless_workspace |
Candidate is cleared and cannot stay visible. |
| Workspace invalidation | any | missing_workspace or invalid_workspace |
Recovery goes to chooser or not-found depending on entry path. |
Display Semantics
| Resolved state | Workspace label | Tenant label | Action affordances |
|---|---|---|---|
tenant_scoped |
Active workspace name | Active tenant name | Switch workspace, Select tenant, Clear tenant context |
tenantless_workspace |
Active workspace name | No tenant selected |
Switch workspace, Select tenant |
missing_workspace / invalid_workspace |
Choose workspace or recovery label |
hidden or disabled | Choose workspace, optional safe return |
invalid_tenant / inaccessible_tenant / incompatible_tenant |
Active workspace name if valid | no stale tenant name shown | Recovery action only; no stale tenant truth |