Some checks failed
Main Confidence / confidence (push) Failing after 1m45s
## 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
333 lines
50 KiB
Markdown
333 lines
50 KiB
Markdown
# Feature Specification: Commercial Entitlements and Billing-State Maturity
|
|
|
|
**Feature Branch**: `251-commercial-entitlements-billing-state`
|
|
**Created**: 2026-04-28
|
|
**Status**: Draft
|
|
**Input**: User description: "Commercial lifecycle follow-up on top of the already-real Spec 247 entitlement substrate, with one central workspace lifecycle resolution, bounded lifecycle states, two real gated behaviors, explicit read-only suspension semantics, and audited state changes without expanding into a billing engine."
|
|
|
|
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
|
|
|
|
- **Problem**: TenantPilot already resolves plan-profile entitlements for a workspace, but it still lacks one central commercial lifecycle state that explains whether the workspace is in trial, grace, normal paid use, or suspended/read-only posture.
|
|
- **Today's failure**: Operators can hit blocked onboarding or reporting actions without one consistent business-state explanation, and a future suspension or grace posture would otherwise be implemented as scattered local conditionals or mistaken as RBAC denial.
|
|
- **User-visible improvement**: Platform operators can set one auditable workspace commercial lifecycle state, and tenant/workspace operators then see a truthful allow, warn, or read-only message directly at onboarding and review-pack action surfaces without losing safe access to existing history and evidence.
|
|
- **Smallest enterprise-capable version**: Add one platform-managed workspace commercial lifecycle overlay on top of the existing entitlement substrate, resolve four bounded lifecycle states, gate managed-tenant onboarding activation plus review-pack start actions from that central decision, and preserve safe read-only access to existing review/evidence history while suspended.
|
|
- **Explicit non-goals**: No payment providers, invoicing, taxes, accounting, checkout, public pricing, website work, customer-account modeling, subscription engine, automated renewal reminders, broad entitlement spread, or customer self-service lifecycle management.
|
|
- **Permanent complexity imported**: One bounded lifecycle state family, one small central lifecycle resolution layer on top of the existing entitlement substrate, one platform-side state change surface with audit, and focused unit plus feature coverage.
|
|
- **Why now**: This directly extends real repo truth from Spec 247 and `WorkspaceEntitlementResolver`, so it is implementation-ready as a narrow follow-up. Localization remains a broader missing foundation, and external support-desk handoff still lacks a concrete external target.
|
|
- **Why not local**: The same commercial posture must drive system support visibility, onboarding activation, review-pack generation, and suspended read-only access rules. Local page checks would drift immediately and recreate the current manual explanation problem.
|
|
- **Approval class**: Core Enterprise
|
|
- **Red flags triggered**: New state axis, foundation-sounding commercial theme, and multi-surface touchpoint. Defense: this slice is limited to one overlay on top of an existing resolver, one platform mutation surface, two already-real gated behaviors, and explicit read-only preservation instead of a broader billing platform.
|
|
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12**
|
|
- **Decision**: approve
|
|
|
|
## Spec Scope Fields *(mandatory)*
|
|
|
|
- **Scope**: workspace
|
|
- **Primary Routes**:
|
|
- `/system/directory/workspaces/{workspace}` for platform-side inspection and lifecycle state change
|
|
- `/admin/onboarding/{onboardingDraft}` for managed-tenant onboarding activation
|
|
- `/admin/reviews` plus existing tenant review detail, tenant dashboard, and review-pack registry/detail surfaces for `Generate pack`, `Regenerate`, and `Export executive pack`
|
|
- existing read-only review, evidence, and generated-pack consumption surfaces that must remain available while suspended/read-only
|
|
- **Data Ownership**: Commercial lifecycle state remains workspace-owned truth and is stored through the existing workspace settings infrastructure. Existing plan profiles and entitlement decisions from Spec 247 remain the underlying workspace-owned substrate. Tenant-owned review packs, evidence snapshots, review history, and onboarding records stay tenant-owned and are not remodeled by this slice.
|
|
- **RBAC**: Platform users with directory visibility plus a dedicated commercial lifecycle management capability may inspect and change state on `/system`. Workspace or tenant members keep their existing onboarding and review-pack capabilities on `/admin`, but lifecycle state is a business-state overlay rather than a self-service setting. Non-members and wrong-plane actors continue to receive 404. Members missing capability continue to receive 403. Members with the required capability but blocked by lifecycle state receive a truthful business-state block instead of an authorization failure.
|
|
|
|
For canonical-view specs, the spec MUST define:
|
|
|
|
- **Default filter behavior when tenant-context is active**: N/A - this slice does not introduce a new tenantless collection or cross-tenant list.
|
|
- **Explicit entitlement checks preventing cross-tenant leakage**: Existing workspace and tenant isolation remain authoritative. The lifecycle overlay never reveals tenant-owned history or artifacts outside the already-authorized workspace and tenant scope.
|
|
|
|
## 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, action gating, system detail controls, operation-start blocking, evidence/report viewers
|
|
- **Systems touched**: existing workspace settings persistence, existing workspace entitlement resolution, system workspace detail view, onboarding activation gate, review-pack generation entry family, audit logging, and existing read-only review/evidence/download surfaces
|
|
- **Existing pattern(s) to extend**: existing workspace entitlement resolver and summary pattern, existing workspace-setting audit path, existing review-pack start UX, existing onboarding activation gate, and existing system detail summary surfaces
|
|
- **Shared contract / presenter / builder / renderer to reuse**: the current workspace entitlement resolution path and its audit-backed settings persistence remain the canonical substrate; this slice adds one bounded commercial lifecycle decision layer on top rather than a second parallel commercial framework
|
|
- **Why the existing shared path is sufficient or insufficient**: The current entitlement substrate is already sufficient for plan defaults, overrides, and per-key allow/block decisions. It is insufficient for one workspace-wide lifecycle posture that can say "expansion frozen" or "read-only suspended" consistently across multiple surfaces.
|
|
- **Allowed deviation and why**: none. No surface may invent local lifecycle labels, local business-state copy, or page-specific suspension rules.
|
|
- **Consistency impact**: State labels, source labels, block reasons, and read-only explanations must mean the same thing on the system workspace page, onboarding completion step, review-pack start actions, and preserved read-only review/evidence surfaces.
|
|
- **Review focus**: Reviewers must verify that all in-scope surfaces consume one shared lifecycle decision, that lifecycle overlay semantics do not expand access beyond current entitlements, and that suspended read-only messaging does not drift across surfaces.
|
|
|
|
## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)*
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: yes
|
|
- **Shared OperationRun UX contract/layer reused**: existing review-pack queued-start, `Open operation`, and canonical run-link behavior remain unchanged when lifecycle state allows the start action
|
|
- **Delegated start/completion UX behaviors**: queued toast, `Open operation` link, dedupe behavior, and terminal lifecycle feedback stay on the existing review-pack path when allowed. A lifecycle block stops earlier and produces no queued-start feedback because no run is created.
|
|
- **Local surface-owned behavior that remains**: local surfaces only render lifecycle state, blocked reason, and the safe next step. They do not replace the existing review-pack run UX.
|
|
- **Queued DB-notification policy**: unchanged
|
|
- **Terminal notification path**: central lifecycle mechanism for existing review-pack runs only
|
|
- **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/platform boundary is changed. Commercial lifecycle state is platform-core workspace truth and must remain provider-neutral even when it gates provider-backed review-pack workflows.
|
|
|
|
## 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 |
|
|
|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | yes | Native Filament system detail page | detail summary, header actions, status messaging | detail page, header action, summary card | no | Single platform mutation surface only |
|
|
| Managed tenant onboarding activation gate | yes | Native Filament wizard | action gating, helper text, business-state callout | completion step, confirmation action | no | Reuses the existing activation step |
|
|
| Review-pack generation entry family | yes | Native Filament widget/resource/page actions | operation-start gating, helper text, state badges | widget action, detail action, list/header action | no | Only `Generate pack`, `Regenerate`, and `Export executive pack` are in scope |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | yes | Native Filament detail and download surfaces | evidence/report viewers, detail messaging | detail page, download action, read-only summary | no | No new routes; the slice only preserves safe read-only availability during suspension |
|
|
|
|
## 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 |
|
|
|---|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | Primary Decision Surface | Platform operator decides whether a workspace should remain trial, move into grace, return to active paid, or become suspended/read-only | Current state, rationale, affected action families, and last changed attribution | Existing entitlement summary and related workspace diagnostics | Primary because this is the one place where commercial posture is intentionally changed | Follows platform support/commercial workflow rather than customer admin navigation | Prevents founders or support staff from reconstructing state from ad hoc notes and blocked actions |
|
|
| Managed tenant onboarding activation gate | Primary Decision Surface | Workspace operator decides whether the current tenant may be activated now | Lifecycle state, whether activation is allowed, and the business-state reason when blocked | Existing onboarding verification and readiness diagnostics remain secondary | Primary because onboarding completion is the actual high-impact mutation point | Keeps the commercial decision inside the activation workflow | Removes the need to ask support whether a block is about permissions or billing state |
|
|
| Review-pack generation entry family | Secondary Context Surface | Reporting operator decides whether to start, retry, or export a review pack from the current tenant or review context | Lifecycle state, whether the start action is blocked, and the safe fallback when suspended/read-only | Existing run state, artifact truth, and review history remain secondary | Not primary because the family exists to continue reporting/review workflows, not to manage commercial posture itself | Stays inside existing report-generation workflows | Avoids a second support lookup just to understand why generation is blocked |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | Tertiary Evidence / Diagnostics Surface | Customer-safe or operator read-only consumer verifies existing history while the workspace is suspended/read-only | Existing history, evidence, and generated pack availability plus a calm read-only explanation | Raw provider or support diagnostics remain secondary and capability-gated | Not primary because these surfaces answer "what history is still safe to read" rather than "what state should change" | Preserves evidence-first review consumption instead of forcing new export workarounds | Prevents suspended workspaces from looking completely unavailable when history should still be readable |
|
|
|
|
## 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 |
|
|
|---|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | support-platform, operator-platform | Current lifecycle state, rationale, last changed attribution, and affected behavior summary | Existing workspace entitlement summary and tenant counts | No raw settings payload or internal debug data by default | `Change commercial state` | Raw settings rows and internal debugging remain hidden | The page states the lifecycle blocker once and reuses the same labels later rather than restating them differently |
|
|
| Managed tenant onboarding activation gate | operator-MSP | Activation allowed/blocked, current lifecycle state, and why the block is business-state rather than permission-state | Existing readiness and verification diagnostics already on the wizard | No support/raw payloads on the default path | `Complete onboarding` when allowed, otherwise `Request commercial review` | Deeper commercial diagnostics stay off the onboarding surface | The step shows one lifecycle explanation and does not restate the whole workspace commercial profile |
|
|
| Review-pack generation entry family | operator-MSP | Start action availability, current lifecycle state, and the safe fallback when generation is blocked | Existing run state and artifact status | No raw support diagnostics on start surfaces | `Generate pack`, `Regenerate`, or `Export executive pack` when allowed; otherwise `View current pack` | System-only lifecycle controls stay off these surfaces | One shared lifecycle reason is reused across all in-scope start actions |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | customer-read-only, operator-MSP | What history remains available and why the workspace is read-only rather than fully inaccessible | Existing review history and artifact provenance | Support/raw details remain collapsed or gated | `View current review` or `Download current pack` | Any mutation affordance stays blocked in suspended/read-only posture | The read-only explanation appears once and later sections add evidence rather than repeating the same blocker |
|
|
|
|
## 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 |
|
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | System / Detail / Diagnostics | Read-only detail with bounded mutation action | Change the workspace lifecycle state | Dedicated workspace detail page | forbidden | Existing admin-workspace and related navigation stay secondary | `Change commercial state` contains the high-risk `Suspended / read-only` path with explicit confirmation | `/system/directory/workspaces` | `/system/directory/workspaces/{workspace}` | Platform workspace identity plus current lifecycle state | Commercial lifecycle | Current state, rationale, and affected behaviors | Acceptable detail-surface exception because mutation stays bounded to one header action on the detail page |
|
|
| Managed tenant onboarding activation gate | Workflow / Guided action entry | Onboarding completion step | Complete onboarding or stop because commercial state blocks expansion | In-page completion step | forbidden | Existing back-navigation and tenant links stay secondary | Existing `Cancel draft` and `Delete draft` remain the only destructive actions | `/admin/onboarding` | `/admin/onboarding/{onboardingDraft}` | Workspace context plus current tenant | Onboarding commercial state | Activation allowed or blocked and why | Existing wizard exception remains valid |
|
|
| Review-pack generation entry family | Contextual action family | Widget/resource/page start actions | Start, retry, or export a review pack when allowed | Explicit action on the current tenant or review context | mixed - existing registry rows may still open detail, but start actions remain explicit | Existing `View` and `Download` stay secondary and outside the blocked start gate | Existing destructive actions remain out of scope and keep current placement | `/admin/reviews` plus existing tenant review-pack collection surfaces | Existing tenant review detail and review-pack detail surfaces | Active workspace, active tenant, review or pack context | Review-pack generation | Start allowed or blocked, and the safe read-only fallback | Grouped-action family exception is documented here so all start actions share one gate |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | Detail / Report viewer / Download | Read-only detail and artifact consumption | View history or download an already-generated pack | Existing review or pack detail page | allowed where the current collection already opens detail | Supporting navigation remains secondary | none | Existing review and review-pack collections | Existing review, evidence, and review-pack detail routes | Active workspace, active tenant, current artifact or review | Review history / Generated pack | Safe read-only availability during suspension | No new surface type introduced |
|
|
|
|
## 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 |
|
|
|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | Platform commercial or support operator | Decide the current commercial posture of a workspace | System detail page | What lifecycle state should this workspace be in now? | State, rationale, affected behaviors, and last changed attribution | Existing entitlement summary and workspace diagnostics | commercial lifecycle, entitlement substrate | TenantPilot only | Change commercial state | Set suspended/read-only |
|
|
| Managed tenant onboarding activation gate | Workspace owner or manager completing onboarding | Decide whether the current tenant can be activated now | Guided workflow step | Can I activate this tenant under the current commercial posture? | Current lifecycle state, whether activation is allowed, and the block reason when not | Existing verification and bootstrap detail | onboarding readiness, commercial lifecycle, entitlement availability | TenantPilot only for activation state | Complete onboarding | Cancel draft, Delete draft |
|
|
| Review-pack generation entry family | Workspace manager or reporting operator | Decide whether a new pack run may start now | Contextual start-action family | Can I start, retry, or export a pack from this context? | Current lifecycle state, whether the start action is blocked, and the safe fallback | Existing run state, review status, and artifact truth | commercial lifecycle, entitlement availability, run state, artifact status | TenantPilot only until the existing run starts | Generate pack, Regenerate, Export executive pack, View current pack | Existing destructive actions remain unchanged and out of scope |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | Customer-safe reader or workspace operator | Consume already-generated history safely while the workspace is read-only | Read-only detail and download surfaces | What history can I still read or download safely? | Existing review/evidence/download truth plus a calm read-only explanation | Raw provider diagnostics and support-only detail | commercial lifecycle, artifact availability | none | View current review, Download current pack | none |
|
|
|
|
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
|
|
|
- **New source of truth?**: yes - one workspace-owned commercial lifecycle state becomes current-release business truth, but it is stored through existing workspace settings rather than a new table
|
|
- **New persisted entity/table/artifact?**: no
|
|
- **New abstraction?**: yes - one bounded lifecycle resolution layer on top of the existing entitlement substrate
|
|
- **New enum/state/reason family?**: yes - the four-state lifecycle family (`trial`, `grace`, `active_paid`, `suspended_read_only`)
|
|
- **New cross-domain UI framework/taxonomy?**: no
|
|
- **Current operator problem**: Support and operators cannot truthfully explain whether a workspace is in a normal commercial state, an expansion freeze, or a read-only suspension without reconstructing the answer from scattered surface behavior.
|
|
- **Existing structure is insufficient because**: Spec 247 gives per-key entitlement truth, but it does not provide one workspace-wide lifecycle posture that can say "activation blocked but reading is still safe" or "new runs blocked while history remains available."
|
|
- **Narrowest correct implementation**: Keep persistence inside the existing workspace settings infrastructure, add one small state family and one shared resolution layer, mutate it from one system detail page, and apply it only to two already-real start behaviors plus suspended read-only preservation.
|
|
- **Ownership cost**: One state vocabulary, one additional decision layer, cross-surface copy discipline, and focused tests for state transitions plus allowed/blocked behavior.
|
|
- **Alternative intentionally rejected**: A new subscription/customer-account model or many per-surface lifecycle flags was rejected because the repo has no current billing domain and the smallest safe slice only needs one central commercial posture.
|
|
- **Release truth**: current-release truth with later follow-up candidates for automation, billing integration, and broader lifecycle-aware entitlement spread
|
|
|
|
### Compatibility posture
|
|
|
|
This feature assumes a pre-production environment.
|
|
|
|
Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec.
|
|
|
|
Canonical replacement is preferred over preservation.
|
|
|
|
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
|
|
|
- **Test purpose / classification**: Unit, Feature
|
|
- **Validation lane(s)**: fast-feedback, confidence
|
|
- **Why this classification and these lanes are sufficient**: Unit coverage proves default state resolution, state precedence over existing entitlements, and state-to-behavior mapping. Focused feature coverage proves platform mutation, audit logging, onboarding blocks, review-pack start blocks, and preserved read-only access without expanding into browser or heavy-governance lanes.
|
|
- **New or expanded test families**: one bounded lifecycle resolver unit family plus focused extensions to the existing system detail, onboarding, review-pack, and preserved read-only feature families
|
|
- **Fixture / helper cost impact**: Add only workspace, platform user, workspace member, onboarding draft, active tenant count, existing review pack, and existing evidence/history fixtures required to prove the state consequences. Avoid payment-provider mocks, browser harnesses, or new heavy support fixtures.
|
|
- **Heavy-family visibility / justification**: none
|
|
- **Special surface test profile**: standard-native-filament, shared-detail-family, monitoring-state-page
|
|
- **Standard-native relief or required special coverage**: Standard Filament feature coverage is sufficient for the system detail mutation surface and onboarding gate. Review-pack gating still needs monitoring-state assertions to prove blocked starts create no run, while suspended read-only preservation needs one detail/download assertion on existing artifact surfaces.
|
|
- **Reviewer handoff**: Reviewers must confirm that lifecycle blocks remain distinct from 404 and 403 outcomes, that source labels stay consistent on the system detail surface, that `grace` and `suspended_read_only` do not collapse into one behavior, that blocked review-pack starts create no queued or terminal notification, that already queued or running review-pack runs remain unaffected by later suspension, and that existing read-only history/download access remains available under current RBAC.
|
|
- **Budget / baseline / trend impact**: low feature-local increase only
|
|
- **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/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`
|
|
|
|
## Scope Boundaries *(required for this slice)*
|
|
|
|
### In Scope
|
|
|
|
- One central workspace commercial lifecycle overlay with exactly four states: `trial`, `grace`, `active_paid`, and `suspended_read_only`
|
|
- One platform-managed lifecycle change path with rationale and audit, persisted through the existing workspace settings infrastructure
|
|
- One shared lifecycle resolution path layered on top of the existing Spec 247 entitlement substrate
|
|
- Lifecycle gating of managed-tenant onboarding activation
|
|
- Lifecycle gating of review-pack `Generate pack`, `Regenerate`, and `Export executive pack` entry points
|
|
- Suspended/read-only preservation of authorized review history, evidence, and already-generated review-pack consumption
|
|
- Explicit business-state messaging that distinguishes lifecycle blocks from RBAC failures
|
|
|
|
### Non-Goals
|
|
|
|
- Payment providers, invoices, taxes, accounting, checkout, public pricing, website work, and payment failure workflows
|
|
- New customer-account, subscription, contract, or offer models
|
|
- Automated timers, expiries, reminders, or scheduled state transitions
|
|
- Customer self-service state changes from the workspace admin plane
|
|
- Broad entitlement expansion across seats, exports, retention, support SLAs, or unrelated feature flags
|
|
- Broad suspension logic across every mutable surface in the product
|
|
- A second commercial control plane outside the existing system workspace detail flow
|
|
|
|
## Assumptions
|
|
|
|
- Spec 247 remains the canonical entitlement substrate. Commercial lifecycle state is an overlay that can warn or restrict, not a replacement for plan-profile and per-key entitlement truth.
|
|
- Commercial lifecycle mutation is platform-managed in this slice. Workspace and tenant operators may observe the resulting state where it matters, but they do not change it themselves.
|
|
- If no explicit lifecycle state has been set for a workspace, the system resolves to `active_paid` so that current repo behavior stays unchanged until a platform operator intentionally selects a different state.
|
|
- `grace` is intentionally narrower than `suspended_read_only`: it freezes new managed-tenant activation but continues to allow existing review-pack start behavior when the underlying entitlement substrate still allows it.
|
|
- `suspended_read_only` preserves existing review/evidence/download access under current RBAC and redaction rules, but blocks new onboarding activation and new review-pack start actions.
|
|
|
|
## Risks
|
|
|
|
- `grace` and `suspended_read_only` can drift into near-duplicates if blocked-action copy and tests do not keep their consequences distinct.
|
|
- A later customer-account or billing source could require revisiting how manual lifecycle transitions are sourced, even though that broader domain is intentionally out of scope here.
|
|
- A future admin-plane commercial settings surface could confuse ownership if it appears without preserving platform-only mutation authority.
|
|
- Mid-flight review-pack runs created before a workspace becomes suspended could create confusion if the product does not clearly state that this slice only blocks future starts.
|
|
|
|
## Deferred Adjacent Candidates
|
|
|
|
- **Localization v1** remains a separate, broader foundation candidate because it requires cross-product locale resolution and copy governance beyond this bounded commercial lifecycle slice.
|
|
- **External Support Desk / PSA Handoff** remains a separate candidate because repo docs still do not define one concrete external desk target to hand off into.
|
|
- Broader billing lifecycle automation, reminders, and external billing-source integration stay deferred until a real account and payment domain exists in repo truth.
|
|
|
|
## User Scenarios & Testing *(mandatory)*
|
|
|
|
### User Story 1 - Set one workspace commercial lifecycle state centrally (Priority: P1)
|
|
|
|
As a platform commercial or support operator, I want to set a workspace's current commercial lifecycle state once so downstream product behavior follows one audited source of truth instead of local exceptions.
|
|
|
|
**Why this priority**: Without one central lifecycle state, every later gate or support explanation would duplicate commercial truth and drift away from the already-real entitlement substrate.
|
|
|
|
**Independent Test**: Open the existing system workspace detail surface, change the lifecycle state with rationale, and verify that the new state is visible there and auditable without touching onboarding or reporting flows.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a workspace has no explicit commercial lifecycle state, **When** an authorized platform operator sets it to `trial` with rationale, **Then** the workspace resolves to `trial`, the change is auditable, and the system detail surface shows the new state and rationale.
|
|
2. **Given** a workspace is currently in `grace`, **When** an authorized platform operator changes it to `suspended_read_only`, **Then** the previous state is replaced, the new state is auditable, and later gated surfaces consume the new state.
|
|
3. **Given** a workspace is in `suspended_read_only`, **When** an authorized platform operator returns it to `active_paid`, **Then** future gated actions again use the normal underlying entitlement substrate instead of the suspended overlay.
|
|
|
|
---
|
|
|
|
### User Story 2 - Truthfully block tenant activation when lifecycle state freezes expansion (Priority: P1)
|
|
|
|
As an authorized workspace operator, I want the onboarding completion step to tell me whether the tenant may be activated under the current commercial lifecycle state so I can distinguish business-state blocking from permissions or onboarding readiness problems.
|
|
|
|
**Why this priority**: Managed-tenant activation is the highest-risk first-slice mutation and the clearest place where a grace or suspended posture must stop expansion without ambiguity.
|
|
|
|
**Independent Test**: Seed workspaces in `trial`, `active_paid`, `grace`, and `suspended_read_only`, open the existing onboarding completion step, and verify that the same action becomes allowed or blocked with the correct business-state explanation before any activation mutation happens.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a workspace is `trial` or `active_paid` and the existing entitlement substrate allows activation, **When** an authorized operator reaches the onboarding completion step, **Then** the step allows completion and no lifecycle block is shown.
|
|
2. **Given** a workspace is in `grace`, **When** the same operator reaches the completion step, **Then** the action remains visible but blocked with a business-state explanation that new managed-tenant activation is frozen during grace.
|
|
3. **Given** a workspace is in `suspended_read_only`, **When** the operator reaches the same step, **Then** activation is blocked before any tenant mutation occurs and the step explains that the workspace is read-only rather than lacking permission.
|
|
|
|
---
|
|
|
|
### User Story 3 - Block new review-pack starts while preserving safe historical access (Priority: P2)
|
|
|
|
As a reporting operator or customer-safe reader, I want new review-pack start actions to obey the current commercial lifecycle state while already-generated history remains safely readable so suspension does not erase needed evidence.
|
|
|
|
**Why this priority**: Review-pack generation already exists on multiple real surfaces, and suspension is only trustworthy if it blocks new starts consistently while preserving safe access to history and already-generated evidence.
|
|
|
|
**Independent Test**: Seed a workspace with an existing generated pack and history, switch it to `suspended_read_only`, verify that `Generate pack`, `Regenerate`, and `Export executive pack` stop before any new run or artifact is created, that blocked starts emit no queued or terminal review-pack notification, that already queued or running review-pack work continues unchanged, and then confirm that authorized readers can still view or download the already-generated artifacts.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a workspace is `active_paid` or `trial` and the existing review-pack entitlement allows generation, **When** an authorized operator starts `Generate pack`, `Regenerate`, or `Export executive pack`, **Then** the current review-pack flow continues unchanged.
|
|
2. **Given** a workspace is in `grace` and the underlying review-pack entitlement allows generation, **When** an authorized operator starts the same action, **Then** the action remains allowed with a grace warning and without blocking the run.
|
|
3. **Given** a workspace is in `suspended_read_only`, **When** an authorized operator attempts `Generate pack`, `Regenerate`, or `Export executive pack`, **Then** the action is blocked before any new `ReviewPack` or `OperationRun` is created and no queued or terminal review-pack notification is emitted for the blocked attempt.
|
|
4. **Given** a review-pack run was already created while the workspace lifecycle state still allowed it, **When** the workspace later moves to `suspended_read_only`, **Then** the existing queued or running review-pack work may complete unchanged because this slice only blocks future start attempts.
|
|
5. **Given** a workspace is in `suspended_read_only` and already has generated review packs, evidence, or review history, **When** an authorized reader opens or downloads those existing artifacts, **Then** the existing read-only access continues under current RBAC and redaction rules.
|
|
|
|
### Edge Cases
|
|
|
|
- A workspace with no explicit lifecycle state must still resolve deterministically to `active_paid` so current Spec 247 behavior does not change accidentally.
|
|
- If the lifecycle state allows a behavior but the underlying entitlement substrate blocks it, the underlying entitlement block still applies and must remain distinguishable from lifecycle blocking.
|
|
- If the lifecycle state becomes `suspended_read_only` while a review-pack run is already queued or running, the existing run may complete; the new state only blocks future start attempts in this slice.
|
|
- A workspace member who lacks the relevant onboarding or review-pack capability must still receive 403 even when the workspace lifecycle state is otherwise permissive.
|
|
- A non-member or wrong-plane actor must not learn whether a workspace is in `grace` or `suspended_read_only`; those requests continue to resolve as 404.
|
|
- Suspended/read-only behavior must never revoke access to already-generated artifacts or review/evidence history that the actor is otherwise allowed to read.
|
|
|
|
## Requirements *(mandatory)*
|
|
|
|
**Constitution alignment (required):** This feature changes runtime behavior and writes workspace-owned commercial state, but it adds no Microsoft Graph calls, no new provider dispatch path, and no new queued workflow family. Lifecycle state changes use the existing workspace settings infrastructure and audit foundation. Existing review-pack `OperationRun` behavior is reused only when lifecycle state allows a start action.
|
|
|
|
**Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001):** The feature introduces one new business-state family because current-release operator workflows now need a workspace-wide commercial posture that per-key entitlement decisions cannot express alone. A narrower local-only approach would still scatter lifecycle semantics across onboarding and review-pack surfaces.
|
|
|
|
**Constitution alignment (XCUT-001):** All in-scope gated behaviors and preserved read-only surfaces must consume the same lifecycle decision. No local page is allowed to invent its own trial, grace, or suspension semantics.
|
|
|
|
**Constitution alignment (DECIDE-AUD-001 / OPSURF-001):** Blocked onboarding and blocked review-pack starts must show customer-safe or operator-safe default content first, with diagnostics and support-only detail remaining secondary. Suspended read-only surfaces must preserve one calm next step instead of turning history surfaces into error pages.
|
|
|
|
**Constitution alignment (PROV-001):** Commercial lifecycle state is platform-core workspace truth and must not import provider-specific vocabulary or billing-provider semantics.
|
|
|
|
**Constitution alignment (TEST-GOV-001):** Proof remains in focused unit plus feature lanes. New fixtures stay limited to workspace, platform operator, workspace member, onboarding draft, tenant count, and existing review-pack/evidence artifacts.
|
|
|
|
**Constitution alignment (OPS-UX):** This feature does not create a new run family. Existing review-pack generation keeps the current queued toast, operation link, and terminal notification path when lifecycle state allows it. Blocked lifecycle starts create no run and no run lifecycle feedback.
|
|
|
|
**Constitution alignment (OPS-UX-START-001):** Lifecycle gating sits before review-pack run creation and delegates all allowed queued-start UX to the existing shared review-pack path.
|
|
|
|
**Constitution alignment (RBAC-UX):** Two authorization planes are involved: platform `/system` for lifecycle mutation and tenant/admin `/admin` for contextual blocked-or-allowed behavior. Wrong-plane or non-member requests remain 404. Members missing capability remain 403. Lifecycle blocking is a product-state response for otherwise-authorized actors and must not masquerade as authorization failure.
|
|
|
|
**Constitution alignment (BADGE-001):** If lifecycle badges or state chips are rendered, their labels and visual semantics must come from one shared lifecycle vocabulary rather than page-local color mapping.
|
|
|
|
**Constitution alignment (UI-FIL-001):** The slice must extend existing native Filament detail, wizard, widget, resource, and download surfaces. No custom commercial panel or page-local status language is allowed.
|
|
|
|
**Constitution alignment (UI-NAMING-001):** Primary labels remain product-facing and specific: `Trial`, `Grace`, `Active paid`, `Suspended / read-only`, `Change commercial state`, `Complete onboarding`, `Generate pack`, `Regenerate`, and `Export executive pack`. Billing-provider or checkout terminology remains out of scope.
|
|
|
|
**Constitution alignment (DECIDE-001):** The system workspace detail page is the one primary commercial decision surface. Onboarding and review-pack surfaces remain contextual decision points that only show the commercial truth required for the immediate action. Existing history/evidence surfaces remain tertiary read-only contexts.
|
|
|
|
**Constitution alignment (UI-CONST-001 / UI-SURF-001 / ACTSURF-001 / UI-HARD-001 / UI-EX-001 / UI-REVIEW-001 / HDR-001):** The feature must preserve the existing system detail, guided onboarding, grouped review-pack actions, and read-only artifact consumption patterns. It may not create a second admin-plane commercial management surface or redundant inspect actions.
|
|
|
|
**Constitution alignment (ACTSURF-001 - action hierarchy):** Lifecycle mutation stays on the system workspace detail page. Onboarding completion remains the primary activation action. Review-pack start actions remain the primary reporting mutations where they already exist. View/download history remains secondary but available during suspension.
|
|
|
|
**Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001):** One thin lifecycle overlay is justified because direct reads from the existing entitlement substrate cannot express one workspace-wide read-only posture. Tests must prove business outcomes such as allowed, warned, blocked, and preserved-read behavior rather than badge rendering alone.
|
|
|
|
**Constitution alignment (Filament Action Surfaces):** The action-surface contract remains satisfied with the documented system detail exception for one bounded mutation action, the existing onboarding wizard exception, and the existing review-pack action family. No empty action groups or redundant view actions are introduced by this slice.
|
|
|
|
**Constitution alignment (UX-001 — Layout & Information Architecture):** The feature extends the existing system detail, onboarding, and review-pack surfaces with bounded state information only. It does not create a new commercial page shell or duplicate summary screen.
|
|
|
|
### Functional Requirements
|
|
|
|
- **FR-251-001 Central lifecycle state**: The system MUST resolve one commercial lifecycle state per workspace with exactly four values: `trial`, `grace`, `active_paid`, and `suspended_read_only`.
|
|
- **FR-251-002 Existing entitlement substrate remains canonical**: The system MUST layer lifecycle state on top of the existing Spec 247 entitlement substrate rather than replacing plan profiles, entitlement keys, or override logic.
|
|
- **FR-251-003 Deterministic default**: If no explicit lifecycle state has been stored for a workspace, the system MUST resolve to `active_paid` so existing behavior remains unchanged until an operator intentionally changes state.
|
|
- **FR-251-004 Workspace-owned persistence**: The system MUST store lifecycle state and rationale through the existing workspace settings infrastructure instead of introducing a new customer-account, subscription, or billing table.
|
|
- **FR-251-005 Platform-managed mutation**: Only authorized platform users MAY change or override lifecycle state in this slice, and the workspace or tenant admin plane MUST NOT become a self-service lifecycle control surface.
|
|
- **FR-251-006 Decision shape**: The effective lifecycle decision MUST include the state, source, operator-visible rationale, last changed attribution, and a summary of which in-scope behaviors are currently warned, allowed, or blocked.
|
|
- **FR-251-007 State precedence**: Lifecycle state MUST apply after the existing entitlement substrate and MAY only warn or restrict. It MUST NOT expand access beyond what the underlying entitlement decision already allows.
|
|
- **FR-251-008 Onboarding activation gate**: Managed-tenant onboarding activation MUST consult the shared lifecycle decision before mutation. `grace` and `suspended_read_only` MUST block activation before any tenant activation state changes occur.
|
|
- **FR-251-009 Review-pack start gate**: `Generate pack`, `Regenerate`, and `Export executive pack` MUST consult the shared lifecycle decision before creating or reusing a `ReviewPack` or `OperationRun`. `suspended_read_only` MUST block those actions before any new run or artifact start occurs.
|
|
- **FR-251-010 Grace semantics**: `grace` MUST have a distinct behavioral consequence from `active_paid` by freezing new managed-tenant onboarding activation while leaving in-scope review-pack start behavior under the existing entitlement substrate.
|
|
- **FR-251-011 Suspended read-only semantics**: `suspended_read_only` MUST block onboarding activation and review-pack start actions while preserving authorized read-only access to existing review history, evidence, and already-generated review-pack consumption.
|
|
- **FR-251-012 In-flight behavior boundary**: A lifecycle state change to `suspended_read_only` MUST affect future start attempts only in this slice and MUST NOT retroactively cancel already-created review-pack runs.
|
|
- **FR-251-013 Message semantics**: Gated surfaces MUST clearly distinguish lifecycle business-state blocking from entitlement-limit blocking and from authorization failure.
|
|
- **FR-251-014 System visibility**: The system workspace detail surface MUST show the current lifecycle state, rationale, affected behavior summary, and last changed attribution to authorized platform users.
|
|
- **FR-251-015 Auditability**: Every lifecycle state change and manual override MUST create an auditable record containing old state, new state, actor, and rationale.
|
|
- **FR-251-016 No scattered lifecycle conditionals**: Onboarding, review-pack generation, and preserved read-only surfaces MUST use the shared lifecycle decision rather than local page-specific commercial booleans.
|
|
- **FR-251-017 Bounded non-goals**: This slice MUST NOT introduce payment providers, invoices, taxes, accounting, checkout, public pricing, website work, customer-account modeling, broad billing automation, or broad entitlement spread beyond the in-scope behaviors above.
|
|
|
|
## UI Action Matrix *(mandatory when Filament is changed)*
|
|
|
|
| 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 |
|
|
|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Platform workspace commercial-state controls | existing system workspace detail surface | none on collection | dedicated detail route | none | none | N/A | `Change commercial state` with bounded state selection and rationale; `Suspended / read-only` path requires explicit confirmation | N/A | yes | Existing system-detail exception remains bounded to one platform mutation surface |
|
|
| Managed tenant onboarding activation gate | existing onboarding wizard completion step | existing back-navigation and tenant links | N/A - guided workflow | none | none | existing onboarding start state unchanged | `Complete onboarding` remains the primary action and becomes lifecycle-gated | N/A | yes - existing onboarding activation audit path | Existing wizard exception remains valid |
|
|
| Review-pack generation entry family | existing tenant dashboard, review register, tenant review detail, and review-pack detail/registry surfaces | current `Generate pack`, `Regenerate`, and `Export executive pack` actions stay primary where already present | existing registry/detail affordances remain unchanged | existing `View` or `Download` shortcuts remain secondary where already present | none | existing `Generate` CTA remains where already present | existing start actions are lifecycle-gated; `View` and `Download` remain outside the blocked-start gate | N/A | no new audit requirement for blocked attempts | Grouped action family stays consistent and does not invent new local start actions |
|
|
| Existing read-only review, evidence, and generated-pack consumption surfaces | existing review/evidence/detail/download surfaces | none | existing detail routes | existing `View` or `Download` actions remain available under current RBAC | none | N/A | existing read-only view/download actions remain available during suspension | N/A | no new audit action; read-only continuation only | No new surface is created; the slice only preserves availability semantics |
|
|
|
|
### Key Entities *(include if feature involves data)*
|
|
|
|
- **Workspace Commercial Lifecycle Setting**: Workspace-owned commercial posture consisting of lifecycle state, rationale, and last change attribution, persisted through the existing workspace settings infrastructure.
|
|
- **Effective Commercial Lifecycle Decision**: Derived decision that overlays the existing entitlement substrate and answers whether in-scope behaviors are allowed, warned, or blocked, plus why.
|
|
|
|
## Success Criteria *(mandatory)*
|
|
|
|
### Measurable Outcomes
|
|
|
|
- **SC-001**: Authorized platform operators can inspect and change a workspace commercial lifecycle state from one system workspace detail surface and see the updated state plus rationale immediately afterward.
|
|
- **SC-002**: Authorized workspace operators can determine in under 30 seconds whether onboarding activation or review-pack start is blocked by commercial state rather than by missing permission or underlying entitlement limits.
|
|
- **SC-003**: 100% of `suspended_read_only` blocked onboarding or review-pack start attempts stop before activation mutation or new run/artifact creation, while authorized readers still retain access to already-generated history and evidence.
|
|
- **SC-004**: Every commercial lifecycle state change produces one auditable old-state to new-state record with actor and rationale, and platform support can inspect that state from one canonical system surface.
|