9.2 KiB
Research: Spec 136 Admin Panel Canonical Tenant Resolution Full Rollout
Decision 1: Use admin registration plus reachability to freeze the rollout manifest
Decision: Treat admin panel provider registration and direct admin page registration as the primary source of truth for admin-visible surfaces, but keep shared resources in scope when admin paths, deep links, embedded widgets, or dual-panel discovery can still execute their code in admin context.
Rationale:
- The codebase already exposes some resources directly in
AdminPanelProvider, while others are tenant-scoped but still referenced from admin pages or shared navigation helpers. - The spec cares about admin tenant drift, not only sidebar registration.
- Using reachability avoids missing shared resources that can still execute admin-path logic without appearing in admin navigation.
Alternatives considered:
- Limit the rollout strictly to resources registered in
AdminPanelProvider: rejected because shared resources and pages can still create admin-path drift through direct URLs, deep links, or widgets. - Treat every tenant resource as automatically in scope: rejected because some tenant-only surfaces have no admin-path behavior to harden.
Decision 2: Keep OperateHubShell as the canonical admin resolver
Decision: Continue using OperateHubShell::activeEntitledTenant(Request $request): ?Tenant as the only canonical tenant resolver for workspace-admin tenant-sensitive behavior.
Rationale:
- The support layer already encodes the intended admin priority order and entitlement checks.
- Spec 135 established this as the correct canonical pattern.
- A second public resolver would reintroduce the same drift risk under a new name.
Alternatives considered:
- Introduce a new universal resolver service: rejected because the feature must preserve the admin-versus-tenant-panel distinction.
- Resolve tenant state separately in each resource or page: rejected because the defect class is inconsistent surface-level resolution.
Decision 3: Persisted tenant-filter synchronization should standardize on CanonicalAdminTenantFilterState
Decision: Apply CanonicalAdminTenantFilterState::sync() as the standard synchronization primitive for admin surfaces that persist tenant-related filters in session.
Rationale:
- The helper already exists and clears or reseeds persisted tenant filter state based on the current canonical admin tenant.
- Filament v5 explicitly supports
persistFiltersInSession(), so session-backed filter drift must be handled deliberately. AlertDeliveryResourceandAuditLogalready show the intended pattern.
Alternatives considered:
- Disable filter persistence on affected surfaces: rejected because persistence is an existing UX choice outside this feature.
- Hand-roll per-page session fixes: rejected because inconsistent local fixes are exactly what the rollout is trying to eliminate.
Decision 4: Search parity should reuse the admin-aware global-search trait or fall back to disablement
Decision: Reuse ScopesGlobalSearchToTenant for rollout resources that stay globally searchable in admin context, and disable admin-path search when parity cannot be guaranteed cheaply.
Rationale:
- Filament v5 requires a View or Edit page for global-searchable resources.
- The existing trait already distinguishes admin-panel and tenant-panel behavior by using
OperateHubShellin the admin panel andFilament::getTenant()elsewhere. - The constitution requires non-member-safe, tenant-safe global search.
Alternatives considered:
- Leave search behavior implicit: rejected because search can bypass list-level scoping and create the same drift bug.
- Build a separate search-only resolver: rejected because the existing trait already captures the right panel split.
Decision 5: Use AlertDeliveryResource and AuditLog as Type B reference patterns
Decision: Treat AlertDeliveryResource and AuditLog as the reference patterns for workspace-wide datasets with canonical tenant-default behavior.
Rationale:
- Both surfaces already combine workspace-wide data with synchronized tenant-default or tenant-filter behavior.
- Both already use
OperateHubShellandCanonicalAdminTenantFilterStaterather than raw panel-native tenant reads. - They are good comparison points for
ProviderConnectionResource,AuditLogguard expansion, and anyEntraGroupResourceworkspace-wide follow-up behavior.
Alternatives considered:
- Use a pure tenant-scoped resource as the reference pattern: rejected because Type B surfaces have different requirements from Type A surfaces.
- Build a new shared reference helper first: rejected because the current reference patterns already exist in production code.
Decision 6: Treat Inventory page and widget alignment as one rollout slice
Decision: Handle InventoryCoverage and InventoryKpiHeader together so the page shell and embedded KPI widget use the same tenant rule.
Rationale:
- The spec names
InventoryKpiHeaderas an explicit correction case. - Widget drift is one of the exact defect classes the feature is intended to eliminate.
- Page-level tenant context is not trustworthy unless the widget and its linked drill-down behavior follow the same source.
Alternatives considered:
- Fix the page only: rejected because widget-only drift would remain.
- Leave the widget workspace-wide while the page is tenant-sensitive: rejected because it violates one-surface, one-source semantics.
Decision 7: Shared tenant resources require panel-aware hardening even when admin navigation is absent
Decision: Keep shared tenant-sensitive resources such as RestoreRunResource, BackupSetResource, and other rollout targets in scope where their code can still be executed by admin paths, deep links, or shared helper logic, even if they do not register admin navigation.
Rationale:
- The spec explicitly calls out panel-safe behavior and shared-code review.
- Some resources are tenant-scoped in navigation yet still participate in admin-driven links, comparisons, or operational workflows.
- Drift can still occur inside shared methods, action URLs, and record-resolution logic.
Alternatives considered:
- Exclude tenant-routed resources from the rollout entirely: rejected because shared code can still carry unsafe resolver assumptions.
- Force admin registration for all tenant resources: rejected because the feature is about semantics, not information architecture redesign.
Decision 8: Surface classification should be explicit and operational
Decision: Freeze the rollout using three operational classes:
- Type A hard tenant-sensitive:
PolicyResource,BackupScheduleResource,BackupSetResource,FindingResource,BaselineCompareLanding,RestoreRunResource,InventoryItemResource,PolicyVersionResource,TenantDiagnostics,InventoryCoverage,InventoryKpiHeader - Type B workspace-wide with tenant-default:
ProviderConnectionResource,AuditLog,EntraGroupResourcefollow-up when admin path remains workspace-wide with tenant-default semantics - Type C workspace-only:
AlertRuleResource,BaselineProfileResource,BaselineSnapshotResource,TenantResource, and other workspace-owned admin surfaces with no tenant-sensitive execution path
Rationale:
- The rollout needs a stable manifest for guard coverage and test scope.
- The classes match the behavior contract described in the spec.
- Explicit classification prevents accidental over-scoping of workspace-wide surfaces.
Alternatives considered:
- Keep an informal list only: rejected because guard and task generation need explicit categories.
- Add a fourth ambiguous class: rejected because the spec requires ambiguous surfaces to be resolved before implementation.
Decision 9: The guardrail should stay as a focused Pest architecture test
Decision: Expand AdminTenantResolverGuardTest rather than creating a new lint rule or analysis tool.
Rationale:
- The existing guard already scans selected canonical admin files for forbidden raw panel-native tenant reads.
- Pest is already the repo standard for architecture and regression guards.
- A focused allowlist and exception inventory is simpler to maintain than new tooling.
Alternatives considered:
- Add a PHPStan or ESLint-style custom rule: rejected because it is heavier than the feature requires.
- Rely on code review only: rejected because the feature explicitly asks for regression resistance and CI enforcement.
Decision 10: No asset or panel bootstrap changes are needed
Decision: Keep the rollout entirely inside existing Filament resources, pages, widgets, support helpers, and tests with no new assets or panel-provider wiring changes.
Rationale:
- The defect is semantic and behavioral, not presentational.
- Existing admin and tenant panels already provide the right routing and discovery model.
- Avoiding asset and provider changes minimizes regression risk and keeps deployment unchanged.
Alternatives considered:
- Add panel-specific assets or hooks to manage tenant state in the browser: rejected because the canonical source is server-side and already exists.
- Change panel registration or routing: rejected because the feature explicitly excludes panel redesign.