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

17 KiB

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)

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)

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