TenantAtlas/specs/136-admin-canonical-tenant/plan.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

237 lines
17 KiB
Markdown

# Implementation Plan: Spec 136 Admin Panel Canonical Tenant Resolution Full Rollout
**Branch**: `136-admin-canonical-tenant` | **Date**: 2026-03-11 | **Spec**: `specs/136-admin-canonical-tenant/spec.md`
**Spec (absolute)**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/136-admin-canonical-tenant/spec.md`
**Input**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/136-admin-canonical-tenant/spec.md`
## Summary
Complete the rollout of the canonical admin tenant rule established in Spec 135 across all remaining admin-visible and admin-reachable shared tenant-sensitive surfaces.
Workspace-admin flows under `/admin/...` will use `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant` as the single tenant source for header context, queries, filters, widgets, links, and sensitive actions. Workspace-admin navigation will stay workspace-only except for baseline assets, so tenant-sensitive entry points move to the tenant panel under `/admin/t/{tenant}/...`. Admin surfaces with persisted tenant-related filters will standardize on `CanonicalAdminTenantFilterState`. Tenant-panel flows under `/admin/t/{tenant}/...` will keep panel-native `Filament::getTenant()` semantics. Global-search parity will be preserved through the existing admin-aware scoping pattern in `ScopesGlobalSearchToTenant` or explicit disablement where parity cannot be guaranteed cheaply.
Implementation is organized into three rollout waves:
1. high-risk tenant-sensitive resources with sensitive actions or strong drift risk,
2. governance, restore, and inventory alignment surfaces,
3. workspace-wide tenant-default pages, diagnostics, widget alignment, and full guard expansion.
## Technical Context
**Language/Version**: PHP 8.4 on Laravel 12
**Primary Dependencies**: Filament v5, Livewire v4, Pest v4, Laravel Sail
**Storage**: PostgreSQL application database and session-backed Filament table state
**Testing**: Pest feature and unit 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, support-layer helpers, and Pest guard tests
**Performance Goals**: Keep monitoring and admin renders DB-only, keep tenant resolution deterministic per request, and avoid broader-than-visible scopes or stale session-driven tenant drift
**Constraints**:
- No dependency changes.
- No new Graph calls, queued workflows, or scheduled workflows.
- No panel-provider or routing redesign; provider registration remains in `bootstrap/providers.php` and panel routing remains intact.
- No universal resolver abstraction that erases the distinction between workspace-admin and tenant-panel semantics.
- Existing destructive and governance-sensitive actions must keep their current confirmation, authorization, and audit behavior while gaining tenant-target parity.
- No new assets are introduced; deployment remains unchanged and does not add `filament:assets` work for this feature.
**Scale/Scope**: Rollout across the canonical admin shell helper, session filter synchronization helper, admin-aware global-search behavior, 10+ affected resources/pages/widgets, the admin panel provider registration map, one architectural guard suite, and focused regression coverage for tenant switching, stale filters, search parity, and sensitive actions
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- **Inventory-first / snapshots**: PASS — the feature changes request-time resolution and session-backed filter state only; inventory, backup, and snapshot storage remain unchanged.
- **Read/write separation**: PASS — no new write workflow is introduced. Existing destructive and governance-sensitive actions remain existing actions and are only hardened to keep visible tenant and execution tenant aligned.
- **Graph contract path**: PASS — no Graph calls or contract-registry changes are required.
- **Deterministic capabilities**: PASS — no capability registry changes; existing Gates, policies, and capability helpers remain authoritative.
- **RBAC-UX planes**: PASS — `/admin/...` and `/admin/t/{tenant}/...` stay explicitly separate. Admin canonical tenant logic applies only in the admin path, tenant-panel-native logic applies only in tenant routes.
- **Workspace isolation**: PASS — workspace-admin routes remain workspace-scoped and tenant-safe, with deny-as-not-found preserved for non-members or out-of-scope access.
- **Tenant isolation**: PASS — Type A and Type B rollout surfaces will keep tenant entitlement checks aligned across query, widgets, links, detail access, and actions.
- **Global search safety**: PASS — any rollout resource that remains globally searchable must either use admin-safe scoped search with a View page present or disable global search where parity cannot be guaranteed.
- **Run observability**: PASS — no new `OperationRun` types or lifecycle behavior. Monitoring remains DB-only at render time.
- **Ops-UX lifecycle / summary counts / notifications**: PASS — unchanged.
- **BADGE-001**: PASS — badge meanings remain centralized; only the tenant context feeding those views is normalized.
- **UI-NAMING-001**: PASS — existing operator-facing phrases such as tenant labels, safe-state messages, and filter labels remain domain-first and implementation-neutral.
- **Filament Action Surface Contract**: PASS WITH EXEMPTION — affected resources and pages mostly keep their current action surfaces. The rollout verifies tenant-target parity for existing actions instead of changing action inventory.
- **Filament UX-001**: PASS WITH EXEMPTION — no layout redesign is needed; the feature only tightens tenant semantics and explicit safe-state behavior.
- **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 are required; Laravel 12 provider registration remains in `bootstrap/providers.php`.
- **Global-search hard rule**: PASS — `PolicyResource`, `PolicyVersionResource`, and `EntraGroupResource` already have View pages where search parity matters; rollout design requires those resources to retain View-page-backed parity or disable admin-path global search.
- **Destructive action safety**: PASS — `BackupScheduleResource`, `BackupSetResource`, `RestoreRunResource`, `FindingResource`, and `PolicyVersionResource` retain existing destructive or governance-sensitive actions; rollout scope is to preserve confirmation + authorization while guaranteeing the action tenant matches the visible tenant.
- **Asset strategy**: PASS — no new panel assets, no `FilamentAsset::register()`, and no deployment change beyond existing processes.
## Phase 0 — Research Summary
Research findings are recorded in `specs/136-admin-canonical-tenant/research.md`.
Key decisions:
- Treat admin panel provider registration plus direct admin page registration as the source of truth for which surfaces are truly admin-visible, while still reviewing shared resources whose code can run in both panels.
- Keep `OperateHubShell::activeEntitledTenant()` as the only canonical admin tenant resolver instead of inventing a second abstraction.
- Standardize persisted tenant-filter synchronization through `CanonicalAdminTenantFilterState` wherever `persistFiltersInSession()` and tenant-related filters coexist.
- Reuse the existing `ScopesGlobalSearchToTenant` admin-aware search pattern for searchable rollout resources, and disable admin-path search if parity is not cheap or safe.
- Treat `AlertDeliveryResource` and `AuditLog` as reference patterns for workspace-wide datasets with canonical tenant-default behavior.
- Expand `AdminTenantResolverGuardTest` from a narrow allowlist to the full rollout surface set with explicit, documented exceptions for tenant-panel-native files.
## Phase 1 — Design & Contracts
### Data Model
Design details are recorded in `specs/136-admin-canonical-tenant/data-model.md`.
Key design points:
- No schema changes are required.
- The feature models request-time surface classification, tenant-resolution state, persisted filter state, sensitive action parity, and guard coverage as behavioral entities.
- Shared resources are treated as panel-aware execution surfaces rather than purely admin or purely tenant objects.
- Persisted filter state is never trusted without synchronization against the current canonical admin tenant.
### Contracts
Internal behavior contracts are recorded in `specs/136-admin-canonical-tenant/contracts/admin-tenant-resolution-rollout.yaml`.
Contract scope:
- workspace-admin canonical tenant rule versus tenant-panel-native rule
- Type A, Type B, and Type C surface classifications
- rollout expectations for the named resources, pages, and widgets
- search parity rules and no-context behavior
- guard coverage and exception inventory expectations
### Quickstart
Implementation and verification steps are recorded in `specs/136-admin-canonical-tenant/quickstart.md`.
### Post-Design Constitution Re-check
- **Inventory-first / snapshots**: PASS — unchanged.
- **Read/write separation**: PASS — the design only normalizes read, filter, link, and action-target semantics.
- **Graph contract path**: PASS — still no Graph activity.
- **RBAC-UX**: PASS — design keeps 404 for non-members or out-of-scope tenant access and 403 only after scope membership is established for capability-gated mutations.
- **Workspace isolation**: PASS — admin routes remain workspace-scoped even when tenant is a shell concept rather than a route parameter.
- **Tenant isolation**: PASS — list, detail, widget, search, and action paths are designed to use one tenant rule per surface.
- **Global search safety**: PASS — the design explicitly requires admin-safe search parity for searchable resources or explicit disablement.
- **Run observability / Ops-UX lifecycle**: PASS — unchanged.
- **BADGE-001 / UI-NAMING-001**: PASS — centralized badge rendering and product wording remain intact.
- **Filament Action Surface Contract**: PASS WITH EXEMPTION — no new destructive actions, no new action-surface inventory.
- **Livewire v4.0+ compliance**: PASS — unchanged Filament v5 / Livewire v4 stack.
- **Provider registration location**: PASS — no panel or provider changes; `bootstrap/providers.php` remains correct.
- **Asset strategy**: PASS — still no asset registration or deployment delta.
## Project Structure
### Documentation (this feature)
```text
specs/136-admin-canonical-tenant/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── admin-tenant-resolution-rollout.yaml
└── tasks.md
```
### Source Code (repository root)
```text
app/
├── Providers/Filament/
│ └── AdminPanelProvider.php
├── Support/
│ ├── OperateHub/
│ │ └── OperateHubShell.php
│ └── Filament/
│ ├── CanonicalAdminTenantFilterState.php
│ └── ScopesGlobalSearchToTenant.php
├── Filament/
│ ├── Pages/
│ │ ├── Monitoring/
│ │ │ └── AuditLog.php
│ │ ├── BaselineCompareLanding.php
│ │ ├── InventoryCoverage.php
│ │ └── TenantDiagnostics.php
│ ├── Resources/
│ │ ├── PolicyResource.php
│ │ ├── BackupScheduleResource.php
│ │ ├── BackupSetResource.php
│ │ ├── FindingResource.php
│ │ ├── RestoreRunResource.php
│ │ ├── InventoryItemResource.php
│ │ ├── PolicyVersionResource.php
│ │ ├── ProviderConnectionResource.php
│ │ ├── EntraGroupResource.php
│ │ └── AlertDeliveryResource.php
│ └── Widgets/
│ ├── Inventory/
│ │ └── InventoryKpiHeader.php
│ └── Operations/
├── Models/
└── Policies/
tests/
├── Feature/
│ ├── Guards/
│ ├── Filament/
│ ├── Monitoring/
│ └── Spec085/
└── Unit/
```
**Structure Decision**: Keep implementation inside the existing Laravel / Filament monolith. Reuse `OperateHubShell`, `CanonicalAdminTenantFilterState`, and the existing admin-aware search trait instead of adding new infrastructure. Harden affected resources, pages, and widgets in place, then extend the current Pest guard and feature coverage.
## Phase 2 — Implementation Planning
Implementation should be delivered in the following slices so `/speckit.tasks` can break work cleanly.
1. **Freeze the final surface inventory and classifications**
- Use `AdminPanelProvider`, direct admin page registration, and shared resource reachability to finalize the rollout manifest.
- Classify each target as Type A hard tenant-sensitive, Type B workspace-wide with tenant-default, or Type C workspace-only.
- Keep tenant-only resources in the rollout only where shared code, admin deep links, cross-resource URLs, or dual-panel discovery can still create admin-path drift.
2. **Standardize support-layer resolver usage**
- Keep `OperateHubShell::activeEntitledTenant(Request)` as the only public canonical admin tenant resolver.
- Avoid introducing a second public resolver abstraction unless it is a thin, internal delegation wrapper for readability only.
- Audit direct admin-path usages of `Tenant::current()` and `Filament::getTenant()` across the rollout set.
3. **Roll out persisted filter-state hardening**
- Apply `CanonicalAdminTenantFilterState::sync()` to every affected admin surface that combines `persistFiltersInSession()` with tenant-related filters or tenant-default behavior.
- Standardize stale-session behavior so old tenant state is cleared or reseeded before the surface renders.
- Reuse `AlertDeliveryResource` and `AuditLog` as known-good reference patterns.
4. **Wave 1: high-risk tenant-sensitive resources**
- Harden `PolicyResource`, `BackupScheduleResource`, `BackupSetResource`, and `FindingResource` so query, detail, filters, links, and sensitive actions align to the same tenant.
- Verify destructive or governance-sensitive actions retain confirmation and server-side authorization while gaining tenant-target parity.
5. **Wave 2: governance, restore, and inventory alignment**
- Harden `BaselineCompareLanding`, `RestoreRunResource`, `InventoryItemResource`, and `PolicyVersionResource`.
- Treat `InventoryCoverage` and `InventoryKpiHeader` as one unit so page and widget cannot diverge.
- Review `RestoreRunResource` and other shared tenant resources as panel-aware code paths even where admin navigation is intentionally absent.
6. **Wave 3: workspace-wide tenant-default, diagnostics, and search parity**
- Harden `ProviderConnectionResource`, `TenantDiagnostics`, `AuditLog`, and `EntraGroupResource` follow-up behavior.
- Keep workspace-wide datasets workspace-wide while synchronizing tenant-default labels, filters, deep links, and persisted state.
- Preserve or explicitly disable admin-path global search where parity is not safe.
7. **Expand the guardrail and developer guidance**
- Extend `AdminTenantResolverGuardTest` to the full rollout surface set.
- Maintain a narrow, explicit exception inventory for tenant-panel-native files and other approved panel-native surfaces.
- Update short developer guidance so future admin surfaces reuse the canonical rule and synchronized filter-state pattern.
8. **Finish with regression coverage and manual tenant-switch verification**
- Add direct tests for `CanonicalAdminTenantFilterState`.
- Add focused feature tests for wrong-tenant drift, stale filters, shared-surface panel behavior, search parity, and wrong-tenant sensitive-action prevention.
- Record a manual tenant-switch check per rollout wave for representative Type A and Type B surfaces.
## Testing Strategy
- Extend `tests/Feature/Guards/AdminTenantResolverGuardTest.php` to cover the full rollout manifest and exceptions.
- Add direct unit or feature coverage for `CanonicalAdminTenantFilterState` behavior on tenant change, stale state, and invalid persisted state.
- Add focused feature coverage for:
- `PolicyResource`, `BackupScheduleResource`, `BackupSetResource`, and `FindingResource` tenant parity across list/detail/action paths,
- `BaselineCompareLanding`, `InventoryCoverage`, and `InventoryKpiHeader` page-widget alignment,
- `RestoreRunResource`, `InventoryItemResource`, and `PolicyVersionResource` shared resource parity across panel modes,
- `ProviderConnectionResource`, `AuditLog`, and `EntraGroupResource` workspace-wide tenant-default behavior and search or deep-link safety,
- representative 404 vs 403 authorization semantics for admin-path tenant-sensitive access.
- Reuse existing Pest helpers, factories, and workspace or tenant session setup; avoid bespoke harnesses.
- Run the minimum affected suite with Sail during implementation, then finish with `vendor/bin/sail bin pint --dirty --format agent`.
## Complexity Tracking
No constitution violations or complexity exemptions are required for this plan.