TenantAtlas/specs/135-canonical-tenant-context-resolution/data-model.md
ahmido cc93329672 feat: canonical tenant context resolution (#164)
## Summary
- introduce a canonical admin tenant filter-state helper and route all in-scope workspace-admin tenant resolution through `OperateHubShell::activeEntitledTenant()`
- align operations monitoring, operation-run deep links, Entra group admin list/view/search behavior, and shared context-bar rendering with the documented scope contract
- add the Spec 135 design artifacts, architecture note, focused guardrail coverage, and non-regression tests for filter persistence, direct-record access, and global search safety

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsKpiHeaderTenantContextTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php tests/Feature/Spec085/OperationsIndexHeaderTest.php tests/Feature/Spec085/RunDetailBackAffordanceTest.php tests/Feature/Filament/OperationRunListFiltersTest.php tests/Feature/Filament/EntraGroupAdminScopeTest.php tests/Feature/Filament/EntraGroupGlobalSearchScopeTest.php tests/Feature/DirectoryGroups/BrowseGroupsTest.php tests/Feature/Filament/EntraGroupEnterpriseDetailPageTest.php tests/Feature/Filament/PolicyVersionResolvedReferenceLinksTest.php tests/Feature/Filament/EntraGroupResolvedReferencePresentationTest.php tests/Feature/Guards/AdminTenantResolverGuardTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php tests/Feature/Alerts/AlertDeliveryDeepLinkFiltersTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TableStatePersistenceTest.php tests/Feature/Filament/TenantScopingTest.php tests/Feature/Filament/Alerts/AlertDeliveryViewerTest.php tests/Unit/Support/References/CapabilityAwareReferenceResolverTest.php`

## Notes
- Filament v5 remains on Livewire v4.0+ compliant surfaces only.
- No provider registration changes were needed; Laravel 12 provider registration remains in `bootstrap/providers.php`.
- Entra group global search remains enabled and is now scoped to the canonical admin tenant contract.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #164
2026-03-11 21:24:28 +00:00

5.1 KiB

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.