# Data Model: Spec 135 Canonical Tenant Context Resolution ## Overview This feature introduces no new database tables or persisted domain objects. Its data model is request-time and behavioral: it formalizes how existing workspace, tenant, filter, and record-access state combine into one canonical tenant-context outcome. ## Entity: Tenant Panel Context **Purpose**: Represents the panel-native tenant context for tenant-panel flows. **Source fields**: - `panel_id` - `tenant_route_parameter` - `filament_tenant` - `tenant_membership_status` **Relationships**: - belongs to one current panel - resolves to one entitled tenant or null **Validation rules**: - Only valid on tenant-panel-native surfaces. - Must not be substituted with admin remembered-tenant fallback semantics. - Non-members must receive deny-as-not-found behavior. ## Entity: Admin Operational Tenant Context **Purpose**: Represents the canonical active tenant for workspace-admin flows. **Source fields**: - `workspace_id` - `filament_tenant_id` nullable - `remembered_tenant_id` nullable - `resolved_tenant_id` nullable - `resolution_source` enum: `filament`, `remembered`, `none` - `is_entitled` boolean **Relationships**: - belongs to one current workspace - may resolve to one tenant in that workspace - is used by headers, widgets, filters, queries, record links, and search **Validation rules**: - `resolved_tenant_id` must be null when no entitled tenant exists. - If both `filament_tenant_id` and `remembered_tenant_id` exist and disagree, `filament_tenant_id` wins. - Any resolved tenant must belong to the active workspace and pass tenant entitlement. ## Entity: Tenant Context Conflict **Purpose**: Captures the request state where more than one tenant source exists for the same admin request. **Fields**: - `filament_tenant_id` - `remembered_tenant_id` - `workspace_id` - `winning_source` - `losing_source` **Validation rules**: - Conflict only exists when both source values are present and different. - Conflict resolution must be deterministic for the entire request. - Losing source must not leak into filters, widgets, counts, or navigation labels. ## Entity: Tenant-Sensitive Filter State **Purpose**: Represents persisted or defaulted filter state whose valid values depend on the canonical tenant context. **Fields**: - `filter_name` - `raw_value` - `canonical_tenant_id` nullable - `workspace_id` - `is_valid_for_context` boolean - `resolution_action` enum: `apply`, `reset`, `ignore`, `replace` **Relationships**: - belongs to one request flow - may constrain one resource list or widget drill-down **Validation rules**: - Filter values must be revalidated whenever canonical tenant context changes. - Filter option lists must never be broader than the underlying list/query scope. - Invalid persisted tenant-sensitive values must not silently remain active after a tenant switch. ## Entity: Scoped Record Access Path **Purpose**: Represents any route or UI affordance that can reveal a tenant-sensitive record. **Fields**: - `surface_type` enum: `list`, `detail`, `direct_url`, `deep_link`, `global_search` - `resource_name` - `workspace_id` - `canonical_tenant_id` nullable - `record_tenant_id` nullable - `authorization_outcome` enum: `ok`, `not_found`, `forbidden` **Relationships**: - points to one resource class and optional record - uses one context resolver appropriate to its panel **Validation rules**: - Detail/direct/search access must never be broader than the corresponding list scope. - Out-of-scope or missing-context access must produce deterministic bounded behavior. - Membership failures remain `not_found`; capability failures after membership is established remain `forbidden`. ## Entity: Admin Surface Guardrail Exception **Purpose**: Documents files that are allowed to use panel-native tenant reads without violating the admin guardrail. **Fields**: - `file_path` - `reason` - `panel_semantics` enum: `tenant_native`, `approved_panel_native_surface` - `review_owner` **Validation rules**: - Every exception must be explicit and stable. - Admin-only files are not valid exceptions. - Guardrail output must clearly distinguish violations from approved exceptions. ## State Transitions ### Admin request context state 1. `no_context` - No valid Filament tenant and no valid remembered tenant. - Outcome: safe empty/not-found bounded behavior. 2. `remembered_only` - No valid Filament tenant, valid remembered tenant. - Outcome: remembered tenant becomes canonical admin tenant. 3. `filament_only` - Valid Filament tenant, no remembered tenant. - Outcome: Filament tenant becomes canonical admin tenant. 4. `conflict` - Valid Filament tenant and valid remembered tenant differ. - Outcome: Filament tenant wins for the full request. ## Invariants - Tenant-panel-native files use panel-native tenant semantics. - Workspace-admin files use the canonical admin resolver when resolving an operational tenant. - Visible tenant context and effective query scope must always match within the same request. - Persisted tenant-sensitive filter state is never trusted without revalidation. - Direct record URLs and search results cannot bypass the same tenant boundary as the list surface.