--- description: "Task list for Workspace & Tenant Closure Lifecycle v1" --- # Tasks: Workspace & Tenant Closure Lifecycle v1 **Input**: Design documents from `specs/292-workspace-tenant-closure/` **Prerequisites**: `specs/292-workspace-tenant-closure/spec.md`, `specs/292-workspace-tenant-closure/plan.md` **Tests**: REQUIRED (Pest). Keep proof bounded to focused `Feature` coverage for system directory, admin workspace and tenant surfaces, chooser recovery, and canonical historical viewers. **Operations**: Reuse the existing `OperationRun` start UX and canonical run viewers. No new run type, no queue family, and no local blocked-run substitute are allowed. **RBAC**: Wrong-plane or non-member access remains `404`; in-scope actors missing capability remain `403`. Closure/removal posture is never an authorization shortcut. **Shared Pattern Reuse**: Reuse `WorkspaceContext`, `TenantOperabilityService`, `WorkspaceCommercialLifecycleResolver`, `BadgeCatalog` / `BadgeRenderer`, current audit infrastructure, and current Filament action-surface patterns. **Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration remains unchanged in `apps/platform/bootstrap/providers.php`. No new panel, no new globally searchable resource, and no new asset strategy are allowed. **Organization**: Tasks are grouped by user story so workspace closure, tenant removal, and posture clarity remain independently implementable and testable. ## Test Governance Checklist - [x] Lane assignment stays `fast-feedback` and `confidence` and remains the narrowest sufficient proof. - [x] New or changed tests stay in focused `Feature` families only unless a bounded implementation seam proves a unit test is necessary. - [x] Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default. - [x] Planned validation commands cover closure, removal, chooser recovery, and historical viewer legitimacy without widening into browser or heavy-governance lanes. - [x] The declared surface test profiles remain `standard-native-filament`, `global-context-shell`, and `shared-detail-family` only. - [x] Any drift toward purge, export, billing workflow, or a second mutation plane resolves as `reject-or-split`, not hidden scope. ## Phase 1: Setup (Shared Context) **Purpose**: Confirm the current lifecycle, chooser, and history seams before any runtime change begins. - [x] T001 Review `specs/292-workspace-tenant-closure/spec.md`, `specs/292-workspace-tenant-closure/plan.md`, `specs/262-lifecycle-governance-taxonomy/spec.md`, `specs/143-tenant-lifecycle-operability-context-semantics/spec.md`, and `specs/274-billing-subscription-truth/spec.md` together so the slice stays grounded in current lifecycle and commercial truth. - [x] T002 [P] Confirm the current system and admin surface seams in `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php`, `apps/platform/app/Filament/System/Pages/Directory/ViewTenant.php`, `apps/platform/app/Filament/System/Pages/Ops/ViewRun.php`, `apps/platform/app/Filament/Resources/Workspaces/Pages/ViewWorkspace.php`, and `apps/platform/app/Filament/Resources/TenantResource.php`. - [x] T003 [P] Confirm the chooser and context seams in `apps/platform/app/Support/Workspaces/WorkspaceContext.php`, `apps/platform/app/Http/Middleware/EnsureWorkspaceSelected.php`, `apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php`, `apps/platform/app/Filament/Pages/ChooseWorkspace.php`, and `apps/platform/app/Filament/Pages/ChooseTenant.php`. - [x] T004 [P] Confirm the current lifecycle and audit seams in `apps/platform/app/Services/Tenants/TenantOperabilityService.php`, `apps/platform/app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php`, `apps/platform/app/Services/Audit/WorkspaceAuditLogger.php`, and the current tenant audit logging path. --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Add the bounded lifecycle truth and write-side seam before surface behavior changes. **Critical**: No user-story work should begin until this phase is complete. - [x] T005 [P] Add failing feature coverage in `apps/platform/tests/Feature/System/Directory/ViewWorkspaceClosureTest.php` and `apps/platform/tests/Feature/System/Ops/ClosedWorkspaceHistoricalAccessTest.php` to lock close/reopen behavior, historical readability, and pre-enqueue blocking. - [x] T006 [P] Add failing feature coverage in `apps/platform/tests/Feature/Filament/Resources/Workspaces/WorkspaceClosureStatusTest.php` and `apps/platform/tests/Feature/Filament/Pages/WorkspaceContextClosureRecoveryTest.php` to lock admin read-only posture, chooser recovery, and cleared-context behavior. - [x] T007 [P] Add failing feature coverage in `apps/platform/tests/Feature/Filament/Resources/TenantResource/TenantWorkspaceRemovalTest.php` to lock remove/restore behavior, chooser exclusion, and tenant-context denial rules. - [x] T008 Create `apps/platform/database/migrations/*_add_workspace_closure_fields.php` and `apps/platform/database/migrations/*_add_managed_environment_workspace_removal_fields.php` so existing records can store explicit closure and removal truth. - [x] T009 Update `apps/platform/app/Models/Workspace.php` and `apps/platform/app/Models/ManagedEnvironment.php` with casts, bounded helper methods, and relationship accessors for the new lifecycle truth. - [x] T010 Implement `apps/platform/app/Services/Workspaces/WorkspaceLifecycleService.php` as the one bounded orchestration seam for close/reopen and remove/restore plus audit-safe state transitions, and keep membership rows preserved rather than recreated or deleted. - [x] T011 Extend `apps/platform/app/Services/Audit/WorkspaceAuditLogger.php` and the current tenant audit logging path with stable action IDs and metadata for close/reopen and remove/restore. - [x] T012 Update the relevant policy and capability seams so close/reopen and remove/restore enforce server-side authorization with the required `404` versus `403` behavior. **Checkpoint**: Workspace and tenant lifecycle truth exists, audit is wired, and write-side behavior is centralized before UI or chooser changes land. --- ## Phase 3: User Story 1 - Close a workspace without losing history (Priority: P1) **Goal**: Authorized platform users can close and reopen workspaces explicitly while preserving readable history. **Independent Test**: Close a workspace from the system detail page, confirm chooser and action gating update, verify historical viewers remain readable, and reopen the workspace. ### Tests for User Story 1 - [x] T013 [P] [US1] Extend `apps/platform/tests/Feature/System/Directory/ViewWorkspaceClosureTest.php` to prove confirmation, impact summary, reason capture, clear guard-failure reasons on unsafe close attempts, canonical success or error notification copy, audit metadata, membership preservation, reopen behavior, and blocked mutation or start behavior. - [x] T014 [P] [US1] Extend `apps/platform/tests/Feature/Filament/Resources/Workspaces/WorkspaceClosureStatusTest.php` to prove admin read-only posture, distinct closed versus suspended copy, and the absence of a second workspace-closure mutation plane. ### Implementation for User Story 1 - [x] T015 [US1] Update `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php` and `apps/platform/app/Filament/System/Pages/Ops/ViewRun.php` so the system surfaces render closure posture, impact summary, and keep closed-workspace history readable with canonical success or error notification copy and clear guard-failure messaging on unsafe attempts. - [x] T016 [US1] Update `apps/platform/app/Support/Workspaces/WorkspaceContext.php`, `apps/platform/app/Http/Middleware/EnsureWorkspaceSelected.php`, `apps/platform/app/Filament/Pages/ChooseWorkspace.php`, and `apps/platform/app/Filament/Widgets/Tenant/TenantReviewPackCard.php` so closed workspaces clear invalid remembered context, route through explicit recovery messaging, and block the in-scope tenant start surface before enqueue. - [x] T017 [US1] Update `apps/platform/app/Filament/Resources/Workspaces/Pages/ViewWorkspace.php` and `apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php` so the admin plane shows read-only closure posture, defers in-scope blocked-state checks to `apps/platform/app/Services/Workspaces/WorkspaceLifecycleService.php`, and does not expose closure mutation controls. **Checkpoint**: Workspace closure becomes an explicit, auditable, read-only posture with preserved historical access. --- ## Phase 4: User Story 2 - Remove a tenant from a workspace without deleting tenant history (Priority: P1) **Goal**: Workspace owners can remove and restore tenants from the active workspace set without losing historical legitimacy. **Independent Test**: Remove a tenant from the tenant-management surface, confirm chooser and tenant-context routes treat it as non-operable, verify historical viewers still render, and restore the tenant. ### Tests for User Story 2 - [x] T018 [P] [US2] Extend `apps/platform/tests/Feature/Filament/Resources/TenantResource/TenantWorkspaceRemovalTest.php` to prove confirmation, impact summary, reason capture, clear guard-failure reasons on unsafe remove attempts, canonical success or error notification copy, audit metadata, membership preservation, restore behavior, chooser exclusion, tenant-context denial, and representative removed-tenant blocked-start no-`OperationRun` behavior on `apps/platform/app/Filament/Widgets/Tenant/TenantReviewPackCard.php`. - [x] T019 [P] [US2] Extend `apps/platform/tests/Feature/System/Ops/ClosedWorkspaceHistoricalAccessTest.php` to prove historical viewers remain readable when the referenced tenant is removed from the workspace. ### Implementation for User Story 2 - [x] T020 [US2] Update `apps/platform/app/Filament/Resources/TenantResource.php` and the resource's view-page action surface so remove/restore actions, impact summaries, canonical success or error notification copy, posture badges, and grouped destructive actions follow the spec contract. - [x] T021 [US2] Update `apps/platform/app/Services/Tenants/TenantOperabilityService.php`, `apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php`, `apps/platform/app/Support/Middleware/DenyNonMemberTenantAccess.php`, `apps/platform/app/Filament/Pages/ChooseTenant.php`, tenant-memory handling in `apps/platform/app/Support/Workspaces/WorkspaceContext.php`, and `apps/platform/app/Filament/Widgets/Tenant/TenantReviewPackCard.php` so removed tenants are no longer valid active context and cannot enqueue new runs. - [x] T022 [US2] Update `apps/platform/app/Filament/System/Pages/Directory/ViewTenant.php` and `apps/platform/app/Filament/System/Pages/Ops/ViewRun.php` so removed tenants remain historically visible with explicit posture and no false not-found behavior. **Checkpoint**: Tenant removal becomes explicit, reversible, and historically safe without remaining an active workspace context. --- ## Phase 5: User Story 3 - Distinguish suspended read-only, closed, and removed clearly (Priority: P2) **Goal**: Operators can tell which lifecycle posture is blocking them and what next action remains legitimate. **Independent Test**: Render admin, system, chooser, and historical surfaces for suspended-read-only, closed, and removed states and verify distinct copy and badges. ### Tests for User Story 3 - [x] T023 [P] [US3] Extend `apps/platform/tests/Feature/System/Directory/ViewWorkspaceClosureTest.php`, `apps/platform/tests/Feature/Filament/Resources/Workspaces/WorkspaceClosureStatusTest.php`, and `apps/platform/tests/Feature/Filament/Resources/TenantResource/TenantWorkspaceRemovalTest.php` to prove distinct posture labels, centralized badge mapping, disclosure ordering, blocked-action explanations, and one dominant next action. ### Implementation for User Story 3 - [x] T024 [US3] Update posture rendering on `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php`, `apps/platform/app/Filament/Resources/Workspaces/Pages/ViewWorkspace.php`, and `apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php` so `Suspended read-only` remains distinct from `Closed` and uses shared badge semantics rather than local mappings. - [x] T025 [US3] Update posture rendering on `apps/platform/app/Filament/Resources/TenantResource.php`, `apps/platform/app/Filament/System/Pages/Directory/ViewTenant.php`, and chooser recovery messaging so `Removed from workspace` remains distinct from archive and provider-missing semantics, and keep decision content first with diagnostics secondary. **Checkpoint**: Lifecycle posture becomes explicit and non-ambiguous across the affected operator and history surfaces. --- ## Phase 6: Polish & Cross-Cutting Validation **Purpose**: Validate the bounded slice and stop without widening scope. - [x] T026 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/Directory/ViewWorkspaceClosureTest.php tests/Feature/System/Ops/ClosedWorkspaceHistoricalAccessTest.php`. - [x] T027 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/Resources/Workspaces/WorkspaceClosureStatusTest.php tests/Feature/Filament/Resources/TenantResource/TenantWorkspaceRemovalTest.php tests/Feature/Filament/Pages/WorkspaceContextClosureRecoveryTest.php`. - [x] T028 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`. - [x] T029 [P] Review touched code to confirm Filament stays on Livewire v4, provider registration remains unchanged in `apps/platform/bootstrap/providers.php`, no globally searchable resource or wider discovery path is added, no asset strategy changes appear, and no second closure-mutation plane slipped in. - [x] T030 [P] Record the final guardrail and test-governance outcome in the implementation close-out without reopening purge, export, billing, or portal scope. --- ## Dependencies & Execution Order ### Phase Dependencies - **Phase 1 (Setup)**: no dependencies; start immediately. - **Phase 2 (Foundational)**: depends on Phase 1 and blocks all user stories. - **Phase 3 (US1)**: depends on Phase 2 and establishes explicit workspace closure truth plus chooser recovery. - **Phase 4 (US2)**: depends on Phase 2 and should land after or alongside US1 so tenant removal composes with the new workspace posture rules. - **Phase 5 (US3)**: depends on Phases 3 and 4 because it clarifies the final shared posture language. - **Phase 6 (Polish)**: depends on all desired user stories being complete. ### User Story Dependencies - **US1 (P1)**: independently testable after Phase 2 and delivers the central workspace-closure capability. - **US2 (P1)**: independently testable after Phase 2 and delivers the central tenant-removal capability. - **US3 (P2)**: depends on the completed runtime posture rules from US1 and US2. ### Within Each User Story - Write the listed Pest coverage first and make it fail for the intended gap. - Keep implementation inside the existing model, service, middleware, Filament, and audit seams named above. - Re-run the narrowest relevant validation command after each story checkpoint before moving on. --- ## Implementation Strategy ### Suggested MVP Scope - MVP = **US1 + US2 together**. The feature is only trustworthy when workspace closure and tenant removal both exist and historical readability remains intact. ### Incremental Delivery 1. Complete Phase 1 and Phase 2. 2. Deliver US1 so explicit workspace closure and chooser recovery exist. 3. Deliver US2 so tenants can be removed or restored without losing history. 4. Deliver US3 so posture language across the affected surfaces becomes unambiguous. 5. Finish with the focused validation and drift-review tasks in Phase 6. ### Team Strategy 1. Settle persistence and bounded service shape first. 2. Parallelize failing tests within each story before runtime edits. 3. Serialize merges around `WorkspaceContext`, `TenantOperabilityService`, `ViewWorkspace`, and `TenantResource` so posture language stays coherent. --- ## Deferred Follow-Ups / Non-Goals - export-before-delete workflow - retention and purge governance - customer self-serve workspace offboarding or billing-driven closure - provider-level lifecycle expansion beyond the current separate specs - lifecycle dashboard or workbench surfaces