49 KiB
Feature Specification: Billing & Subscription Truth Layer v1
Feature Branch: 274-billing-subscription-truth
Created: 2026-05-04
Status: Ready for implementation
Input: User description: "Promote the remaining commercial follow-through beyond Specs 247 and 251 into one bounded billing/subscription truth layer. Keep current plan profiles, entitlement gating, and commercial lifecycle UX intact where already real. Add one durable subscription source of truth that can explain why a workspace is trial, paid, overdue, cancellation-pending, or ended without introducing a billing engine, payment-provider integration, or customer portal."
Spec Candidate Check (mandatory - SPEC-GATE-001)
- Problem: TenantPilot already has plan profiles, entitlement gates, and a manual commercial lifecycle overlay, but it still has no durable subscription record that explains why a workspace is trial, active paid, in grace, or suspended read-only.
- Today's failure: Platform operators can manually set lifecycle posture on the system workspace page, yet the product still cannot answer whether that posture is backed by a current subscription, what billing period is active, when trial ends, or when cancellation should take effect.
- User-visible improvement: Platform operators can record one current subscription truth once, workspace operators can inspect a calm read-only summary, and the existing onboarding and review-pack gates become subscription-backed where a subscription exists instead of relying only on manual lifecycle overlays.
- Smallest enterprise-capable version: Introduce one workspace-owned current subscription record, one bounded subscription-state family plus current-period fields, one shared resolver that maps subscription truth into the existing commercial lifecycle resolver, one platform mutation surface on the existing system workspace page, and one read-only workspace-settings summary.
- Explicit non-goals: No payment-provider integration, no invoices, no checkout, no taxes, no seat billing, no public pricing, no website work, no customer billing portal, no webhook automation, no recurring reminder engine, no multi-subscription history browser, no CRM/account domain, and no second runtime gate outside the existing lifecycle resolver.
- Permanent complexity imported: One new workspace-owned subscription entity, one bounded resolver layered into the current commercial lifecycle path, one migration, one system detail mutation action, one read-only admin summary, and focused unit plus feature coverage.
- Why now: Specs 247 and 251 are already repo-real and deliberately left billing/subscription truth open. Without this follow-through, later commercial work will either duplicate manual lifecycle state or keep living in founder memory and external notes.
- Why not local: The same subscription truth must inform the system directory, workspace admin understanding, and the current lifecycle-backed onboarding and review-pack decisions. Local notes or page-level fields would drift immediately.
- Approval class: Core Enterprise
- Red flags triggered: New persisted truth, new state family, and multi-surface touchpoint. Defense: the slice stays on one current subscription record per workspace, keeps lifecycle as the only runtime gate, and avoids any billing engine, payment provider, or customer-account expansion.
- Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | Gesamt: 11/12
- Decision: approve
Spec Scope Fields (mandatory)
- Scope: workspace
- Primary Routes:
/system/directory/workspaces/{workspace}onApp\Filament\System\Pages\Directory\ViewWorkspace/admin/settings/workspaceonApp\Filament\Pages\Settings\WorkspaceSettings/admin/onboarding/{onboardingDraft}onApp\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard- current review-pack generation entry surfaces backed by
App\Services\ReviewPackService
- Data Ownership: Subscription truth becomes one workspace-owned persisted record with its own lifecycle in a new
workspace_subscriptionstable. Existing plan profiles, entitlement overrides, and manual commercial lifecycle fallback remain workspace-owned through existing settings infrastructure. Tenant-owned review packs, evidence snapshots, and onboarding records stay unchanged. - RBAC: Platform users with current directory visibility plus the existing dedicated commercial-management capability may inspect and update subscription truth on
/system. Workspace members withCapabilities::WORKSPACE_SETTINGS_VIEWmay inspect the read-only subscription summary on/admin/settings/workspace. Existing onboarding and review-pack capability checks remain authoritative. Non-members and wrong-plane actors continue to receive 404. In-scope actors missing capability continue to receive 403. Business-state blocking remains a product-state response for otherwise authorized actors.
For canonical-view specs, the spec MUST define:
- Default filter behavior when tenant-context is active: N/A - this slice adds no tenantless collection and no canonical tenant list.
- Explicit entitlement checks preventing cross-tenant leakage: Existing workspace and tenant isolation remain authoritative. Subscription truth is workspace-owned and must never reveal tenant-owned records outside existing authorized 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): system detail summaries, bounded header actions, settings summaries, status messaging, onboarding and review-pack action gating helper text
- Systems touched:
WorkspaceCommercialLifecycleResolver,WorkspaceEntitlementResolver,ViewWorkspace,WorkspaceSettings,ManagedTenantOnboardingWizard,ReviewPackService,WorkspaceAuditLogger, and the shared badge or status vocabulary - Existing pattern(s) to extend: existing system workspace detail summary and bounded mutation pattern, existing workspace settings read-only summary pattern, existing lifecycle-gated onboarding and review-pack paths, and existing audit-backed commercial change semantics
- Shared contract / presenter / builder / renderer to reuse: the current commercial lifecycle decision path remains the shared runtime gate; this slice adds one bounded
WorkspaceSubscriptionResolverand extends the currentWorkspaceCommercialLifecycleResolverso downstream surfaces keep consuming one lifecycle decision instead of a second gate - Why the existing shared path is sufficient or insufficient: The existing lifecycle resolver is sufficient as the one gate surface. It is insufficient as source truth because it currently relies on manual lifecycle state with no durable subscription record, no current billing-period context, and no bounded subscription-state vocabulary.
- Allowed deviation and why: none. The feature must not create a second billing panel, page-local subscription copy, or direct subscription gate checks on onboarding or review-pack surfaces.
- Consistency impact: Subscription state labels, lifecycle labels, source labels, fallback wording, and date wording must stay aligned across the system detail surface, workspace settings summary, and the existing lifecycle-backed action surfaces.
- Review focus: Reviewers must verify that subscription truth feeds the existing lifecycle resolver, that downstream surfaces still consume lifecycle rather than local subscription checks, and that fallback wording remains explicit when no subscription record exists.
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 generation continues to reuse
App\Services\ReviewPackService,App\Support\OpsUx\OperationUxPresenter, andApp\Support\OperationRunLinks. Subscription truth adds no new run family. - Delegated start/completion UX behaviors: When the derived lifecycle allows review-pack generation, queued toast, run link, dedupe handling, and terminal notifications stay on the existing review-pack path. When derived lifecycle blocks, no new
OperationRunis created and no queued or terminal lifecycle feedback is emitted. - Local surface-owned behavior that remains: The system detail surface owns subscription edit inputs. Workspace settings owns the read-only summary block. Onboarding and review-pack surfaces own only the immediate business-state message for the current action.
- Queued DB-notification policy: unchanged
- Terminal notification path: unchanged 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. External billing providers remain out of scope. Subscription truth is platform-core commercial metadata owned by TenantPilot.
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 subscription truth section | yes | Native Filament system detail page | detail summary, header action, status messaging | detail page, header action, summary cards | no | Extends the existing workspace detail page instead of adding a second system console |
| Workspace settings read-only subscription summary | yes | Native Filament singleton settings page | settings summary, support text | page, section | no | Read-only only; no new admin-plane mutation surface |
| Managed tenant onboarding completion gate | yes | Native Filament wizard | action gating, helper text | completion step, confirmation action | no | Reuses the existing lifecycle-aware completion step |
| Review-pack generation entry family | yes | Native Filament widget/resource/page actions | operation-start gating, helper text, lifecycle messaging | widget action, detail action, list/header action | no | The slice keeps the existing start family and only changes the upstream lifecycle source |
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 subscription truth section | Primary Decision Surface | Platform operator decides what current subscription truth should back a workspace's commercial posture | Current subscription state, period dates, lifecycle mapping, fallback status, and last changed attribution | Existing entitlement summary and related workspace context remain secondary | Primary because this is the one commercial truth surface that changes the shared runtime source | Keeps commercial truth on one system page instead of support notes and ad hoc overrides | Removes guesswork about why a workspace is trial, overdue, or ending |
| Workspace settings read-only subscription summary | Secondary Context Surface | Workspace operator inspects current commercial posture without mutating it | Current subscription-backed or fallback-backed state, next relevant date, and concise explanation | No additional diagnostics in this slice | Not primary because workspace users do not change subscription truth here | Keeps self-serve understanding on the current settings page | Reduces support questions caused by invisible commercial posture |
| Managed tenant onboarding completion gate | Secondary Context Surface | Workspace operator decides whether onboarding may complete now | Current lifecycle outcome and why it is blocked or allowed | Broader subscription detail stays secondary | Not primary because onboarding still answers an activation question, not a billing-management question | Keeps the commercial explanation inside the current activation workflow | Prevents operators from confusing subscription-driven blocks with permission problems |
| Review-pack generation entry family | Secondary Context Surface | Reporting operator decides whether generation may start now | Current lifecycle outcome and concise reason | Broader subscription detail stays secondary | Not primary because the family exists to continue reporting work, not to manage subscription truth | Keeps the explanation inside the current start workflow | Prevents separate support lookup when generation blocks for commercial reasons |
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 subscription truth section | support-platform, operator-platform | Current subscription state, derived lifecycle state, period dates, fallback indicator, and concise reason | Reference identifier, last changed attribution, and inherited entitlement summary | No raw provider payloads or external-billing diagnostics | Update subscription truth |
Future external-sync detail and any raw billing payload remain out of scope | The page states the commercial posture once and uses secondary detail for reference and attribution |
| Workspace settings read-only subscription summary | operator-MSP | Current commercial posture, whether it is subscription-backed or fallback-backed, next relevant date, and concise explanation | No additional diagnostics in this slice | none | none - read-only context only | Platform mutation controls and raw reference detail stay hidden | The settings page mirrors the derived truth and does not restate every downstream block reason |
| Managed tenant onboarding completion gate | operator-MSP | Current lifecycle outcome and concise business-state reason for the activation decision | Existing readiness diagnostics remain secondary | none | Complete onboarding when allowed |
Full subscription detail stays off the onboarding step | One lifecycle message is reused instead of separate subscription prose |
| Review-pack generation entry family | operator-MSP | Current lifecycle outcome and concise business-state reason for the start decision | Existing run state and artifact status remain secondary | none | Generate pack, Regenerate, or Export executive pack when allowed |
Full subscription detail stays off the start surfaces | The same lifecycle result is reused across current start-entry points |
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 subscription truth section | System / Detail / Diagnostics | Read-only detail with bounded mutation action | Update the current subscription truth | Dedicated workspace detail page | forbidden | Existing admin-workspace and related navigation stay secondary | None in this slice; update is high-impact but not destructive | /system/directory/workspaces |
/system/directory/workspaces/{workspace} |
Platform workspace identity plus current commercial posture | Subscription truth | Subscription state, derived lifecycle, next relevant date, and fallback status | Existing system-detail exception remains bounded to one platform mutation surface |
| Workspace settings read-only subscription summary | Config / Settings / Singleton | Workspace settings page | Inspect the current commercial posture | In-page read-only section | forbidden | Existing settings navigation remains secondary | none | /admin/settings/workspace |
/admin/settings/workspace |
Active workspace context | Subscription summary | Subscription-backed or fallback-backed commercial posture and next relevant date | Existing singleton-settings exception remains valid |
| Managed tenant onboarding completion gate | Workflow / Guided action entry | Onboarding completion step | Complete onboarding or stop because commercial posture blocks activation | In-page completion section | forbidden | Existing back-navigation and tenant links remain secondary | Existing draft-cancel and draft-delete actions remain where they are today | /admin/onboarding |
/admin/onboarding/{onboardingDraft} |
Active workspace 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 remain secondary and stay outside the start gate |
Existing destructive actions remain out of scope and keep their current placement | current tenant dashboard, /admin/reviews, and current review-pack collection surfaces |
current tenant review and review-pack detail routes | Active workspace, active tenant, review or pack context | Review-pack generation | Start allowed or blocked and why | Existing grouped-action family remains authoritative |
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 subscription truth section | Platform support or commercial operator | Record and inspect the current subscription truth for a workspace | System detail page | What subscription-backed commercial posture should this workspace be in now? | Subscription state, derived lifecycle, period dates, fallback status, and concise reason | Reference identifier, attribution, and inherited entitlement summary | subscription state, lifecycle state, fallback status | TenantPilot only | Update subscription truth | none |
| Workspace settings read-only subscription summary | Workspace owner or manager | Understand the current commercial posture without changing it | Singleton settings page | What commercial state is currently active for this workspace, and when does it change next? | Subscription-backed or fallback-backed state, next relevant date, and concise explanation | No additional diagnostics in this slice | subscription state, lifecycle state, fallback status | none | none | none |
| Managed tenant onboarding completion gate | Workspace owner or manager completing onboarding | Decide whether the current tenant may be activated now | Guided workflow step | Can I activate this tenant under the current commercial posture? | Current lifecycle outcome and concise reason | Existing readiness and verification detail | lifecycle state, 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 review-pack run may start now | Contextual start-action family | Can I start, retry, or export a pack from this context? | Current lifecycle outcome and concise reason | Existing run state and artifact status | lifecycle state, entitlement availability, run state | TenantPilot only until the current run starts | Generate pack, Regenerate, Export executive pack | Existing destructive actions remain unchanged and out of scope |
Proportionality Review (mandatory when structural complexity is introduced)
- New source of truth?: yes - one workspace-owned current subscription record becomes the durable source behind commercial posture when present
- New persisted entity/table/artifact?: yes - one current workspace subscription record per workspace
- New abstraction?: yes - one bounded resolver that reads subscription truth and maps it into the existing lifecycle path
- New enum/state/reason family?: yes - one bounded subscription-state family
- New cross-domain UI framework/taxonomy?: no
- Current operator problem: Operators can set lifecycle posture today, but they still cannot truthfully explain whether that posture is backed by a real subscription, what billing period is active, or when trial or cancellation should take effect.
- Existing structure is insufficient because: Existing settings-backed plan profiles and manual lifecycle overlays do not provide a distinct subscription entity with its own lifecycle, dates, reference, or future sync seam.
- Narrowest correct implementation: Introduce one current subscription record per workspace, map it into the existing lifecycle resolver, keep lifecycle as the only runtime gate, reuse current system and settings surfaces, and keep history in audit rather than adding a broader subscription ledger.
- Ownership cost: One new table, one resolver, one migration, one system detail mutation surface, read-only summary upkeep, and focused tests for mapping plus fallback behavior.
- Alternative intentionally rejected: More workspace-setting keys were rejected because subscription truth now has an independent lifecycle, date fields, and future sync seam that deserve explicit persistence. A broader customer-account or billing engine was rejected as beyond current-release truth.
- Release truth: current-release truth with explicit follow-up room for later external billing sync and automation
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 state validation, lifecycle mapping, and fallback precedence. Focused feature coverage proves the existing system detail page, workspace settings summary, onboarding completion gate, and review-pack start gate without widening into browser or heavy-governance lanes.
- New or expanded test families: one new
Entitlementsunit family for subscription truth plus focused extensions to the existing system, settings, onboarding, and review-pack feature families - Fixture / helper cost impact: Add only workspace, platform user, workspace member, onboarding draft, existing tenant review or review pack, and subscription-record fixtures required to prove current commercial decisions. Avoid browser harnesses, payment mocks, and external sync 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 workspace settings summary. Existing onboarding and review-pack feature families remain the proving lane for current runtime effects.
- Reviewer handoff: Reviewers must confirm that subscription-backed workspaces now derive lifecycle from the new record, that fallback behavior remains explicit for workspaces without a subscription, that onboarding and review-pack surfaces still consult lifecycle rather than direct subscription checks, that blocked review-pack starts create no run, and that existing review-pack view or download access remains unchanged.
- 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/WorkspaceSubscriptionResolverTest.php tests/Unit/Entitlements/WorkspaceCommercialLifecycleResolverTest.phpexport 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/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.phpexport PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Onboarding/ManagedTenantOnboardingEntitlementTest.php tests/Feature/ReviewPack/ReviewPackEntitlementEnforcementTest.php tests/Feature/ReviewPack/ReviewPackGenerationTest.php tests/Feature/ReviewPack/ReviewPackDownloadTest.php tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.phpexport PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
Scope Boundaries (required for this slice)
In Scope
- One current workspace-owned subscription record with a bounded state family and current-period context
- Exactly five subscription states:
trial,active,past_due,cancel_at_period_end, andended - One shared resolver that maps subscription state into the existing commercial lifecycle decision when a subscription record exists
- Explicit fallback to the existing manual lifecycle overlay when no subscription record exists for a workspace
- One system-plane mutation surface on the existing workspace directory detail page
- One read-only workspace-settings summary for current commercial posture
- Audit logging for subscription truth changes
Non-Goals
- Payment providers, invoices, taxes, checkout, public pricing, and website work
- Customer-account, contract, CRM, or broader billing-domain models
- Multiple current subscriptions per workspace or a historical subscription ledger UI
- Webhook sync, scheduled renewal or expiry jobs, dunning, or reminder automation
- Direct subscription checks on onboarding or review-pack surfaces outside the shared lifecycle resolver
- A second admin-plane commercial-management surface
Assumptions
- One current subscription record per workspace is the narrowest current-release truth; historical state changes can stay in
AuditLogfor v1. - Existing plan profile and entitlement override behavior from Spec 247 remains authoritative for what a workspace is allowed to do. Subscription truth only changes the upstream lifecycle source when present.
- Existing manual commercial lifecycle state from Spec 251 remains the fallback source for workspaces that do not yet have a subscription record.
cancel_at_period_enddoes not auto-transition after the period-end date in v1. The date is visible so platform operators can perform an explicit review instead of relying on implicit automation.- Existing review-pack runs that were already created before a later subscription or lifecycle change may complete unchanged because this slice only changes future start decisions.
Risks
- Subscription-backed lifecycle and fallback manual lifecycle can drift if fallback visibility is not explicit when no subscription record exists.
- One current subscription record may be too narrow for later upsell or add-on scenarios, but it is the smallest correct current-release truth.
- Later external billing sync could replace the v1 mutation path, but pre-production posture allows the source to evolve without long compatibility shims.
cancel_at_period_endcan become stale if operators ignore the visible date, so the system detail surface must keep the date obvious even without automation.
Deferred Adjacent Candidates
- External billing-provider sync and webhook-driven subscription updates
- Invoice or receipt storage and payment-collection flows
- Customer billing portal or workspace self-serve commercial controls
- Broader demo or seeded trial automation beyond the current commercial truth layer
User Scenarios & Testing (mandatory)
User Story 1 - Record one current workspace subscription truth centrally (Priority: P1)
As a platform support or commercial operator, I want to record one current subscription truth for a workspace so the product can explain commercial posture from one durable source instead of manual notes and free-form lifecycle overrides.
Why this priority: Without a durable subscription record, every later commercial or support surface must guess whether the current lifecycle posture is truly backed by a paid subscription, trial, overdue state, or pending cancellation.
Independent Test: Open the existing system workspace detail page, create or update subscription truth with state, dates, reference, and status reason, and confirm the page shows the new derived commercial posture plus audit attribution without touching onboarding or review-pack code paths.
Acceptance Scenarios:
- Given a workspace has no current subscription record, When an authorized platform operator saves a
trialsubscription withtrial_ends_at, Then the workspace shows that subscription as the current commercial truth and the derived lifecycle resolves totrial. - Given a workspace currently has an
activesubscription, When the same operator updates it topast_duewith status reason and period dates, Then the record is updated, the change is auditable, and the derived lifecycle resolves tograce. - Given a workspace currently has a subscription record, When an unauthorized or wrong-plane actor attempts to update it, Then authorization remains enforced through existing 404 and 403 rules and no subscription truth is changed.
User Story 2 - Keep current lifecycle-backed runtime gates but source them from subscription truth when present (Priority: P1)
As an authorized workspace operator, I want the current onboarding and review-pack decisions to continue using one shared lifecycle gate so the product remains consistent while subscription-backed workspaces stop relying on manual lifecycle state.
Why this priority: The repo already gates onboarding and review-pack starts through the lifecycle resolver. The value of this slice is not a new gate but a more truthful upstream source.
Independent Test: Seed one workspace with a subscription record and one without. Confirm that the subscription-backed workspace derives lifecycle from subscription state, the fallback workspace still uses manual lifecycle state, and the existing onboarding or review-pack behavior follows the resulting lifecycle outcome in both cases.
Acceptance Scenarios:
- Given a workspace has an
activesubscription and the underlying entitlement substrate allows activation, When an authorized operator reaches onboarding completion, Then the step allows completion and identifies the commercial posture as subscription-backed. - Given a workspace has a
past_duesubscription, When the same operator attempts onboarding completion, Then the step blocks before any activation mutation and explains the current grace posture as derived from subscription truth. - Given a workspace has no subscription record but does have an explicit manual lifecycle state of
suspended_read_only, When an authorized operator attempts review-pack generation, Then the current lifecycle block still applies and the surface makes clear that the result comes from fallback lifecycle truth rather than a subscription record. - Given a workspace has a
cancel_at_period_endsubscription and the current review-pack entitlement allows generation, When an authorized operator starts review-pack generation, Then the existing run flow continues through the current lifecycle gate and no second subscription gate is introduced.
User Story 3 - Let workspace operators inspect the current commercial posture without gaining billing controls (Priority: P2)
As a workspace owner or manager, I want to inspect the current subscription-backed or fallback-backed commercial posture on the existing settings page so I can understand why current actions are allowed or blocked without opening the platform plane.
Why this priority: Customer-safe self-understanding matters, but it must not create a second mutation plane or a broader billing UI.
Independent Test: Open /admin/settings/workspace as a workspace manager, confirm the page shows the current commercial posture, next relevant date, and fallback indicator, and verify there are no edit controls for subscription truth.
Acceptance Scenarios:
- Given a workspace has a current
trialsubscription, When an authorized workspace manager opens the existing settings page, Then the page shows the current commercial posture, trial end date, and a concise explanation without exposing platform mutation controls. - Given a workspace has no subscription record and is still using fallback lifecycle truth, When the same manager opens the settings page, Then the page clearly marks the posture as fallback-backed rather than subscription-backed.
- Given a non-member or wrong-plane actor attempts to inspect the same page, When the request is evaluated, Then existing isolation rules still apply and no workspace commercial truth is leaked.
Edge Cases
- A workspace with no subscription record must continue to behave exactly as current Specs 247 and 251 require until a platform operator records subscription truth.
- A workspace with a subscription state of
cancel_at_period_endand a past period-end date must remain explicitly review-required on the system detail surface without auto-transitioning in v1. - If a workspace has a subscription state that maps to an allowed lifecycle but the underlying entitlement substrate blocks the action, the substrate block still wins and must remain distinguishable from subscription-backed lifecycle decisions.
- If a review-pack run was already created before a later subscription or lifecycle change, the existing run may complete; the new commercial posture affects future starts only.
- A workspace member lacking onboarding or review-pack capability must still receive 403 even if subscription-backed lifecycle would otherwise allow the action.
- A non-member or wrong-plane actor must not learn whether a workspace is trial, overdue, cancellation-pending, ended, or fallback-backed; those requests continue to resolve as 404.
Requirements (mandatory)
Constitution alignment (required): This feature adds runtime-changing commercial truth and one new workspace-owned persisted entity, but it does not add Microsoft Graph calls, provider dispatch, payment-provider integrations, or a new queued workflow family. Existing review-pack OperationRun behavior is reused only when the current lifecycle gate allows the start action.
Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001): The feature introduces a new persisted subscription record because the current-release operator problem now needs distinct commercial truth with its own lifecycle, dates, and audit trail rather than more settings keys. A narrower settings-only approach would continue to blur lifecycle fallback and subscription-backed truth.
Constitution alignment (XCUT-001): All in-scope surfaces must reuse the same derived lifecycle decision, whether it comes from a subscription record or fallback lifecycle state. No surface may invent local subscription-to-lifecycle rules or local block reasons.
Constitution alignment (DECIDE-AUD-001 / OPSURF-001): The system detail page shows the subscription-backed decision first and deeper reference detail second. Workspace settings shows only the calm read-only summary needed for self-understanding. Onboarding and review-pack surfaces show only the immediate lifecycle outcome required for the action.
Constitution alignment (PROV-001): Subscription truth remains provider-neutral and platform-core. The feature must not import Stripe-, payment-gateway-, or vendor-specific semantics into shared commercial vocabulary.
Constitution alignment (TEST-GOV-001): Proof stays in focused unit plus feature lanes. New fixtures remain limited to workspace, subscription record, platform operator, workspace member, onboarding draft, and review-pack-capable tenant context.
Constitution alignment (OPS-UX): This feature adds no new run family. Existing review-pack generation keeps the current queued toast, operation link, and terminal notification path when lifecycle allows it. Blocked starts create no run and no run lifecycle feedback.
Constitution alignment (OPS-UX-START-001): Subscription truth sits upstream of WorkspaceCommercialLifecycleResolver. The lifecycle resolver remains the only gate that review-pack entry surfaces consult before run creation.
Constitution alignment (RBAC-UX): Two authorization planes are involved: platform /system for subscription mutation and admin /admin for read-only or contextual lifecycle effects. Wrong-plane or non-member requests remain 404. Members missing capability remain 403. Business-state blocking remains distinct from authorization failure.
Constitution alignment (BADGE-001): If subscription state or derived lifecycle badges are rendered, their labels and visual semantics must come from one shared catalog or mapping rather than page-local color logic.
Constitution alignment (UI-FIL-001): The slice extends existing native Filament detail, settings, wizard, widget, and resource surfaces only. No custom billing panel, independent status language, or new design system is allowed.
Constitution alignment (UI-NAMING-001): Primary operator labels stay product-facing and specific: Subscription status, Derived commercial posture, Trial ends, Current period ends, Update subscription truth, Complete onboarding, Generate pack, Regenerate, and Export executive pack. Checkout or vendor terms remain out of scope.
Constitution alignment (DECIDE-001): The system workspace detail page is the one primary commercial-truth decision surface. Workspace settings remains a read-only context surface. Onboarding and review-pack surfaces remain contextual decision points that only expose the current lifecycle result.
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, singleton settings, onboarding wizard, and grouped review-pack action patterns. It may not add a second admin-plane commercial management surface, redundant inspect actions, or mixed catch-all action groups.
Constitution alignment (ACTSURF-001 - action hierarchy): Subscription mutation stays on the system workspace detail page. Workspace settings remains read-only. Onboarding completion stays the primary activation action. Review-pack generation stays the primary reporting mutation where already present.
Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001): One thin subscription resolver plus lifecycle mapping is justified because direct reads from settings and manual lifecycle state cannot express subscription-backed truth. Tests must prove business outcomes such as mapping, fallback, allowed versus blocked execution, and auditability rather than cosmetic 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 singleton settings exception for read-only context, the existing onboarding wizard exception, and the existing review-pack action family. No empty action groups or redundant inspect actions are introduced.
Constitution alignment (UX-001 - Layout & Information Architecture): The system detail page remains the one mutation surface, and workspace settings remains the read-only summary surface. The feature does not create a new commercial shell or duplicate summary page.
Functional Requirements
- FR-274-001 Workspace-owned subscription truth: The system MUST persist one current subscription record per workspace in a dedicated workspace-owned table rather than as additional workspace-setting keys.
- FR-274-002 Bounded subscription-state catalog: The current subscription record MUST use exactly these state identifiers in v1:
trial,active,past_due,cancel_at_period_end, andended. - FR-274-003 Required commercial context fields: The current subscription record MUST support a bounded set of commercial context fields:
billing_reference(optional),trial_ends_at(required fortrial),current_period_starts_atandcurrent_period_ends_at(required foractive,past_due, andcancel_at_period_end),current_period_ends_at(required forended), andstatus_reason(required on every explicit mutation). - FR-274-004 Platform-managed mutation: Only authorized platform users MAY create or update current subscription truth in this slice, the system-plane mutation MUST remain confirmation-protected, and workspace or tenant admin users MUST NOT gain self-service mutation controls.
- FR-274-005 Auditability: Every create or update of current subscription truth MUST record old state, new state, actor, and status reason through the existing audit foundation.
- FR-274-006 Lifecycle derivation precedence:
WorkspaceCommercialLifecycleResolverMUST derive the effective lifecycle state from current subscription truth when a subscription record exists for the workspace. - FR-274-007 Lifecycle fallback: If no current subscription record exists for a workspace,
WorkspaceCommercialLifecycleResolverMUST preserve the current Spec 251 fallback behavior by using existing manual lifecycle state or defaultactive_paid, and the existing system-planeChange commercial stateaction remains available only in that fallback condition. - FR-274-008 Deterministic lifecycle mapping: The lifecycle resolver MUST map subscription states as follows:
trial->trial,active->active_paid,past_due->grace,cancel_at_period_end->active_paid, andended->suspended_read_only. - FR-274-009 No second runtime gate: Onboarding activation and review-pack generation MUST continue consulting the shared lifecycle resolver only. Subscription truth MUST NOT create a second direct gate on those surfaces.
- FR-274-010 Stale review-required visibility: If a
trialrecord has a pasttrial_ends_atdate or acancel_at_period_endrecord has a pastcurrent_period_ends_atdate, the system workspace detail page MUST surface that the subscription record needs review, but v1 MUST NOT auto-transition the state. - FR-274-011 System workspace visibility: The existing system workspace detail page MUST show current subscription state, derived lifecycle state, next relevant date, fallback status, reference, and last changed attribution to authorized platform users.
- FR-274-012 Workspace settings visibility: The existing workspace settings page MUST show a read-only summary of the current commercial posture, explicitly indicating whether it is subscription-backed or fallback-backed.
- FR-274-013 Onboarding activation continuity: Managed-tenant onboarding activation MUST keep using the lifecycle decision after subscription mapping, stopping before tenant activation when the lifecycle outcome blocks the action.
- FR-274-014 Review-pack start continuity:
Generate pack,Regenerate, andExport executive packMUST keep using the lifecycle decision after subscription mapping, stopping before any newReviewPackorOperationRunis created when the lifecycle outcome blocks the action. - FR-274-015 Existing artifact access unchanged: This slice MUST NOT change existing view or download access to already-generated review packs, evidence, or review history that remain accessible under current RBAC and lifecycle rules.
- FR-274-016 One current record only: V1 MUST support exactly one current subscription record per workspace and rely on audit history rather than a multi-row subscription ledger or browsing surface.
- FR-274-017 Bounded non-goals: This slice MUST NOT introduce invoices, payment collection, taxes, checkout, website pricing, vendor-specific billing adapters, webhook automation, customer portals, or a second commercial control plane.
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 subscription truth section | app/Filament/System/Pages/Directory/ViewWorkspace.php |
none on collection | dedicated detail route only | none | none | N/A | Update subscription truth and, only when no subscription record exists, fallback Change commercial state; both remain confirmation-protected |
N/A | yes | Existing system-detail exception remains bounded to one platform mutation surface |
| Workspace settings read-only subscription summary | app/Filament/Pages/Settings/WorkspaceSettings.php |
Save remains owned by settings, but subscription summary is read-only |
N/A - singleton settings page | none | none | N/A | none | existing settings save or cancel behavior unchanged; no subscription edit controls | no new audit event; read-only only | Existing singleton-page exception remains valid |
| Managed tenant onboarding completion gate | app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php |
existing back-navigation and tenant links | N/A - guided workflow | none | none | existing onboarding start state unchanged | Complete onboarding remains the primary action and stays lifecycle-gated |
N/A | yes - existing onboarding audit semantics remain | Existing wizard exception remains valid |
| Review-pack generation entry family | current tenant dashboard, review register, tenant review detail, and review-pack detail or registry surfaces | existing Generate pack, Regenerate, and Export executive pack actions stay primary where already present |
existing registry/detail affordances remain unchanged | existing View and Download shortcuts remain secondary where already present |
none | existing Generate CTA remains where already present |
existing start actions remain lifecycle-gated; View and Download stay outside the start gate |
N/A | no new audit requirement for blocked attempts | Existing grouped action family remains authoritative |
Key Entities (include if feature involves data)
- WorkspaceSubscription: One workspace-owned current subscription record containing bounded subscription state, current commercial dates, optional reference, and status reason.
- WorkspaceSubscriptionSummary: A derived read model that combines current subscription truth, fallback status, and next relevant date for system and admin surfaces.
- EffectiveCommercialLifecycleDecision: The existing shared lifecycle decision, now potentially sourced from current subscription truth before it reaches onboarding and review-pack entry surfaces.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: Authorized platform operators can inspect and update a workspace's current subscription truth from one system workspace detail surface and see the updated derived commercial posture immediately afterward.
- SC-002: Authorized workspace operators can determine from the settings page in under 30 seconds whether the current commercial posture is subscription-backed or fallback-backed and what the next relevant date is.
- SC-003: 100% of onboarding and review-pack decisions for workspaces with a current subscription record follow the derived lifecycle mapping from that record rather than stale manual lifecycle state.
- SC-004: The feature ships without adding a second runtime gate, a new run family, a payment-provider integration, or a customer-facing billing surface.