## 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
109 lines
6.8 KiB
Markdown
109 lines
6.8 KiB
Markdown
# Research: Spec 135 Canonical Tenant Context Resolution
|
||
|
||
## Decision 1: Preserve explicit split semantics between panels
|
||
|
||
**Decision**: Keep tenant-panel and workspace-admin tenant resolution as two explicit concepts. Tenant-panel flows continue to use panel-native tenant context. Workspace-admin flows continue to use `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant`.
|
||
|
||
**Rationale**:
|
||
- The spec explicitly forbids an unspecified universal resolver.
|
||
- Existing code already encodes the correct admin behavior in `OperateHubShell`.
|
||
- Tenant-panel screens legitimately use `Filament::getTenant()` and `Tenant::current()` because the tenant is part of panel-native routing semantics.
|
||
|
||
**Alternatives considered**:
|
||
- Build a single app-wide tenant resolver service: rejected because it would blur the admin-versus-tenant distinction the spec wants to preserve.
|
||
- Convert all existing tenant reads to admin semantics: rejected because tenant-panel pages are intentionally panel-native.
|
||
|
||
## Decision 2: Treat `OperateHubShell` as the canonical admin priority rule
|
||
|
||
**Decision**: Use `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant` as the single admin source of truth and document its priority order as:
|
||
1. entitled Filament tenant,
|
||
2. entitled remembered workspace tenant,
|
||
3. null when neither is valid.
|
||
|
||
**Rationale**:
|
||
- `OperateHubShell` already resolves the conflict the spec describes.
|
||
- It already checks entitlement before returning a tenant.
|
||
- It already drives visible admin UI labels such as “Filtered by tenant” and tenant return affordances.
|
||
|
||
**Alternatives considered**:
|
||
- Resolve directly from session in each page/resource: rejected because the bug class is inconsistent per-surface resolution.
|
||
- Prefer remembered tenant over Filament tenant: rejected because existing admin code and the spec’s conflict scenario require Filament tenant to win.
|
||
|
||
## Decision 3: Use alert delivery as the admin reference pattern
|
||
|
||
**Decision**: Use `AlertDeliveryResource` as the “already correct” admin reference pattern for query scoping, tenant filter defaults, and tenant filter option narrowing.
|
||
|
||
**Rationale**:
|
||
- The resource already scopes by workspace and tenant entitlement.
|
||
- It already uses `OperateHubShell::activeEntitledTenant()` for both query narrowing and filter default behavior.
|
||
- Its record URL and read-only list structure already fit the feature’s target class of admin monitoring flows.
|
||
|
||
**Alternatives considered**:
|
||
- Use Operations as the reference pattern: rejected because Operations is one of the surfaces that still has an inconsistency in its KPI widget.
|
||
- Use a tenant-panel resource as reference: rejected because the feature is specifically about admin-panel canonical behavior.
|
||
|
||
## Decision 4: Treat Operations page and KPI widget as one remediation unit
|
||
|
||
**Decision**: Plan the Operations page shell and `OperationsKpiHeader` widget as one remediation slice.
|
||
|
||
**Rationale**:
|
||
- `Monitoring\Operations` already uses `OperateHubShell` for header actions and table query narrowing.
|
||
- `OperationsKpiHeader` still reads `Filament::getTenant()` directly, which can disagree with the page header and table when only remembered tenant context exists.
|
||
- The spec requires visible tenant context, counts, widgets, filters, and links to resolve from the same canonical tenant.
|
||
|
||
**Alternatives considered**:
|
||
- Fix only the page query: rejected because the current inconsistency class is specifically page shell versus widget mismatch.
|
||
- Leave the widget workspace-wide while the table is tenant-scoped: rejected because that violates visible-scope parity.
|
||
|
||
## Decision 5: Harden OperationRun filters against stale persisted state
|
||
|
||
**Decision**: Revalidate tenant-sensitive persisted filters against the current canonical admin tenant on every relevant request.
|
||
|
||
**Rationale**:
|
||
- `OperationRunResource` persists filters in session.
|
||
- The tenant filter default already uses `OperateHubShell`, but some other filter option builders still reference raw `Filament::getTenant()`.
|
||
- The spec explicitly calls out persisted tenant-sensitive filter state as a regression risk when switching tenants.
|
||
|
||
**Alternatives considered**:
|
||
- Disable filter persistence entirely: rejected because persistence is a broader UX choice outside this feature.
|
||
- Trust the persisted filter if it still parses: rejected because syntactic validity is not the same as tenant-scope validity.
|
||
|
||
## Decision 6: Entra groups need record-resolution hardening, not just table scoping
|
||
|
||
**Decision**: Treat Entra groups as a list-plus-record-resolution problem. Scope must be aligned in `table()`, `getEloquentQuery()`, direct record/view access, and global search.
|
||
|
||
**Rationale**:
|
||
- `EntraGroupResource::table()` narrows using `Tenant::current()`.
|
||
- `EntraGroupResource::getEloquentQuery()` currently returns the broader base query.
|
||
- The resource has a view page, so direct record URLs can bypass table-only constraints.
|
||
- The spec explicitly names Entra group record-resolution and search as in-scope remediation.
|
||
|
||
**Alternatives considered**:
|
||
- Scope only the table query: rejected because the feature exists to fix list/detail/search parity.
|
||
- Remove the view page: rejected because the spec focuses on safe access, not reducing product capability.
|
||
|
||
## Decision 7: Use a panel-aware search rule or disable admin global search for Entra groups
|
||
|
||
**Decision**: Prefer a panel-aware scoped global-search query for Entra groups. If implementation cannot guarantee parity cheaply, disable global search for that resource in admin contexts.
|
||
|
||
**Rationale**:
|
||
- The constitution and spec both require tenant-safe, non-member-safe search behavior.
|
||
- The repo already has a tenant-scoped search concern based on `Filament::getTenant()`, but that is not sufficient for admin remembered-tenant semantics.
|
||
- The spec allows disabling global search where safe parity cannot be guaranteed.
|
||
|
||
**Alternatives considered**:
|
||
- Leave current search behavior implicit: rejected because the spec explicitly calls out search entry points.
|
||
- Build a broad search-only exception: rejected because it would reintroduce scope drift.
|
||
|
||
## Decision 8: The guardrail should be a focused architecture test, not a new lint tool
|
||
|
||
**Decision**: Implement the lightweight guardrail as a Pest architecture/regression test that scans selected admin Filament paths for raw `Filament::getTenant()` and `Tenant::current()` patterns, with a documented allowlist for tenant-panel-native files.
|
||
|
||
**Rationale**:
|
||
- The repo already relies on Pest for regression guards.
|
||
- This keeps the feature self-contained and avoids adding external tooling.
|
||
- The test can fail with actionable file paths and still permit explicit documented exceptions.
|
||
|
||
**Alternatives considered**:
|
||
- Add a custom PHPStan rule or external linter: rejected because it is heavier than the spec requires.
|
||
- Rely on code review discipline only: rejected because the feature explicitly asks for a maintainable guardrail. |