# 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. - `AlertDeliveryResource` and `AuditLog` already 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 `OperateHubShell` in the admin panel and `Filament::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 `OperateHubShell` and `CanonicalAdminTenantFilterState` rather than raw panel-native tenant reads. - They are good comparison points for `ProviderConnectionResource`, `AuditLog` guard expansion, and any `EntraGroupResource` workspace-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 `InventoryKpiHeader` as 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`, `EntraGroupResource` follow-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.