TenantAtlas/specs/292-workspace-tenant-closure/spec.md
Ahmed Darrazi adf9237152
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m32s
feat: implement workspace and tenant closure lifecycle
2026-05-07 15:08:20 +02:00

48 KiB

Feature Specification: Workspace & Tenant Closure Lifecycle v1

Feature Branch: 292-workspace-tenant-closure
Created: 2026-05-07
Status: Approved for implementation
Input: User description: "Promote the lifecycle taxonomy follow-through into one bounded runtime slice that closes workspaces, removes tenants from workspaces, and defines explicit suspended read-only versus closed behavior without introducing purge flows."

Spec Candidate Check (mandatory - SPEC-GATE-001)

  • Problem: TenantPilot already has partial lifecycle truth for tenant operability, archived workspaces, and subscription-driven suspended read-only posture, but it still has no explicit product truth for deliberately closing a workspace or removing a tenant from a workspace while preserving history.
  • Today's failure: Operators can archive records or lose access implicitly through membership or context drift, but they cannot make an explicit, auditable closure decision. archived, suspended read-only, selector invalidation, and historical access are still separate behaviors rather than one bounded closure contract.
  • User-visible improvement: Platform and workspace operators can close or reopen workspaces and remove or restore tenants with explicit confirmation, clear read-only behavior, consistent chooser recovery, and preserved historical viewers instead of relying on implicit 404s or overloaded archive semantics.
  • Smallest enterprise-capable version: Add explicit closure truth on Workspace, explicit removed-from-workspace truth on ManagedEnvironment, reuse existing admin and system Filament surfaces, keep history readable, block new mutations and starts where required, and stop short of export, purge, or billing-provider workflows.
  • Explicit non-goals: No hard delete, no purge engine, no export-before-delete flow, no retention executor, no customer self-serve billing portal, no payment-provider integration, no membership auto-deletion, no new panel, no new global-search resource, no broad lifecycle engine, and no reopening of the full Spec 262 taxonomy package.
  • Permanent complexity imported: One bounded closure/removal truth on existing records, one bounded write orchestration path, explicit close/reopen and remove/restore action surfaces, chooser and context recovery rules, and focused audit plus feature-test coverage. No new standalone closure table or workflow engine is introduced.
  • Why now: Spec 262 deliberately reserved Workspace & Tenant Closure Lifecycle v1 as the next runtime follow-through after the taxonomy-first package, and the repo now has enough real substrate in tenant operability, commercial lifecycle, audit, and workspace context handling to implement it safely.
  • Why not local: Closure and removal semantics affect workspace selection, tenant selection, tenant-bound routes, canonical run viewers, action gating, audit, and platform/admin surfaces together. A local page fix would preserve ambiguity and drift.
  • Approval class: Core Enterprise
  • Red flags triggered: New lifecycle truth, cross-plane UI impact, and destructive-like action semantics. Defense: the slice stays explicitly bounded, reuses existing surfaces and audit paths, adds no purge or export behavior, and resists a generic lifecycle framework.
  • Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | Gesamt: 10/12
  • Decision: approve

Spec Scope Fields (mandatory)

  • Scope: workspace + tenant-bound + canonical-view
  • Primary Routes:
    • /system/directory/workspaces
    • /system/directory/workspaces/{workspace}
    • /system/directory/tenants/{tenant}
    • /system/ops/runs/{run}
    • /admin/workspaces
    • /admin/workspaces/{record}
    • /admin/choose-workspace
    • /admin/choose-tenant
    • /admin/settings/workspace
    • existing tenant-management resource pages backed by apps/platform/app/Filament/Resources/TenantResource.php
    • existing tenant-context routes under /admin/t/{tenant}/...
  • Data Ownership:
    • Workspace closure truth remains workspace-owned and lives on the existing Workspace record.
    • Tenant removal truth remains workspace-owned on the existing ManagedEnvironment record and does not create cross-workspace sharing.
    • Workspace memberships, tenant memberships, audit logs, evidence, review artifacts, and OperationRun history remain owned by their current records and are preserved in place.
    • This feature does not introduce new historical ledgers, export bundles, or purge artifacts.
  • RBAC:
    • Authorization planes involved: platform /system for workspace close and reopen; admin /admin for workspace read-only visibility, chooser recovery, and tenant remove or restore within the current workspace.
    • Non-members or actors outside the relevant workspace or tenant scope receive deny-as-not-found (404).
    • Actors who are in scope but lack the required capability receive forbidden (403).
    • Canonical record viewers continue to authorize off record ownership and entitlement, not off remembered workspace or tenant context.

For canonical-view specs, the spec MUST define:

  • Default filter behavior when tenant-context is active: Closed workspaces and removed tenants must never become the remembered active context. Canonical viewers may still render historically linked records when entitlement allows, even if the current remembered context was cleared.
  • Explicit entitlement checks preventing cross-tenant leakage: Canonical viewers must validate workspace ownership of the record, referenced-tenant ownership where applicable, active actor entitlement to the workspace and referenced tenant, and route legitimacy independent of remembered selector state. Closed or removed lifecycle flags are product posture, not authorization shortcuts.

Cross-Cutting / Shared Pattern Reuse (mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write N/A - no shared interaction family touched)

  • Cross-cutting feature?: yes
  • Interaction class(es): status messaging, badges, header actions, row or detail actions, chooser recovery messaging, canonical detail viewers, and audit-backed lifecycle copy
  • Systems touched: WorkspaceContext, chooser pages, tenant operability, commercial lifecycle summaries, admin and system detail pages, audit logging, and canonical run or tenant viewers
  • Existing pattern(s) to extend: WorkspaceCommercialLifecycleResolver, TenantOperabilityService, BadgeCatalog and BadgeRenderer, current audit-log infrastructure, current Filament action surfaces, and existing chooser or context-recovery flows
  • Shared contract / presenter / builder / renderer to reuse: WorkspaceContext, TenantOperabilityService, BadgeCatalog and BadgeRenderer, WorkspaceAuditLogger, tenant audit logging, current Filament detail and table action patterns, and the current system-directory plus admin resource surfaces
  • Why the existing shared path is sufficient or insufficient: The repo already has the shared chooser, audit, and status paths needed for bounded runtime delivery. What is missing is the explicit closure and removed-from-workspace truth those shared paths must consume.
  • Allowed deviation and why: none. This feature must not create a parallel lifecycle language or a page-local closure vocabulary.
  • Consistency impact: Suspended read-only, Closed, Removed from workspace, Archived, and Provider missing must remain distinct meanings with consistent badges, copy, and blocked-action explanations.
  • Review focus: Reviewers must verify that workspace closure and tenant removal reuse the existing shared status, chooser, audit, and canonical viewer seams instead of introducing one-off page logic.
  • Touches OperationRun start/completion/link UX?: yes, by lifecycle gating and canonical historical viewing only
  • Shared OperationRun UX contract/layer reused: existing shared OperationRun start UX and current canonical monitoring surfaces remain authoritative
  • Delegated start/completion UX behaviors: blocked starts in closed workspaces or removed-tenant contexts must happen before enqueue, create no new run, and keep current View run or canonical-detail links unchanged for already-existing history
  • Local surface-owned behavior that remains: system and admin surfaces own only the close/reopen and remove/restore inputs plus the impact summary shown before confirmation
  • Queued DB-notification policy: unchanged; this feature introduces no new queued notification family
  • Terminal notification path: unchanged; no new OperationRun lifecycle is introduced
  • Exception required?: none

Provider Boundary / Platform Core Check (mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write N/A - no shared provider/platform boundary touched)

N/A - no shared provider or platform boundary is broadened here. Workspace closure and tenant removal are workspace-owned lifecycle concerns and must not be modeled as provider state.

UI / Surface Guardrail Impact (mandatory when operator-facing surfaces are changed; otherwise write N/A)

Surface / Change Operator-facing surface change? Native vs Custom Shared-Family Relevance State Layers Touched Exception Needed? Low-Impact / N/A Note
System workspace detail close or reopen controls yes Native Filament header actions, badges, audit-backed detail summaries page, detail no n/a
Admin workspace detail read-only closure summary yes Native Filament status messaging, summary sections page, detail no n/a
Managed tenant list and detail remove or restore controls yes Native Filament row/detail actions, badges, lifecycle explanations page, detail no n/a
Workspace and tenant chooser recovery after closure or removal yes Native Filament + global context shell navigation, shell recovery, selector messaging shell, page no special shell handling only
Canonical run and tenant viewers for removed or closed history yes Native Filament shared detail family, historical evidence visibility detail no no new mutation surface

Decision-First Surface Role (mandatory when operator-facing surfaces are changed)

Surface Decision Role Human-in-the-loop Moment Immediately Visible for First Decision On-Demand Detail / Evidence Why This Is Primary or Why Not Workflow Alignment Attention-load Reduction
System workspace detail Primary Decision Surface Decide whether a workspace should be closed or reopened Current posture, impact summary, closure reason, one dominant action Audit history, affected tenant counts, related historical records Primary because platform users make the closure decision here Follows workspace governance, not storage internals Removes guesswork about what closure changes and what remains readable
Managed tenant list and detail Primary Decision Surface Decide whether a tenant remains in the active workspace set Tenant posture, removal impact, one dominant remove or restore action Related memberships, historical runs, audit trail Primary because workspace operators govern tenant presence here Aligns to tenant-management workflow Avoids overloading archive and implicit chooser disappearance
Admin workspace detail Secondary Context Surface Understand why the workspace is read-only or unavailable for active selection Workspace posture, read-only explanation, next allowed inspection path Closure reason, timestamps, linked history Not primary because the admin plane does not own closure mutation Supports post-decision inspection inside the workspace family Keeps one clear explanation instead of repeating blockers across pages
Workspace and tenant chooser recovery Secondary Context Surface Recover from a cleared or invalid remembered context Why the prior context is invalid and one next step Optional timestamps or posture detail only if needed Not primary because the user is recovering from an already-made decision Follows operator recovery flow Replaces silent redirects with one clear explanation
Canonical run and historical viewers Tertiary Evidence / Diagnostics Surface Inspect historical evidence after closure or removal Historical record remains readable plus lifecycle badge on related workspace or tenant Linked audit trail, tenant or workspace metadata Not primary because the feature does not ask operators to decide here Keeps evidence and history accessible without reopening active context Prevents false not-found errors when context changed

Audience-Aware Disclosure (mandatory when operator-facing surfaces are changed)

Surface Audience Modes In Scope Decision-First Default-Visible Content Operator Diagnostics Support / Raw Evidence One Dominant Next Action Hidden / Gated By Default Duplicate-Truth Prevention
System workspace detail support-platform Current posture, closure reason summary, impact, close or reopen action Related tenant counts, membership posture, recent activity Full audit events and internal IDs Close workspace or Reopen workspace Raw audit payloads and low-level IDs remain secondary One top summary states the posture once; lower sections add evidence only
Managed tenant list and detail operator-MSP, support-platform Tenant posture, removal explanation, remove or restore action Related memberships, lifecycle timestamps, affected history counts Raw audit details and internal identifiers Remove tenant or Restore tenant Raw audit details stay secondary or support-only The tenant posture chip and summary line are the single visible source of truth
Admin workspace detail operator-MSP Workspace posture and what remains readable Commercial-state detail, closure timestamps, guidance to history surfaces Platform-only internal detail stays hidden Review workspace history Platform-only internal detail stays hidden Read-only explanation appears once in the summary region
Workspace and tenant chooser recovery operator-MSP Why the context was cleared and which chooser or page to use next Minimal recovery detail only None Choose workspace or Choose tenant Internal route or entitlement diagnostics stay hidden Recovery page states the blocker once and points to one next action
Canonical run and historical viewers operator-MSP, support-platform Historical record remains readable and the related workspace or tenant posture Linked workspace and tenant metadata, timestamps, linked audit Raw identifiers and platform-only debug metadata Review history Raw metadata remains secondary The record summary owns the lifecycle note so deeper sections do not restate it

UI/UX Surface Classification (mandatory when operator-facing surfaces are changed)

Surface Action Surface Class Surface Type Likely Next Operator Action Primary Inspect/Open Model Row Click Secondary Actions Placement Destructive Actions Placement Canonical Collection Route Canonical Detail Route Scope Signals Canonical Noun Critical Truth Visible by Default Exception Type / Justification
System workspace detail Detail / Header Actions Governance detail Close or reopen workspace Route-record detail n/a Secondary detail links stay inside sections Detail header only with confirmation /system/directory/workspaces /system/directory/workspaces/{workspace} Workspace identity, posture badge Workspace Open, suspended read-only, or closed posture plus impact none
Admin workspace detail Detail / Context Summary Workspace management detail Review closure posture and history Route-record detail n/a Related links inside sections none on admin plane for closure /admin/workspaces /admin/workspaces/{record} Workspace identity, posture badge Workspace Read-only explanation and next allowed path none
Managed tenant list List / Table / More Registry list Inspect tenant and decide remove or restore Full-row click required More for remove or restore and rare actions More only /admin/tenants /admin/tenants/{tenant} Workspace context, tenant posture Tenant Active versus removed posture none
Managed tenant detail Detail / Header Actions Governance detail Remove or restore tenant Route-record detail n/a Secondary lifecycle links inside sections Detail header or More with confirmation /admin/tenants /admin/tenants/{tenant} Workspace identity, tenant posture Tenant Removed-from-workspace state and impact none
Workspace and tenant chooser recovery Shell / Recovery Global-context shell Recover to a valid current context Single recovery card / chooser list chooser row click only None beyond recovery guidance none /admin/choose-workspace or /admin/choose-tenant same as collection Current workspace and tenant context validity Workspace / Tenant Why the previous context is invalid global-context-shell
Canonical run and historical viewers Detail / Evidence Historical detail Inspect history without reopening active context Route-record detail n/a Secondary related links inside sections none /system/ops/runs or current historical collection /system/ops/runs/{run} and related detail routes Workspace or tenant posture badges Run / Historical record Historical legitimacy plus related lifecycle note shared-detail-family

Operator Surface Contract (mandatory when operator-facing surfaces are changed)

Surface Primary Persona Decision / Operator Action Supported Surface Type Primary Operator Question Default-visible Information Diagnostics-only Information Status Dimensions Used Mutation Scope Primary Actions Dangerous Actions
System workspace detail Platform operator Decide whether workspace should be closed or reopened Governance detail Should this workspace remain operable? Workspace posture, closure reason, impact, what remains readable Full audit trail, internal IDs, related history counts commercial posture, closure posture TenantPilot only Close workspace, Reopen workspace Close workspace
Admin workspace detail Workspace owner or manager Understand why current workspace is read-only or unavailable for active context Workspace management detail What does this posture change for my workspace work? Workspace posture, read-only explanation, history access path Timestamps and related summary counts commercial posture, closure posture None on this surface Review workspace history None
Managed tenant list and detail Workspace owner Decide whether tenant remains part of the workspace's active operating set Governance list/detail Should this tenant stay operable in this workspace? Tenant posture, removal explanation, effect on selectors and actions Membership and historical record details tenant lifecycle, removed-from-workspace posture TenantPilot only Remove tenant, Restore tenant Remove tenant
Workspace and tenant chooser recovery Workspace member Recover from invalid remembered context Global-context shell Which valid workspace or tenant should I use now? Recovery explanation and one next chooser action Minimal recovery detail only context validity, closure or removal posture None Choose workspace, Choose tenant None
Canonical run and historical viewers Workspace operator or platform operator Inspect history after closure or removal Historical detail Is this record still legitimate and what lifecycle posture does it reflect? Record summary plus related workspace or tenant posture Audit links and related metadata record status, workspace or tenant posture None Review history None

Proportionality Review (mandatory when structural complexity is introduced)

  • New source of truth?: yes - explicit workspace closed truth and explicit tenant removed-from-workspace truth on existing records
  • New persisted entity/table/artifact?: yes - new persisted lifecycle fields on Workspace and ManagedEnvironment; no new standalone entity or table family beyond targeted columns
  • New abstraction?: yes, one bounded write-orchestration service if needed for close/reopen and remove/restore audit-safe mutations; no generic lifecycle framework
  • New enum/state/reason family?: yes - explicit closed and removed-from-workspace posture with audit reason text and derived blocked-action consequences
  • New cross-domain UI framework/taxonomy?: no
  • Current operator problem: the product cannot currently make a deliberate closure or removal decision without misusing archive semantics, implicit 404s, or commercial suspension language.
  • Existing structure is insufficient because: archived_at on workspaces and the subscription resolver's suspended-read-only posture do not encode an explicit closure decision, and current tenant lifecycle does not cover workspace-specific removal while preserving history.
  • Narrowest correct implementation: add bounded fields on existing records, derive read-only and selector behavior from those fields plus current commercial posture, reuse current surfaces, and keep mutations inside one bounded orchestration seam.
  • Ownership cost: migration work, model and middleware touch points, Filament surface updates, audit-log additions, and focused feature-test maintenance.
  • Alternative intentionally rejected: overloading archive or suspended-read-only semantics was rejected because it would preserve ambiguity; a generic lifecycle engine or new closure table was rejected because current-release truth only needs bounded fields and shared-surface reuse.
  • Release truth: current-release truth

Compatibility posture

This feature assumes a pre-production environment.

Backward compatibility, migration shims, historical alias states, and compatibility-specific tests are out of scope unless implementation reveals a concrete current-release blocker.

Canonical replacement of ambiguous archive or suspension meanings is preferred over preserving overloaded semantics.

Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)

  • Test purpose / classification: Feature
  • Validation lane(s): fast-feedback, confidence
  • Why this classification and these lanes are sufficient: The slice changes Filament surfaces, middleware or context recovery, audit-backed mutations, and canonical viewer legitimacy. Focused feature coverage is the narrowest honest proof because route, policy, and page behavior matter more than isolated unit indirection.
  • New or expanded test families: focused feature coverage for system directory, admin workspace and tenant resources, chooser recovery, and canonical run or historical viewers
  • Fixture / helper cost impact: moderate but bounded; test setup needs explicit workspace membership, tenant membership, commercial posture, and closure or removal state, but no new heavy provider or browser defaults
  • Heavy-family visibility / justification: none; browser coverage is not required for the initial proof path
  • Special surface test profile: standard-native-filament, global-context-shell, shared-detail-family
  • Standard-native relief or required special coverage: functional-core plus state-contract coverage is required; browser or heavy-governance coverage is out of scope unless implementation proves a shell-only gap that feature tests cannot honestly prove
  • Reviewer handoff: reviewers must verify that close/reopen and remove/restore remain confirmation-protected and audit-backed, that chooser recovery clears invalid context explicitly, that blocked start or mutate paths do not create runs, and that canonical historical viewers stay accessible when entitlement allows
  • Budget / baseline / trend impact: none expected beyond one feature-local increase in admin and system feature coverage
  • Escalation needed: none
  • Active feature PR close-out entry: Guardrail
  • Planned validation commands:
    • 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
    • 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
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent

User Scenarios & Testing (mandatory)

User Story 1 - Close a workspace without losing history (Priority: P1)

As a platform operator, I need to close and later reopen a workspace explicitly, so that I can stop active operations and mutations while preserving readable history and audit evidence.

Why this priority: Workspace closure is the core enterprise-trust behavior missing from the current product. Without it, every related lifecycle action still depends on implicit archive or access loss.

Independent Test: Can be fully tested by closing a workspace from the system directory, confirming chooser and action gating update, verifying historical viewers remain readable for entitled actors, and reopening the workspace to restore normal operability.

Acceptance Scenarios:

  1. Given a platform actor is authorized to govern a workspace, When the actor closes the workspace with confirmation and a reason, Then the workspace becomes non-selectable as current context, in-scope active mutations are blocked, and the closure decision is written to audit.
  2. Given a workspace is closed, When an entitled actor opens a historical run or workspace detail viewer, Then the record remains readable with explicit closed posture and no false not-found error.
  3. Given a workspace is closed, When the platform actor reopens it, Then the workspace becomes selectable again and existing memberships plus historical records remain intact.

User Story 2 - Remove a tenant from a workspace without deleting tenant history (Priority: P1)

As a workspace owner, I need to remove and later restore a tenant from the active workspace set, so that the tenant stops appearing as an operable context without destroying its history, memberships, or canonical records.

Why this priority: Workspace operators need a bounded lifecycle action smaller than deletion and more explicit than archive or hidden selector disappearance.

Independent Test: Can be fully tested by removing a tenant from the tenant-management surface, confirming chooser and tenant-context routes stop treating it as operable, verifying canonical historical viewers still render, and restoring the tenant.

Acceptance Scenarios:

  1. Given an owner is authorized for tenant governance, When the owner removes a tenant from the workspace with confirmation and a reason, Then the tenant no longer appears in choose-tenant or tenant-context start flows and the action is written to audit.
  2. Given a tenant was removed from the workspace, When an entitled actor opens a canonical run or historical detail page that references that tenant, Then the record remains readable and the tenant is labeled removed from workspace.
  3. Given a tenant is removed from the workspace, When the owner restores it, Then the tenant becomes selectable and operable again without recreating memberships or historical records.

User Story 3 - Distinguish suspended read-only, closed, and removed clearly (Priority: P2)

As an operator, I need workspace and tenant posture to explain whether I am blocked because the workspace is commercially suspended, explicitly closed, or the tenant was removed from the workspace, so that I know what next action is legitimate.

Why this priority: Clear posture language prevents operators from treating commercial billing state, workspace closure, and tenant removal as the same event.

Independent Test: Can be fully tested by rendering admin and system surfaces plus chooser recovery for a suspended-read-only workspace, a closed workspace, and a removed tenant and confirming each posture has distinct copy, badge semantics, and blocked-action behavior.

Acceptance Scenarios:

  1. Given a workspace is commercially suspended but not closed, When the actor opens admin or system summaries, Then the posture reads suspended read-only and not closed.
  2. Given a workspace is closed, When the actor lands on chooser or current-context recovery surfaces, Then the UI explains that the prior workspace is closed and offers one valid next action instead of a silent redirect.
  3. Given a tenant is removed from the workspace, When the actor visits tenant management or historical detail surfaces, Then the tenant is labeled removed from workspace and not archived or provider missing.

Edge Cases

  • A user lands on /admin with a remembered current workspace that was closed after the last session; the product must clear the invalid context and route through an explicit recovery path.
  • A remembered current tenant was removed from the workspace while the workspace itself remains open; the tenant context must be cleared without breaking workspace-scoped pages.
  • A workspace is already SUSPENDED_READ_ONLY from the commercial resolver and then becomes explicitly closed; the product must show both truths without collapsing them into one ambiguous blocker.
  • A removed tenant or closed workspace is still referenced by OperationRun, audit, evidence, or review artifacts; canonical viewers must stay readable when entitlement allows.
  • A workspace close or tenant removal action is attempted while a last-owner or active-membership guard would otherwise make the result unsafe; the feature must preserve current owner-guard behavior and surface a clear failure reason.
  • A reopen or restore action is performed after the remembered context was cleared; the product must not silently reactivate old context without the chooser or explicit selection flow confirming it.

Requirements (mandatory)

Constitution alignment (required): This feature introduces no Microsoft Graph calls. It does introduce destructive-like lifecycle mutations, so close/reopen and remove/restore must be previewed, confirmation-protected, audit-logged, authorization-checked server-side, and covered by focused tests. No purge or hard-delete behavior is allowed in this slice.

Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001): The feature adds bounded persisted lifecycle truth on existing records because current release truth now requires a deliberate closure posture distinct from archive and commercial suspension. A generic lifecycle engine, registry, or new closure history ledger is explicitly out of scope. The narrowest correct implementation is to add fields on existing records, derive blocking behavior from those fields plus current commercial posture, and keep write orchestration bounded.

Constitution alignment (XCUT-001): The feature reuses existing chooser, context, badge, audit, and canonical-viewer seams. It must not introduce page-local status vocabularies or a parallel action language for closure and removal.

Constitution alignment (DECIDE-AUD-001 / OPSURF-001): System workspace detail and managed tenant detail are the primary decision surfaces. Chooser recovery and historical viewers remain secondary or tertiary surfaces. Default-visible content must explain posture and one next action; deeper audit and raw identifiers stay secondary.

Constitution alignment (PROV-001): This slice is platform-core lifecycle work. Provider presence remains a separate lifecycle dimension from Spec 261 and must not be reused to model closure or removal.

Constitution alignment (TEST-GOV-001): Proof stays in focused feature coverage for native Filament surfaces, chooser recovery, and canonical viewers. No heavy-governance or browser lane should be introduced unless implementation reveals a shell-only gap that feature tests cannot honestly prove.

Constitution alignment (OPS-UX): The feature does not add a new OperationRun type. It does require that blocked starts in closed workspaces or removed-tenant contexts fail before enqueue and that existing canonical run viewers remain authoritative and readable when entitlement allows.

Constitution alignment (OPS-UX-START-001): Existing start surfaces continue to rely on the shared OperationRun start UX path when allowed. Close or remove posture only changes whether a start is permitted; it must not introduce local queued toast, dedupe, or notification semantics.

Constitution alignment (RBAC-UX): Platform and admin planes remain separated. Workspace non-members, tenant non-members, and wrong-plane actors get 404; in-scope actors lacking capability get 403. Destructive-like actions must use ->requiresConfirmation() and server-side authorization. Closed or removed posture is never a substitute for an authorization decision.

Constitution alignment (OPS-EX-AUTH-001): This feature does not change auth-handshake behavior.

Constitution alignment (BADGE-001): Workspace and tenant posture badges must be centralized. Suspended read-only, Closed, and Removed from workspace must not fall back to ad hoc labels or local color choices.

Constitution alignment (UI-FIL-001): All changed admin and system surfaces remain native Filament pages or resources. No local card system, no ad hoc status-color system, and no fake row interactivity may be introduced. One dominant action per primary decision surface is required.

Constitution alignment (UI-NAMING-001): Primary operator-facing verbs are Close workspace, Reopen workspace, Remove tenant, and Restore tenant. Workspace and Tenant are the real target objects and must remain stable across buttons, modals, audit prose, and notifications.

Constitution alignment (DECIDE-001): This feature adds explicit human-in-the-loop lifecycle decisions. The default experience must become calmer and clearer by separating active context, read-only closure posture, and historical evidence rather than adding more ambiguous lifecycle states.

Constitution alignment (UI-CONST-001 / UI-SURF-001 / ACTSURF-001 / UI-HARD-001 / UI-EX-001 / UI-REVIEW-001 / HDR-001): Changed surfaces must preserve exactly one primary inspect/open model, keep destructive actions in header or More, avoid redundant view actions, and keep chooser recovery and historical viewers focused on one immediate question.

Constitution alignment (ACTSURF-001 - action hierarchy): Workspace close/reopen and tenant remove/restore must not compete with navigation. Primary decision surfaces keep one dominant action; rare or adjacent lifecycle actions stay grouped.

Constitution alignment (OPSURF-001): Every changed surface must show whether the blocker is commercial suspension, explicit closure, or tenant removal. Mutation scope must stay legible: all lifecycle mutations in this slice are TenantPilot-only and do not mutate Microsoft tenants.

Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001): The feature must map existing domain truth directly to UI posture without adding a new presentation taxonomy or semantic wrapper layer. Tests should prove user-facing business consequences and access behavior, not thin indirection.

Constitution alignment (Filament Action Surfaces): System workspace detail, admin workspace detail, managed tenant list and detail, chooser recovery, and any touched historical viewers must satisfy the action-surface contract described in the matrix below.

Constitution alignment (UX-001 - Layout & Information Architecture): Existing Filament layouts remain in force. Any added summary or warning content must fit current Main/Aside or detail-section layouts, keep one primary action, and preserve native empty-state and badge semantics.

Functional Requirements

  • FR-001: The system MUST model workspace closure as explicit product truth distinct from archived_at and distinct from commercial SUSPENDED_READ_ONLY posture.
  • FR-002: Authorized platform actors MUST be able to close and reopen a workspace through an explicit confirmation flow that captures a human-readable reason and writes an audit event.
  • FR-003: A closed workspace MUST stop being selectable as the current workspace in /admin/choose-workspace and MUST clear any remembered tenant context tied to that workspace.
  • FR-004: A closed workspace MUST block the in-scope tenant start surface and the in-scope state-changing admin surfaces touched by this slice while preserving entitled read access to management and historical viewers.
  • FR-005: The admin plane MUST show read-only closure posture for an affected workspace without exposing a second closure-mutation plane.
  • FR-006: The system MUST model tenant removal from workspace as explicit product truth distinct from tenant archive and distinct from provider-missing lifecycle.
  • FR-007: Authorized workspace actors MUST be able to remove and restore a tenant from the workspace through explicit confirmation flows that capture a reason and write audit events.
  • FR-008: A removed tenant MUST stop being selectable in /admin/choose-tenant and MUST stop being a valid tenant-context route target under /admin/t/{tenant}/....
  • FR-009: A removed tenant MUST remain inspectable on tenant-management and canonical historical viewer surfaces when the actor remains entitled to the workspace and tenant history.
  • FR-010: Canonical historical viewers in scope for this v1, specifically system run viewers and system workspace or tenant historical detail viewers, MUST remain viewable when they reference a closed workspace or removed tenant and entitlement still allows access.
  • FR-011: The system MUST preserve existing workspace-membership and tenant-membership records during close/reopen and remove/restore flows in this v1 slice; no membership purge or recreation workflow is allowed.
  • FR-012: The product MUST distinguish Suspended read-only, Closed, and Removed from workspace with separate badge semantics, copy, and blocked-action explanations.
  • FR-013: Chooser and context-recovery surfaces MUST explain why a remembered context was cleared and MUST offer one valid next action rather than silently redirecting.
  • FR-014: Authorization on affected surfaces MUST preserve 404 for non-members or wrong-plane actors and 403 for in-scope actors missing capability.
  • FR-015: Lifecycle mutations in this feature MUST be labeled consistently as Close workspace, Reopen workspace, Remove tenant, and Restore tenant across buttons, modals, notifications, and audit prose.
  • FR-016: Close/reopen and remove/restore actions MUST be TenantPilot-only mutations; no Microsoft tenant or provider mutation is performed in this slice.
  • FR-017: Blocked starts caused by closed workspaces or removed tenants on the in-scope tenant start surface MUST not create OperationRun records, local blocked-run substitutes, or new notification families.
  • FR-018: The feature MUST preserve current owner-guard and membership-safety behavior rather than bypassing it through lifecycle mutations.
  • FR-019: This feature MUST not widen discovery: existing global-search and list entitlement rules remain unchanged, and no new discovery path may leak closed or removed records to actors who are not already entitled to inspect them.
  • FR-020: The feature MUST not add purge, export-before-delete, retention execution, or billing-provider logic.

UI Action Matrix (mandatory when Filament is changed)

If this feature adds or modifies any Filament Resource / RelationManager / Page, fill out the matrix below.

For each surface, list the exact action labels, whether they are destructive (confirmation? typed confirmation?), RBAC gating (capability + enforcement helper), whether the mutation writes an audit log, and any exemption or exception used.

Surface Location Header Actions Inspect Affordance (List/Table) Row Actions (max 2 visible) Bulk Actions (grouped) Empty-State CTA(s) View Header Actions Create/Edit Save+Cancel Audit log? Notes / Exemptions
System workspace detail apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php Close workspace or Reopen workspace Route-record detail none none n/a Close workspace or Reopen workspace n/a yes Platform capability only; confirmation required; no second control plane on /admin
Admin workspace detail apps/platform/app/Filament/Resources/Workspaces/Pages/ViewWorkspace.php none added in this slice Route-record detail none none n/a none for closure on admin plane Save/cancel unchanged where edit already exists no direct mutation Read-only summary only
Managed tenant list apps/platform/app/Filament/Resources/TenantResource.php list page none beyond current list controls Full-row click to tenant detail current safe shortcuts only; Remove tenant or Restore tenant lives in More none in this slice existing tenant-creation CTA stays unchanged n/a n/a yes for remove/restore Destructive-like action grouped to More
Managed tenant detail apps/platform/app/Filament/Resources/TenantResource.php view page Remove tenant or Restore tenant plus existing safe navigation Route-record detail none none n/a Remove tenant or Restore tenant Save/cancel unchanged where edit already exists yes Owner or equivalent capability only; confirmation required
Workspace chooser apps/platform/app/Filament/Pages/ChooseWorkspace.php none chooser row or card select Choose workspace none existing empty-state CTA only if no valid workspace exists n/a n/a no Closed workspaces excluded from selectable set
Tenant chooser apps/platform/app/Filament/Pages/ChooseTenant.php none chooser row or card select Choose tenant for active tenants only none existing empty-state CTA only if no valid tenant exists n/a n/a no Removed tenants excluded from selectable set

Key Entities (include if feature involves data)

  • Workspace closure truth: Explicit lifecycle posture on Workspace that marks a workspace as closed or reopened and records the reason and actor without deleting the workspace.
  • Tenant removal truth: Explicit lifecycle posture on ManagedEnvironment that marks a tenant as removed from the active workspace set or restored without deleting the tenant record.
  • Remembered workspace and tenant context: Convenience selection state that must be cleared when closure or removal makes it invalid.
  • Canonical historical viewer in scope: Existing system run viewers plus system workspace and tenant detail viewers that remain readable even when active context becomes invalid.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: In validation scenarios, 100% of close/reopen and remove/restore actions show explicit posture and impact summary with no ambiguous fallback to Archived.
  • SC-002: In validation scenarios, 0 closed workspaces and 0 removed tenants appear as selectable choices in the normal workspace and tenant choosers.
  • SC-003: In validation scenarios, 100% of entitled system run viewers and in-scope system historical detail viewers remain accessible when they reference a closed workspace or removed tenant.
  • SC-004: In validation scenarios, 100% of blocked actions explain whether the blocker is Suspended read-only, Closed, or Removed from workspace.
  • SC-005: In validation scenarios, 100% of lifecycle mutations covered by this slice write audit events that include actor, prior state, new state, timestamp, and reason.

Summary

Workspace closure and tenant removal are the next runtime follow-through after the lifecycle taxonomy foundation from Spec 262. The feature does not attempt deletion, purge, or export. It introduces one explicit workspace-level closure truth, one explicit tenant removed-from-workspace truth, and the minimum surface, chooser, and canonical-view behavior needed to make those truths operable and auditable.

The key product distinction is that commercial suspension, explicit workspace closure, tenant removal from workspace, tenant archive, and provider-missing lifecycle are not the same thing. This feature productizes the first two missing runtime decisions without reopening the broader lifecycle package.

Goals

  • Let platform users close and reopen workspaces deliberately and auditably.
  • Let workspace owners remove and restore tenants from the active workspace set without deleting history.
  • Make chooser, context-recovery, and canonical-view behavior explicit when closure or removal invalidates active context.
  • Keep suspended read-only posture distinct from explicit closure and tenant removal.
  • Reuse current Filament, audit, chooser, and canonical-view seams rather than introducing a new lifecycle framework.

Non-Goals

  • No purge, export-before-delete, or retention-execution workflow.
  • No hard delete or membership-deletion cascade.
  • No payment-provider or billing-portal work.
  • No reopening of broader provider lifecycle or retention taxonomy work.
  • No new panel, global-search resource, or custom UI system.

Assumptions

  • WorkspaceCommercialLifecycleResolver remains the source of commercial suspension truth and is not replaced by closure logic.
  • Workspace memberships and tenant memberships remain the entitlement backbone even when a workspace is closed or a tenant is removed.
  • Existing canonical historical viewers already have legitimate workspace identity and can keep rendering when lifecycle posture changes.
  • Close/reopen and remove/restore are TenantPilot-only mutations in this v1.

Risks

  • Closure and removal could be mistakenly folded into existing archive semantics if the implementation does not keep badges, copy, and blocked-action explanations distinct.
  • Chooser recovery could regress into silent redirects if the feature does not explicitly handle cleared context.
  • Canonical historical viewers could accidentally inherit active-context requirements and start returning false not-found errors.
  • The slice could widen into purge, export, or billing-provider logic unless the follow-up boundaries stay explicit.

Follow-Up Work

  • Export-before-delete and retention/purge governance remain separate follow-up specs.
  • Any future customer self-service offboarding or billing-driven closure workflow remains a separate commercial or portal slice.
  • Broader lifecycle alignment for other governed objects remains outside this workspace and tenant closure follow-through.

Final Direction

The intended runtime contract is: a suspended workspace stays readable but commercially blocked, a closed workspace becomes non-selectable and read-only for entitled actors, and a removed tenant stops being an active workspace context without losing historical legitimacy. The implementation should add only the truth and behavior needed to make those distinctions real.