TenantAtlas/specs/251-commercial-entitlements-billing-state/plan.md
ahmido 7ee4909212
Some checks failed
Main Confidence / confidence (push) Failing after 1m45s
feat: commercial lifecycle overlay for workspace entitlements (#292)
## Summary
- add the bounded workspace commercial lifecycle overlay from spec 251 on top of the existing entitlement substrate
- expose audited commercial state inspection and mutation on the system workspace detail surface
- gate onboarding activation and review-pack start actions through the shared lifecycle decision while preserving suspended read-only access to existing review, evidence, and generated-pack history
- add focused Pest coverage plus the spec/plan/tasks/data-model/contract artifacts for the feature

## Validation
- targeted Pest unit and feature lanes for lifecycle resolution, system-plane mutation, onboarding gating, review-pack enforcement, download preservation, customer review workspace access, and evidence snapshot access
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- integrated browser smoke on the system workspace detail and the preserved read-only review/evidence/review-pack surfaces

## Notes
- branch: `251-commercial-entitlements-billing-state`
- base: `dev`
- commit: `606e9760`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #292
2026-04-28 13:39:33 +00:00

298 lines
33 KiB
Markdown

# Implementation Plan: Commercial Entitlements and Billing-State Maturity
**Branch**: `251-commercial-entitlements-billing-state` | **Date**: 2026-04-28 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/spec.md`
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
## Summary
- Layer one bounded workspace commercial lifecycle overlay on top of the already-real Spec 247 entitlement substrate, not beside it. The existing `WorkspaceEntitlementResolver` remains canonical for plan/default/override truth, and the new slice adds one explicit lifecycle state plus action-family outcomes for onboarding activation, review-pack start, and preserved read-only history access.
- Keep mutation narrow and platform-owned: persist lifecycle state through the existing workspace settings infrastructure, expose inspection plus state change from the existing system workspace detail surface, and keep `/admin` limited to contextual allow, warn, or block messaging on onboarding and review-pack surfaces.
- Preserve current review/evidence/download truth while suspended. New lifecycle blocking must stop future onboarding activation and future review-pack starts before any tenant mutation, `ReviewPack`, or `OperationRun` creation, while leaving already-generated history and evidence consumption under current RBAC intact.
## Technical Context
**Language/Version**: PHP 8.4 (Laravel 12)
**Primary Dependencies**: Filament v5 + Livewire v4, existing workspace settings stack (`SettingsRegistry`, `SettingsResolver`, `SettingsWriter`), `WorkspaceEntitlementResolver`, `ReviewPackService`, system directory detail page
**Storage**: PostgreSQL via existing `workspace_settings` rows plus existing audit log records; no new table or billing/account model
**Testing**: Pest unit and feature tests via Laravel Sail
**Validation Lanes**: fast-feedback, confidence
**Target Platform**: Monorepo Laravel web application in `apps/platform`, using existing Filament admin and system panels
**Project Type**: web
**Performance Goals**: Reuse existing settings reads and current workspace aggregates only, add no new external calls during render, keep review-pack dedupe and shared run UX unchanged when allowed, and short-circuit blocked review-pack starts before any `ReviewPack` or `OperationRun` write
**Constraints**: One commercial lifecycle overlay only, four bounded states, two real gated behavior families, preserved authorized read-only history/evidence/download access while suspended, explicit `/admin` vs `/system` separation, no payment provider/invoice/checkout/website/broad billing-engine scope
**Scale/Scope**: One bounded lifecycle resolver, one system-plane mutation surface, one platform capability addition, one onboarding gate, one review-pack action-family gate, and focused lifecycle/read-only test coverage
## Filament v5 / Panel Notes
- **Livewire v4.0+ compliance**: The slice stays inside existing Filament v5 pages, widgets, resources, and Livewire-backed actions. No Livewire v3 assumptions or compatibility work are introduced.
- **Provider registration location**: No panel/provider registration changes are planned. Existing Laravel 12 + Filament provider registration remains in `bootstrap/providers.php`.
- **Global search**: No new globally searchable resource is introduced. Current global-search behavior remains unchanged.
- **Destructive and high-impact actions**: The future `Change commercial state` action on the system workspace detail page must use `->requiresConfirmation()`, require platform authorization, and write audit history. The `Suspended / read-only` transition is the only high-risk path in scope. Review-pack and onboarding blocks remain non-destructive business-state responses, not hidden authorization failures.
- **Asset strategy**: No new panel or shared assets are planned. Deployment remains unchanged, including `cd apps/platform && php artisan filament:assets` when registered Filament assets are deployed elsewhere in the product.
## UI / Surface Guardrail Plan
- **Guardrail scope**: changed surfaces
- **Native vs custom classification summary**: mixed
- **Shared-family relevance**: system detail controls, status messaging, onboarding helper text, review-pack action gating, review/evidence viewer messaging
- **State layers in scope**: page, detail
- **Audience modes in scope**: operator-MSP, support-platform, customer/read-only
- **Decision/diagnostic/raw hierarchy plan**: decision-first on the system workspace detail surface and on the immediate onboarding/review-pack action context; diagnostics-second via the existing entitlement substrate and review/run/history context; no new raw/support payload surface is planned
- **Raw/support gating plan**: capability-gated system-plane inspection only; customer/read-only surfaces remain calm and evidence-first
- **One-primary-action / duplicate-truth control**: `/system/directory/workspaces/{workspace}` remains the only mutation surface; onboarding and review-pack surfaces show only the local lifecycle consequence required for the immediate action; suspended read-only history pages do not restate the whole commercial profile
- **Handling modes by drift class or surface**: review-mandatory because one lifecycle vocabulary must stay consistent across system, onboarding, review-pack, and read-only history surfaces
- **Repository-signal treatment**: review-mandatory
- **Special surface test profiles**: standard-native-filament, shared-detail-family, monitoring-state-page
- **Required tests or manual smoke**: functional-core, state-contract
- **Exception path and spread control**: no second admin-plane commercial mutation surface, no page-local lifecycle labels, and no broad suspension sweep across unrelated mutable surfaces
- **Active feature PR close-out entry**: Guardrail
## Shared Pattern & System Fit
- **Cross-cutting feature marker**: yes
- **Systems touched**: `SettingsRegistry`, `SettingsResolver`, `SettingsWriter`, existing workspace-setting audit path, `WorkspaceEntitlementResolver`, `WorkspaceSettings` as the current entitlement substrate reference, `App\Filament\System\Pages\Directory\ViewWorkspace`, `ManagedTenantOnboardingWizard`, `ReviewPackService`, `TenantReviewPackCard`, `ReviewRegister`, `TenantReviewResource`, `ReviewPackResource`, `CustomerReviewWorkspace`, `EvidenceSnapshotResource`, and `WorkspaceEntitlementBlockedException`
- **Shared abstractions reused**: `WorkspaceEntitlementResolver`, `WorkspacePlanProfileCatalog`, `SettingsResolver`, `SettingsWriter`, current workspace audit logging, `ReviewPackService`, `OperationUxPresenter`, `OperationRunLinks`, `Capabilities`, `PlatformCapabilities`, and existing Filament action/resource surfaces
- **New abstraction introduced? why?**: one bounded `WorkspaceCommercialLifecycleResolver` is justified because the existing entitlement resolver answers per-key entitlement truth but does not express one workspace-wide lifecycle posture with action-family outcomes, preserved read-only semantics, or system/admin messaging
- **Why the existing abstraction was sufficient or insufficient**: Spec 247 already provides canonical entitlement substrate truth and must remain the foundation. It is insufficient for `trial`, `grace`, `active_paid`, and `suspended_read_only` because those states cut across more than one entitlement key and need one central business-state explanation
- **Bounded deviation / spread control**: no page-local lifecycle conditionals and no second exception taxonomy by default; prefer reusing the existing blocked decision payload/catch path for review-pack actions unless implementation proves that the current class name or payload cannot carry lifecycle metadata cleanly
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: yes
- **Central contract reused**: existing shared review-pack OperationRun start UX through `ReviewPackService`, `OperationUxPresenter`, and `OperationRunLinks`
- **Delegated UX behaviors**: queued toast, run link, run-enqueued browser event, dedupe messaging, and existing terminal notifications remain unchanged when review-pack generation is allowed; lifecycle-blocked starts create no `OperationRun`, no queued DB notification, and no terminal notification
- **Surface-owned behavior kept local**: onboarding completion helper text, review-pack tooltips/disabled state, and suspended read-only explanation on history surfaces remain local projections of the central lifecycle decision
- **Queued DB-notification policy**: unchanged explicit opt-in only
- **Terminal notification path**: unchanged central lifecycle mechanism for existing review-pack runs only
- **Exception path**: none planned; lifecycle blocking must happen before `ReviewPackService` creates or reuses a `ReviewPack` or `OperationRun`, and the preferred later implementation is to extend the current blocked-decision payload rather than invent a second parallel business-state exception family
## Provider Boundary & Portability Fit
- **Shared provider/platform boundary touched?**: no
- **Provider-owned seams**: N/A
- **Platform-core seams**: workspace commercial lifecycle vocabulary, lifecycle rationale, action-family outcomes, system/admin messaging, and audit semantics
- **Neutral platform terms / contracts preserved**: `workspace`, `trial`, `grace`, `active paid`, `suspended / read-only`, `commercial state`, `review pack`, `managed tenant activation`
- **Retained provider-specific semantics and why**: none; review-pack generation stays provider-backed operationally, but the new lifecycle vocabulary remains platform-core and provider-neutral
- **Bounded extraction or follow-up path**: none
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: PASS - the slice adds workspace-owned business state, not new inventory or backup truth.
- Read/write separation: PASS - the only new write is a confirmation-protected, audited system-plane lifecycle mutation using existing workspace settings persistence.
- Graph contract path: PASS - no new Microsoft Graph path is introduced.
- Deterministic capabilities: PASS - admin-plane capabilities remain unchanged, and any new platform capability stays registry-backed.
- RBAC-UX: PASS - `/admin` and `/system` remain separated; wrong-plane and non-member access stay 404; member-without-capability stays 403; otherwise-authorized actors get a business-state block or warning instead of authorization failure.
- Workspace isolation: PASS - admin-plane contextual behavior still requires established workspace context.
- RBAC-UX destructive confirmation: PASS - the future system-plane state-change action must require confirmation and rationale.
- RBAC-UX global search: PASS - no new searchable resource or search scope is introduced.
- Tenant isolation: PASS - onboarding, review-pack, review history, evidence, and download surfaces remain tenant-safe.
- Run observability: PASS - review-pack generation keeps the existing `OperationRun` path when allowed, and blocked starts stop before run creation.
- OperationRun start UX: PASS - the plan preserves shared review-pack start UX and inserts lifecycle blocking before run creation.
- Ops-UX 3-surface feedback: PASS - existing feedback stays toast + progress surfaces + terminal notification only when a run exists.
- Ops-UX lifecycle: PASS - no new `OperationRun` lifecycle contract is introduced.
- Ops-UX summary counts: N/A - no `summary_counts` shape change is planned.
- Ops-UX guards: N/A - no new run guard family is planned in the planning slice.
- Ops-UX system runs: N/A - initiator-null behavior is unchanged.
- Automation: N/A - no new queued or scheduled workflow family is introduced.
- Data minimization: PASS - no payment payloads, account records, or provider secrets are introduced.
- Test governance (TEST-GOV-001): PASS - the plan stays in focused unit + feature lanes with explicit proof commands and limited fixture growth.
- Proportionality (PROP-001): PASS - persistence stays in existing settings rows, and the only new structural element is one bounded lifecycle overlay.
- No premature abstraction (ABSTR-001): PASS - no interface, registry, strategy system, or framework is planned; only one local resolver is added because multiple real surfaces already need the same lifecycle decision.
- Persisted truth (PERSIST-001): PASS - no new table or durable artifact is introduced.
- Behavioral state (STATE-001): PASS - `grace` and `suspended_read_only` create distinct action-family consequences immediately, and `trial` remains justified because it is part of the explicit platform-managed commercial posture and audit workflow even though its two in-scope gated families currently match `active_paid`.
- UI semantics (UI-SEM-001): PASS - the plan prefers direct mapping from lifecycle truth to helper text and badges instead of a new presentation framework.
- Shared pattern first (XCUT-001): PASS - system detail, onboarding, review-pack, and read-only history surfaces all reuse the existing substrate and shared run path first.
- Provider boundary (PROV-001): PASS - the new vocabulary is platform-core, not Microsoft-shaped.
- V1 explicitness / few layers (V1-EXP-001, LAYER-001): PASS - one explicit state family plus one thin overlay resolver is the narrowest viable shape.
- Spec discipline / bloat check (SPEC-DISC-001, BLOAT-001): PASS - the plan keeps the whole lifecycle overlay in one coherent spec and includes proportionality review below.
- Badge semantics (BADGE-001): PASS - any future lifecycle badge must reuse shared badge semantics or stay plain text; no page-local color taxonomy is planned.
- Filament-native UI (UI-FIL-001): PASS - the slice extends existing Filament pages, resources, widgets, and the current system Blade page.
- Filament-native UI local Blade/Tailwind: PASS - the existing system Blade view remains the only custom-rendered surface in scope and must preserve current Filament visual language.
- UI/UX surface taxonomy (UI-CONST-001 / UI-SURF-001): PASS - existing system detail, guided onboarding, action family, and read-only viewer surface types remain intact.
- Decision-first operating model (DECIDE-001): PASS - system workspace detail is primary, onboarding/review-pack surfaces stay contextual, and read-only history/evidence pages remain tertiary evidence/diagnostics.
- Audience-aware disclosure (DECIDE-AUD-001 / OPSURF-001): PASS - system detail stays platform/support-facing, admin action gates stay operator-first, and suspended read-only pages keep customer-safe history access without raw platform diagnostics.
- UI/UX inspect model (UI-HARD-001): PASS - no duplicate inspect affordances are added.
- UI/UX action hierarchy (UI-HARD-001 / UI-EX-001): PASS - the plan keeps one system mutation action and existing onboarding/review-pack primary actions in place.
- UI/UX scope, truth, and naming (UI-HARD-001 / UI-NAMING-001 / OPSURF-001): PASS - labels remain narrow and billing-provider-free.
- UI/UX placeholder ban (UI-HARD-001): PASS - no empty action groups are planned.
- UI naming (UI-NAMING-001): PASS - primary labels stay `Change commercial state`, `Complete onboarding`, `Generate pack`, `Regenerate`, and `Export executive pack`.
- Operator surfaces (OPSURF-001): PASS - mutation scope remains explicit, and `/admin` surfaces only show contextual lifecycle truth.
- Operator surface page contract: PASS - the spec already defines the required surface contracts.
- Filament UI Action Surface Contract: PASS - touched surfaces already have contracts or exemptions; the plan preserves them while adding lifecycle truth.
- Filament UI UX-001 (Layout & IA): PASS - no new page shell or panel is introduced.
- Action-surface discipline (ACTSURF-001 / HDR-001): PASS - one system primary action and existing onboarding/review-pack action families remain the only primary mutations in scope.
- UI review workflow: PASS - guardrail, shared-family, and exception posture remain explicit in this plan.
## Test Governance Check
- **Test purpose / classification by changed surface**: `Unit` for the bounded lifecycle overlay and behavior matrix; `Feature` for system-plane mutation, onboarding activation gating, review-pack start blocking, and preserved suspended read-only consumption
- **Affected validation lanes**: `fast-feedback`, `confidence`
- **Why this lane mix is the narrowest sufficient proof**: the business risk is deterministic decision ordering plus existing Filament/Livewire and service entry points. Browser or heavy-governance coverage would add cost without proving additional current-release risk for this bounded overlay.
- **Narrowest proving command(s)**:
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Entitlements/WorkspaceCommercialLifecycleResolverTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/ViewWorkspaceEntitlementsTest.php tests/Feature/System/Spec113/AuthorizationSemanticsTest.php tests/Feature/Onboarding/ManagedTenantOnboardingEntitlementTest.php tests/Feature/Onboarding/OnboardingRbacSemanticsTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackEntitlementEnforcementTest.php tests/Feature/ReviewPack/ReviewPackGenerationTest.php tests/Feature/ReviewPack/ReviewPackDownloadTest.php tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php tests/Feature/Evidence/EvidenceSnapshotResourceTest.php`
- **Fixture / helper / factory / seed / context cost risks**: limited to workspace, platform user, workspace member, onboarding draft, tenant, existing review pack, tenant review, and evidence snapshot fixtures
- **Expensive defaults or shared helper growth introduced?**: no - implementation should reuse existing factories and opt-in tenant/review/evidence helpers only
- **Heavy-family additions, promotions, or visibility changes**: none
- **Surface-class relief / special coverage rule**: standard-native relief for system detail and onboarding; monitoring-state-page proof for no new run creation; shared-detail-family proof for preserved view/download access while suspended
- **Closing validation and reviewer handoff**: rerun the exact targeted commands above, verify 404 vs 403 vs business-state outcomes separately, verify system detail source labels remain consistent, verify blocked review-pack starts create no new `ReviewPack` or `OperationRun` and emit no queued or terminal notification, verify already queued or running review-pack runs continue unaffected after later suspension, and verify suspended workspaces still allow authorized review/evidence/download access
- **Budget / baseline / trend follow-up**: none expected beyond ordinary feature-local growth
- **Review-stop questions**: does the state vocabulary stay narrow enough, does the system/admin split remain intact, does suspended read-only coverage avoid broad mutation-sweep scope, and does the blocked-decision transport avoid a second exception framework
- **Escalation path**: document-in-feature if only payload wording or helper reuse needs adjustment; follow-up-spec only if the overlay starts pulling unrelated mutable surfaces into suspension logic
- **Active feature PR close-out entry**: Guardrail
- **Why no dedicated follow-up spec is needed**: the testing cost stays local to one overlay resolver and a small set of existing pages/services/resources; no new browser or heavy-governance harness is justified
## Project Structure
### Documentation (this feature)
```text
specs/251-commercial-entitlements-billing-state/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── workspace-commercial-lifecycle-overlay.logical.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md # Created later by /speckit.tasks, not by this plan step
```
### Source Code (repository root)
```text
apps/platform/
├── app/
│ ├── Exceptions/Entitlements/WorkspaceEntitlementBlockedException.php
│ ├── Filament/
│ │ ├── Pages/
│ │ │ ├── Reviews/CustomerReviewWorkspace.php
│ │ │ ├── Reviews/ReviewRegister.php
│ │ │ ├── Settings/WorkspaceSettings.php
│ │ │ └── Workspaces/ManagedTenantOnboardingWizard.php
│ │ ├── Resources/
│ │ │ ├── EvidenceSnapshotResource.php
│ │ │ ├── EvidenceSnapshotResource/Pages/ViewEvidenceSnapshot.php
│ │ │ ├── ReviewPackResource.php
│ │ │ ├── ReviewPackResource/Pages/ViewReviewPack.php
│ │ │ ├── TenantReviewResource.php
│ │ │ └── TenantReviewResource/Pages/ViewTenantReview.php
│ │ ├── System/Pages/Directory/ViewWorkspace.php
│ │ └── Widgets/Tenant/TenantReviewPackCard.php
│ ├── Models/WorkspaceSetting.php
│ ├── Services/
│ │ ├── Entitlements/WorkspaceCommercialLifecycleResolver.php # likely new bounded overlay service
│ │ ├── Entitlements/WorkspaceEntitlementResolver.php
│ │ ├── ReviewPackService.php
│ │ └── Settings/
│ │ ├── SettingsResolver.php
│ │ └── SettingsWriter.php
│ ├── Support/
│ │ ├── Auth/Capabilities.php
│ │ ├── Auth/PlatformCapabilities.php
│ │ └── Settings/SettingsRegistry.php
├── resources/views/filament/system/pages/directory/view-workspace.blade.php
└── tests/
├── Feature/
└── Unit/
```
**Structure Decision**: Single Laravel/Filament application inside `apps/platform`, with one new bounded lifecycle overlay service and changes limited to existing settings persistence, system detail, onboarding, review-pack, and read-only review/evidence/download surfaces plus focused Pest coverage.
## Likely Implementation Surfaces
- `app/Support/Settings/SettingsRegistry.php` to register lifecycle-setting definitions and validation using the existing workspace settings infrastructure
- `app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php` as the new bounded overlay, with `WorkspaceEntitlementResolver.php` remaining the canonical substrate provider
- `app/Support/Auth/PlatformCapabilities.php` and related platform authorization helpers for one dedicated commercial-lifecycle management capability
- `app/Filament/System/Pages/Directory/ViewWorkspace.php` and `resources/views/filament/system/pages/directory/view-workspace.blade.php` for read-only summary plus the confirmation-protected state-change action
- `app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php` for contextual lifecycle messaging and activation blocking before tenant mutation
- `app/Services/ReviewPackService.php`, `app/Filament/Widgets/Tenant/TenantReviewPackCard.php`, `app/Filament/Pages/Reviews/ReviewRegister.php`, `app/Filament/Resources/TenantReviewResource.php`, `app/Filament/Resources/ReviewPackResource.php`, `app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php`, and `app/Filament/Resources/TenantReviewResource/Pages/ViewTenantReview.php` for shared start gating and tooltip/disabled-state reuse
- Existing read-only consumption surfaces `CustomerReviewWorkspace.php`, `ViewTenantReview.php`, `ViewReviewPack.php`, `ViewEvidenceSnapshot.php`, and the current review-pack download path to prove suspended history/evidence access remains available under existing RBAC
- Focused unit and feature tests under `tests/Unit/Entitlements`, `tests/Feature/System/Directory`, `tests/Feature/Onboarding`, `tests/Feature/ReviewPack`, `tests/Feature/Reviews`, and `tests/Feature/Evidence`
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| One bounded `WorkspaceCommercialLifecycleResolver` | Two real gated behavior families plus preserved read-only consumption need one shared workspace-wide decision layered above existing entitlements | Page-local conditionals in onboarding, review-pack resources/widgets, and system detail would drift immediately and undermine business-state consistency |
| Four-state commercial lifecycle vocabulary | Platform operators need one auditable commercial posture that distinguishes trial, grace, active paid, and suspended/read-only on the single system decision surface | Three unlabeled booleans or ad hoc flags would either collapse grace into suspension or lose the explicit platform-side lifecycle state needed for support and audit |
## Proportionality Review
- **Current operator problem**: The repo can already answer per-key entitlement questions, but it cannot say in one place whether a workspace is currently trialing, in grace, fully active paid, or suspended/read-only, nor can it explain why onboarding and review-pack starts are blocked while history remains readable.
- **Existing structure is insufficient because**: `WorkspaceEntitlementResolver` and current workspace settings expose substrate truth only. They do not provide one workspace-wide lifecycle posture, one system-owned mutation path, or one action-family outcome that distinguishes lifecycle blocks from entitlement blocks and authorization failures.
- **Narrowest correct implementation**: Keep persistence inside existing `workspace_settings`, add only one four-state lifecycle family plus rationale, derive action-family outcomes in one bounded overlay service, mutate it from one system detail page, and apply it only to onboarding activation, review-pack starts, and preserved read-only history/evidence/download semantics.
- **Ownership cost created**: One new state vocabulary, one overlay service, one platform capability, cross-surface copy discipline, and focused lifecycle/read-only tests.
- **Alternative intentionally rejected**: A billing/subscription engine, customer-account model, payment-provider seam, or many local page booleans was rejected because the current release only needs a single workspace commercial overlay on top of the already-real entitlement substrate.
- **Release truth**: current-release truth. The four-state vocabulary is justified now because platform operators already need to set and audit those named postures, even though only `grace` and `suspended_read_only` introduce new blocked outcomes for the two in-scope action families in this slice.
## Phase 0 — Research (output: `research.md`)
See: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/research.md`
Goals:
- Confirm the narrowest reuse of the existing `entitlements` settings domain and audit path for lifecycle state and rationale.
- Confirm that one bounded overlay service can compose `WorkspaceEntitlementResolver` without creating a second commercial framework.
- Confirm that lifecycle mutation remains platform-only on the existing system workspace detail page and does not leak into `/admin` self-service.
- Confirm that review-pack start blocking happens before `ReviewPack` or `OperationRun` creation and can reuse the current blocked-decision transport.
- Confirm that suspended read-only preservation remains bounded to existing review, evidence, and generated-pack consumption surfaces instead of becoming a broad product-wide suspension sweep.
## Phase 1 — Design & Contracts (outputs: `data-model.md`, `contracts/`, `quickstart.md`)
See:
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/data-model.md`
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/contracts/workspace-commercial-lifecycle-overlay.logical.openapi.yaml`
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/251-commercial-entitlements-billing-state/quickstart.md`
Design focus:
- Persist `commercial_lifecycle_state` plus rationale through the existing `entitlements` settings domain instead of adding a new table or billing domain.
- Keep the overlay inside `App\Services\Entitlements` and let it compose `WorkspaceEntitlementResolver` rather than replacing it.
- Extend the existing system workspace detail page with a read-only lifecycle summary and one confirmation-protected `Change commercial state` action, while leaving `WorkspaceSettings` as substrate truth rather than a second mutation plane.
- Gate `ManagedTenantOnboardingWizard` completion from the central lifecycle decision after underlying entitlement truth is known.
- Gate review-pack `Generate pack`, `Regenerate`, and `Export executive pack` starts through `ReviewPackService` and current action surfaces, stopping before any `ReviewPack` or `OperationRun` write when the lifecycle blocks the action.
- Preserve `CustomerReviewWorkspace`, review detail, evidence detail, review-pack detail, and pack download access under current RBAC while suspended, and keep any broader mutable-surface suspension work explicitly out of scope.
## Phase 1 — Agent Context Update
After Phase 1 artifacts are generated, update Copilot context from the completed plan:
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/.specify/scripts/bash/update-agent-context.sh copilot`
## Phase 2 — Implementation Outline (tasks created later by `/speckit.tasks`)
- Register lifecycle state and rationale setting definitions under the existing `entitlements` settings domain and wire them into the current workspace-setting audit path.
- Add one bounded `WorkspaceCommercialLifecycleResolver` that composes underlying entitlement decisions and yields action-family outcomes plus suspended read-only allowances.
- Add one dedicated platform capability for commercial lifecycle management and enforce it on the system detail mutation action only.
- Extend `ViewWorkspace` plus its Blade view with current lifecycle state, affected behavior summary, and a confirmation-protected `Change commercial state` action.
- Gate onboarding completion in `ManagedTenantOnboardingWizard` using the shared lifecycle decision while preserving existing tenant operability checks and 404/403 semantics.
- Gate review-pack start surfaces and `ReviewPackService` using the shared lifecycle decision, preserving current queued-start UX when allowed and reusing the existing blocked-decision transport when blocked.
- Prove suspended read-only continuation by asserting existing review/evidence/download surfaces remain available under current RBAC while no new onboarding activation or review-pack run can start.
- Add focused Sail/Pest unit and feature coverage only.
## Constitution Check (Post-Design)
Re-check result: PASS. The design keeps Filament v5 + Livewire v4 compliance intact, leaves provider registration unchanged in `bootstrap/providers.php`, introduces no new globally searchable resource, keeps asset strategy unchanged, preserves strict `/admin` vs `/system` separation, layers one bounded lifecycle resolver above the existing entitlement substrate, and blocks review-pack starts before `OperationRun` creation rather than forking shared run UX.
## Planning Readiness
- Outcome: keep
- No unresolved clarification markers remain in the plan-phase artifacts.
- No application implementation is included in this planning step.
- The next repo-native step is `/speckit.tasks` for an implementation task breakdown, not code changes.
## Implementation Close-Out
- **Workflow outcome**: keep.
- **Implementation result**: one bounded commercial lifecycle overlay was implemented through existing workspace settings, one system-plane `Change commercial state` action, onboarding activation gating, review-pack start allow/warn/block semantics, and preserved suspended read-only review/evidence/download access.
- **Blocked-decision transport**: document-in-feature. The existing `WorkspaceEntitlementBlockedException` transport remains sufficient for review-pack blocked starts; no second business-state exception family was introduced.
- **Preserved read-only scope**: document-in-feature. Suspension stays bounded to onboarding activation and new review-pack starts in this spec; broader mutable-surface suspension remains out of scope.
- **Browser smoke path**: `/system/login` as `operator@tenantpilot.io`, `/system/directory/workspaces/1`, open `Change commercial state`, set `Trial` with rationale, confirm, observe updated lifecycle summary and notification follow-up, then restore `Active paid`.
- **Browser smoke result**: pass after fixing `WorkspaceResolver` to accept Livewire serialized workspace route parameters; the follow-up notification update no longer emits console errors or 404/419 markers.
- **Lane results**: targeted unit/support/system/onboarding/review-pack/read-only Pest lanes passed; dirty-only Pint passed; `git diff --check` passed.