# Implementation Plan: Spec 080 Workspace-Managed Tenant Administration Migration **Branch**: `080-workspace-managed-tenant-admin` | **Date**: 2026-02-07 | **Spec**: [/specs/080-workspace-managed-tenant-admin/spec.md](spec.md) **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/spec.md` **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. ## Summary Migrate tenant administration surfaces out of tenant scope (`/admin/t/{tenant}/*`) into workspace scope (`/admin/*`) and keep tenant scope strictly for operational modules. Implementation strategy: - Introduce a second Filament panel for tenant operations at `/admin/t/{tenant}`. - Convert the existing admin panel at `/admin` into a tenantless workspace management panel. - Register management resources/pages only in the workspace panel, ensuring tenant-scoped management routes are not registered (404). - Rewire internal CTAs/links (onboarding, required permissions, provider connection edit) to the new canonical workspace routes. ## Technical Context **Language/Version**: PHP 8.4.15 (Laravel 12) **Primary Dependencies**: Filament v5, Livewire v4, Tailwind v4 **Storage**: PostgreSQL (via Sail) **Testing**: Pest v4 (PHPUnit v12 runner) **Target Platform**: Web application (server-rendered Filament/Livewire) **Project Type**: Laravel monolith **Performance Goals**: No new performance targets; management viewers must remain DB-only at render time **Constraints**: - No external calls during render for management viewers (DB-only). - Dev-stage removed routes must 404 (no redirects). - 404 vs 403 semantics must follow RBAC-UX. **Scale/Scope**: Enterprise SaaS IA separation across admin surfaces (route + navigation correctness) ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* Assessment (pre-Phase 0): PASS - No new Graph calls are introduced by this migration. - No new long-running operations are introduced. - Authorization behavior is being tightened via panel separation and route registration. - Monitoring/management viewers remain DB-only. Notes: - This feature changes how routes are registered (which affects discovery, global search, and navigation). It must include regression tests ensuring removed tenant-scoped management routes do not exist. - Inventory-first: clarify what is “last observed” vs snapshots/backups - Read/write separation: any writes require preview + confirmation + audit + tests - Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php` - Deterministic capabilities: capability derivation is testable (snapshot/golden tests) - RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; non-member tenant access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks) - RBAC-UX: destructive-like actions require `->requiresConfirmation()` and clear warning text - RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics) - Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked - Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on `/auth/*` without `OperationRun` - Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter - Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens - Badge semantics (BADGE-001): status-like badges use `BadgeCatalog` / `BadgeRenderer`; no ad-hoc mappings; new values include tests ## Project Structure ### Documentation (this feature) ```text specs/[###-feature]/ ├── plan.md # This file (/speckit.plan command output) ├── research.md # Phase 0 output (/speckit.plan command) ├── data-model.md # Phase 1 output (/speckit.plan command) ├── quickstart.md # Phase 1 output (/speckit.plan command) ├── contracts/ # Phase 1 output (/speckit.plan command) └── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan) ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ ├── Pages/ │ │ ├── Workspaces/ │ │ ├── Monitoring/ │ │ └── … │ ├── Resources/ │ └── Concerns/ ├── Providers/ │ └── Filament/ │ ├── AdminPanelProvider.php │ ├── TenantPanelProvider.php │ └── SystemPanelProvider.php ├── Support/ │ └── Middleware/ routes/ └── web.php tests/ ├── Feature/ └── Unit/ bootstrap/ └── providers.php ``` **Structure Decision**: Laravel monolith. Panel providers define panel boundaries. Routes outside Filament are defined in `routes/web.php`. ## Complexity Tracking No constitution violations requiring justification. ## Phase 0 — Outline & Research Outputs (written during `/speckit.plan`): - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/research.md` Research questions (all resolved): - How to configure a tenancy panel whose URLs are `/admin/t/{tenant}` without duplicating `/t/`. - How to enforce route removal: do not register resources/pages in the tenant panel. - How to keep global search isolated by panel registration. ## Phase 1 — Design & Contracts Outputs: - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/data-model.md` - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/contracts/` - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/quickstart.md` ### Panel Design **Workspace panel (Manage)** - ID: `admin` (keep existing ID to avoid breaking `panel:admin` middleware usage) - Path: `/admin` - Tenancy: disabled (no `->tenant(...)`) - Registered artifacts: tenant management resources/pages, monitoring pages. **Tenant panel (Operate)** - ID: `tenant` (new) - Path: `/admin/t` - Tenancy: enabled via `Tenant::class` with `slugAttribute: 'external_id'` - Tenant route prefix: blank/`null` so tenant routes become `/admin/t/{tenant}/...` - Registered artifacts: operational resources/pages only (inventory, drift, backups, policies, directory, etc.). - Route-shape verification is mandatory: automated regression checks must assert canonical tenant URLs are `/admin/t/{tenant}` and `/admin/t/{tenant}/...` (never `/admin/t/t/{tenant}`). Laravel 11+ provider registration requirement: - Register the new tenant panel provider in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/bootstrap/providers.php`. ### Routing/Removal Mechanism Tenant-scoped management routes return 404 by construction: - Management resources/pages are **not registered** in the tenant panel. - No redirects (dev-stage). ### Authorization Design - Workspace management pages: require selected workspace + membership; non-member → 404. - Tenant operational routes: require workspace membership + tenant entitlement; non-entitled → 404. - Mutations: capability missing → 403 (server-side policy/gate); destructive-like actions require `->requiresConfirmation()`. ### Global Search Design - Workspace panel: managed tenants are searchable (ensure resource has Edit/View page). - Tenant panel: tenant-management entities are not registered, so they cannot appear in global search. ## Phase 2 — Implementation Plan (Code + Tests) Stop condition for `/speckit.plan`: this section outlines implementation, but actual task breakdown happens in `/speckit.tasks`. Planned steps: 1. Add new panel provider for tenant operations (e.g., `App\Providers\Filament\TenantPanelProvider`). 2. Register provider in `bootstrap/providers.php` (Laravel 11+ pattern). 3. Refactor `AdminPanelProvider` into a tenantless workspace panel: - remove tenancy configuration (`->tenant(...)`, tenant menu, tenant route prefix) - remove tenant-only middleware from the workspace panel pipeline 4. Move/register management pages/resources into workspace panel: - `TenantResource` (managed tenants CRUD / manage view) - `ProviderConnectionResource` (workspace-managed connections by tenant) - required permissions viewer page - membership management surfaces - onboarding/activation surfaces 5. Move/register operational pages/resources into the tenant panel. 6. Rewire internal links/CTAs that currently build tenant-scoped management URLs to the new workspace-managed canonical URLs. 7. Add regression tests (Pest) to cover: - workspace member can access `/admin/tenants*` - non-member gets 404 - tenant entitlement required for `/admin/t/{tenant}/...` - canonical tenant panel route shape is `/admin/t/{tenant}/...` (no duplicated `/t`) - tenant-scoped management routes are missing (404) - link rewiring expectations (where feasible) 8. Run targeted test pack and Pint: - `vendor/bin/sail artisan test --compact tests/Feature/...` - `vendor/bin/sail bin pint --dirty`