## 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
15 KiB
Implementation Plan: Spec 135 Canonical Tenant Context Resolution
Branch: 135-canonical-tenant-context-resolution | Date: 2026-03-11 | Spec: specs/135-canonical-tenant-context-resolution/spec.md
Spec (absolute): /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/135-canonical-tenant-context-resolution/spec.md
Input: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/135-canonical-tenant-context-resolution/spec.md
Summary
Establish and enforce one explicit tenant-context rule across tenant-sensitive admin flows: tenant-panel screens continue to use panel-native tenant context, while workspace-admin screens use OperateHubShell::activeEntitledTenant(Request $request): ?Tenant as the only public canonical operational tenant resolver, with Filament tenant taking precedence over remembered tenant state when both exist.
Implementation focuses on three concrete remediation targets already visible in the codebase:
- the workspace-admin Operations monitoring shell and KPI widget,
- the OperationRun resource filters and detail navigation behavior used by Monitoring,
- the Entra group list, direct-record, and global-search access path.
The work also adds one lightweight regression guard so new admin-panel code does not reintroduce raw tenant-panel-native tenant reads, while preserving tenant-panel-native behavior in approved tenant-context files. This iteration remediates the known high-risk surfaces named in the spec and records a residual inventory for remaining admin call sites that are already compliant or intentionally deferred.
Technical Context
Language/Version: PHP 8.4 on Laravel 12
Primary Dependencies: Filament v5, Livewire v4, Pest v4, Laravel Sail
Storage: PostgreSQL application database
Testing: Pest feature tests via vendor/bin/sail artisan test --compact
Target Platform: Web application with Filament admin and tenant panels
Project Type: Laravel monolith with Filament resources, pages, widgets, policies, and support-layer helpers
Performance Goals: Maintain DB-only render behavior for monitoring/admin pages, avoid broader-than-visible query scopes, and keep context resolution deterministic per request without extra remote calls
Constraints:
- No dependency changes.
- No new Graph calls, queued jobs, or write workflows.
- No generic “universal tenancy resolver” that erases the admin versus tenant-panel distinction.
- Direct URLs, deep links, and global search must fail safe when context is missing or out of scope.
- Existing Filament action surfaces stay structurally unchanged unless a safe-state affordance is required. Scale/Scope: Targeted architecture hardening across existing support helpers, 3 in-scope admin surfaces, 1 reference pattern, 1 lightweight guardrail, and a focused regression matrix covering remembered-only, Filament-only, conflict, and no-context states
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Inventory-first / snapshots: PASS — this feature changes read-time scope resolution only; inventory, backups, and snapshots remain unchanged.
- Read/write separation: PASS — no new mutating flow is introduced; this is read, filter, navigation, and authorization hardening.
- Graph contract path: PASS — no Graph calls or contract-registry changes are involved.
- Deterministic capabilities: PASS — no capability registry changes; existing policies and capability helpers remain authoritative.
- RBAC-UX planes: PASS —
/adminadmin flows and/admin/t/{tenant}/...tenant-context flows stay explicitly separate; non-members remain 404, in-scope capability denials remain 403. - Workspace isolation: PASS — workspace-admin routes stay tenant-safe even without tenant in the URL; missing workspace context continues to collapse to safe empty/not-found behavior.
- Tenant isolation: PASS — all in-scope queries, filter options, direct-record lookups, and search results are planned to remain bounded by tenant entitlement.
- Global search safety: PASS — tenant-sensitive resources will either adopt admin-safe scoped search behavior or disable global search where parity cannot be guaranteed.
- Run observability: PASS — no new operational run type or lifecycle behavior is introduced; Monitoring remains DB-only at render time.
- Ops-UX lifecycle / summary counts / notifications: PASS — unchanged.
- BADGE-001: PASS — badge semantics stay centralized; only the tenant context feeding those surfaces is normalized.
- UI-NAMING-001: PASS — plan preserves operator-facing language such as “Filtered by tenant” and explicit safe-state wording.
- Filament Action Surface Contract: PASS WITH EXEMPTION — affected resources/pages remain read-only or existing-action surfaces; the feature changes scoping and safe outcomes, not action inventory.
- Filament UX-001: PASS WITH EXEMPTION — no layout redesign is required beyond explicit safe no-context messaging.
- Livewire v4.0+ compliance: PASS — Filament v5 + Livewire v4 remain unchanged and are the required stack for all affected screens.
- Provider registration location: PASS — no panel-provider changes are needed; Laravel 12 provider registration remains in
bootstrap/providers.php.
Phase 0 — Research Summary
Research findings are recorded in specs/135-canonical-tenant-context-resolution/research.md.
Key decisions:
- Keep the two-context model explicit instead of abstracting it into a single cross-panel resolver.
- Treat
OperateHubShell::activeEntitledTenant()as the admin-panel source of truth because it already implements the desired priority order: entitled Filament tenant first, remembered workspace tenant second, otherwise null. - Use the existing alert-delivery resource as the reference pattern for admin-scope query, filter default, and filter-option alignment.
- Harden Entra group list/view/search behavior because the current table path scopes with
Tenant::current()whilegetEloquentQuery()remains broader, creating direct-record and potential search drift. - Implement a lightweight architectural guard as a focused test that flags
Filament::getTenant()/Tenant::current()reads inside approved admin directories, with an allowlist for tenant-panel-native files.
Phase 1 — Design & Contracts
Data Model
Design details are recorded in specs/135-canonical-tenant-context-resolution/data-model.md.
Key design points:
- No schema changes are required.
- The feature introduces a request-time domain model around context state, not persisted business data.
- Canonical admin context is derived from workspace membership, current Filament tenant, remembered tenant session state, and entitlement checks.
- Filter state becomes explicitly revalidated view state, not trusted persisted state.
Contracts
Internal behavior contracts are recorded in specs/135-canonical-tenant-context-resolution/contracts/context-resolution-matrix.yaml.
Contract scope:
/admin/operations- tenantless operation-run detail flows under
/admin/operations/{run} - admin-scoped OperationRun filters and related option lists
- Entra group list/view/global-search access paths
- alert-delivery admin resource as the non-regression reference pattern
Quickstart
Implementation and verification steps are recorded in specs/135-canonical-tenant-context-resolution/quickstart.md.
Post-Design Constitution Re-check
- Inventory-first / snapshots: PASS — unchanged.
- Read/write separation: PASS — only read-path behavior and regression guards are added.
- Graph contract path: PASS — still no Graph activity.
- RBAC-UX: PASS — design explicitly keeps 404 for out-of-scope access, 403 for capability denial after scope establishment, and server-side authorization for detail/search/list paths.
- Workspace isolation: PASS — admin routes without workspace or tenant context stay bounded and deterministic.
- Tenant isolation: PASS — detail URLs and search results are planned to use the same tenant-bound query contract as lists.
- Global search safety: PASS — design requires parity or explicit disablement for tenant-sensitive admin resources.
- Run observability / Ops-UX lifecycle: PASS — no changes.
- BADGE-001 / UI-NAMING-001: PASS — existing centralized badge and operator language rules remain intact.
- Filament Action Surface Contract: PASS WITH EXEMPTION — no new destructive actions, no action-surface expansion.
- Livewire v4.0+ compliance: PASS — all affected Filament screens remain on the supported Filament v5 / Livewire v4 stack.
- Provider registration location: PASS — no provider changes;
bootstrap/providers.phpremains the correct Laravel 12 location.
Project Structure
Documentation (this feature)
specs/135-canonical-tenant-context-resolution/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── context-resolution-matrix.yaml
└── tasks.md
Source Code (repository root)
app/
├── Filament/
│ ├── Concerns/
│ ├── Pages/
│ │ ├── Monitoring/
│ │ └── Operations/
│ ├── Resources/
│ │ ├── EntraGroupResource/
│ │ └── AlertDeliveryResource/
│ └── Widgets/
│ └── Operations/
├── Models/
├── Policies/
├── Support/
│ ├── OperateHub/
│ ├── Workspaces/
│ └── Navigation/
└── Http/Controllers/
resources/views/
└── filament/
tests/
├── Feature/
│ ├── Monitoring/
│ ├── Operations/
│ ├── Filament/
│ └── Spec085/
└── Unit/
Structure Decision: Keep implementation inside the existing Laravel / Filament monolith. Reuse the support-layer admin resolver in app/Support/OperateHub/, harden the affected Filament pages/resources/widgets in place, and add focused Pest regression tests rather than introducing new infrastructure.
Phase 2 — Implementation Planning
Implementation should be delivered in the following slices so /speckit.tasks can break work cleanly.
- Document and centralize the canonical rule
- Refine and document
OperateHubShell::activeEntitledTenant(Request $request): ?Tenantas the only public admin-panel tenant resolver. - Any helper introduced for implementation convenience must remain internal-only and delegate to
OperateHubShell::activeEntitledTenant(Request)rather than becoming a second public resolver. - Ensure callers can distinguish admin-panel canonical context from tenant-panel-native context without introducing a universal cross-panel API.
- Preserve remembered-only support and explicit null/no-context outcomes.
- Remediate Monitoring shell consistency
- Update
OperationsKpiHeaderto use the same canonical admin tenant asMonitoring\Operations. - Verify header label, KPI cards, table query, and “Show all tenants” behavior stay aligned in remembered-only, Filament-only, conflict, and no-context states.
- Make the operations-index no-context outcome explicit: show the workspace-scoped
All tenantsstate only for entitled tenants in the active workspace, clear tenant-default filters, and hide tenant-only KPI behavior instead of inferring a tenant.
- Harden OperationRun resource filter parity
- Ensure tenant-sensitive filter options and defaults use the canonical admin resolver, not raw
Filament::getTenant(). - Revalidate persisted filter state whenever canonical tenant context changes between requests.
- Keep direct record/detail access bounded by the same workspace + tenant entitlement rules as the list, with explicit
404for non-members or out-of-scope tenant access and403only after scope membership is established where capability gates apply. - Make the run-detail no-context outcome explicit: allow rendering only for active-workspace records that still pass tenant entitlement checks when tenant-bound; otherwise respond as not found.
- Harden Entra group list, detail, and search behavior
- Replace raw
Tenant::current()assumptions for admin-sensitive access paths with explicit panel-aware resolution. - Align
table()scoping,getEloquentQuery(), record URL resolution, and view page behavior so direct record URLs cannot exceed list scope. - Apply admin-safe global search scoping or disable global search for this resource if parity is not maintainable.
- Make the admin Entra-group no-context outcome explicit: admin list/detail requests respond as not found, and admin global search returns no tenant-owned Entra-group results when no canonical admin tenant exists.
- Preserve tenant-panel-native behavior in explicitly approved tenant-context files.
- Retain alert-delivery as the reference pattern
- Confirm the alert-delivery resource continues to model the intended admin query and filter alignment.
- Refactor only if needed to extract a reusable pattern without regressing current behavior.
- Add the lightweight guardrail
- Implement a focused regression/architecture test that scans approved admin Filament directories for new raw
Filament::getTenant()/Tenant::current()patterns. - Maintain an explicit allowlist for tenant-panel-native files and equivalent approved panel-native surfaces.
- Fail with actionable file references so future regressions are cheap to fix.
- Record residual scope explicitly
- Document remaining admin tenant-resolution call sites outside the remediation set.
- Mark each residual call site as already compliant, intentionally excepted, or deferred beyond this iteration.
- Expand the regression matrix
- Extend existing Spec 085 monitoring coverage for remembered-only, Filament-only, conflict, and no-context states.
- Add focused tests for OperationRun filter default/option parity, stale persisted filter handling, and positive/negative authorization semantics.
- Add Entra group list/view/search regression tests for direct URLs, out-of-scope access, no-context safety, and non-member-safe search behavior.
- Reuse existing factories, workspace-session helpers, and authorization helpers; avoid bespoke test harnesses.
Testing Strategy
- Extend existing monitoring tests in
tests/Feature/Spec085/rather than replacing them. - Add focused feature coverage for:
- workspace-admin operations page and KPI consistency,
- tenantless operation-run detail access parity,
- OperationRun filter default and option scoping,
- Entra group list/view/global-search tenant safety,
- explicit
404vs403authorization semantics for in-scope admin access paths, - guardrail enforcement for new admin-only raw tenant reads.
- Run only the affected Pest files with Sail during implementation.
- Finish with
vendor/bin/sail bin pint --dirty --format agentand the minimal relatedvendor/bin/sail artisan test --compact ...commands.
Complexity Tracking
No constitution violations or complexity exemptions are required for this plan.