TenantAtlas/specs/199-global-context-shell-contract/data-model.md
Ahmed Darrazi b515796839
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 53s
feat: finalize global shell context contract
2026-04-18 15:59:02 +02:00

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_id
    • workspace_intended_url
    • workspace_last_tenant_ids
  • Existing user-level fields such as users.last_workspace_id and users.last_tenant_id or user_tenant_preferences.last_used_at remain 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

  • route
  • explicit_switch
  • explicit_select
  • session_workspace
  • filament_tenant
  • remembered
  • query_hint
  • none

ShellState

  • tenant_scoped
  • tenantless_workspace
  • missing_workspace
  • invalid_workspace
  • missing_tenant
  • invalid_tenant
  • inaccessible_tenant
  • incompatible_tenant

RecoveryAction

  • none
  • render_tenantless_workspace
  • redirect_choose_workspace
  • redirect_operations_index
  • redirect_evidence_overview
  • redirect_workspace_home
  • redirect_workspace_managed_tenants
  • redirect_workspace_record_fallback
  • abort_not_found

PageCategory

  • workspace_scoped
  • workspace_chooser_exception
  • tenant_bound
  • tenant_scoped_evidence
  • canonical_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 RememberedContextValidity for 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

  • ResolvedShellContext is composed from zero or one RequestedWorkspaceContext, zero or one RequestedTenantContext, zero or one RememberedContextCandidate, and zero or one InvalidContext plus RecoveryDirective.
  • RecoveryDirective is downstream of ResolvedShellContext.state and PageCategory.
  • RememberedContextCandidate.workspaceId is always keyed to the resolved workspace candidate; it is not global across workspaces.

Resolution Rules

Workspace Resolution

  1. Try a valid explicit workspace request when the current entry flow provides one.
  2. Otherwise use the current session workspace if it remains valid.
  3. Otherwise allow a valid last-used workspace restore only during initial resolution.
  4. Otherwise emit missing_workspace or invalid_workspace with a chooser-oriented recovery directive.

Tenant Resolution

  1. On tenant-bound pages, validate route tenant first and fail if it is missing, inaccessible, or incompatible.
  2. On workspace-scoped pages, accept a valid route tenant or explicit tenant-selection request first.
  3. Next accept a validated query-backed tenant hint only on routes where the contract explicitly allows query-backed shell resolution.
  4. Next accept validated Filament::getTenant() only if it matches the resolved workspace and remains entitled.
  5. Next accept remembered tenant only when the page category permits tenantless fallback and no stronger valid tenant source exists.
  6. 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