TenantAtlas/specs/136-admin-canonical-tenant/research.md
ahmido 45a804970e feat: complete admin canonical tenant rollout (#165)
## Summary
- complete Spec 136 canonical admin tenant rollout across admin-visible and shared Filament surfaces
- add the shared panel-aware tenant resolver helper, persisted filter-state synchronization, and admin navigation segregation for tenant-sensitive resources
- expand regression, guard, and parity coverage for admin-path tenant resolution, stale filters, workspace-wide tenant-default surfaces, and panel split behavior

## Validation
- `vendor/bin/sail artisan test --compact tests/Feature/Guards/AdminTenantResolverGuardTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TableStatePersistenceTest.php`
- `vendor/bin/sail artisan test --compact --filter='CanonicalAdminTenantFilterState|PolicyResource|BackupSchedule|BackupSet|FindingResource|BaselineCompareLanding|RestoreRunResource|InventoryItemResource|PolicyVersionResource|ProviderConnectionResource|TenantDiagnostics|InventoryCoverage|InventoryKpiHeader|AuditLog|EntraGroup'`
- `vendor/bin/sail bin pint --dirty --format agent`

## Notes
- Livewire v4.0+ compliance is preserved with Filament v5.
- Provider registration remains unchanged in `bootstrap/providers.php`.
- `PolicyResource` and `PolicyVersionResource` have admin global search disabled explicitly; `EntraGroupResource` keeps admin-aware scoped search with a View page.
- Destructive and governance-sensitive actions retain existing confirmation and authorization behavior while using canonical tenant parity.
- No new assets were introduced, so deployment asset strategy is unchanged and does not add new `filament:assets` work.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #165
2026-03-13 08:09:20 +00:00

134 lines
9.2 KiB
Markdown

# 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.