TenantAtlas/specs/274-billing-subscription-truth/data-model.md
ahmido 35b59eb628 274: Billing subscription truth - add workspace subscription model & tests (#326)
Automated PR: commit all local changes and add feature 274-billing-subscription-truth.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #326
2026-05-04 21:15:57 +00:00

7.9 KiB

Data Model: Billing & Subscription Truth Layer v1

Date: 2026-05-04
Branch: 274-billing-subscription-truth

Overview

This slice adds one new workspace-owned source of truth: a current subscription record. Existing plan profiles, entitlement overrides, and manual commercial lifecycle fallback remain in their current storage. The new record feeds the existing commercial lifecycle resolver when present.

Persisted Truth

1. Workspace Subscription Aggregate

Persistence: New workspace_subscriptions table
Ownership: Workspace-owned
Scope: One current record per workspace

Field Type Nullable Validation Notes
id bigint no primary key Internal record id
workspace_id bigint no foreign key, unique Enforces one current subscription record per workspace
state string no must be one of trial, active, past_due, cancel_at_period_end, ended Current subscription posture
billing_reference string yes trimmed, max 191 chars Optional contract, subscription, or invoice reference label
trial_ends_at datetime yes required when state=trial Current trial end date
current_period_starts_at datetime yes required when state is active, past_due, or cancel_at_period_end Current commercial period start
current_period_ends_at datetime yes required when state is active, past_due, cancel_at_period_end, or ended Current commercial period end or ended-on boundary
status_reason text no required on every explicit mutation path Operator-visible explanation
created_at datetime no standard timestamps Creation time
updated_at datetime no standard timestamps Latest mutation time

Write rules:

  • Mutation happens from the system plane only.
  • workspace_id is immutable once the row exists.
  • The record is updated in place in v1; no historical row chain is created.
  • Audit history captures before and after values and actor attribution.

Relationships:

  • workspace_subscriptions.workspace_id references workspaces.id.
  • Workspace gains a singular subscription relationship.

Existing Persisted Truth Reused

2. Workspace Entitlement Substrate

Persistence: Existing workspace_settings rows plus code-owned plan catalog
Owner: WorkspaceEntitlementResolver

This slice does not remodel:

  • plan profile selection
  • first-slice entitlement overrides
  • first-slice entitlement usage summaries

These remain the substrate that lifecycle may restrict after subscription mapping.

3. Manual Lifecycle Fallback

Persistence: Existing workspace_settings rows from Spec 251
Owner: WorkspaceCommercialLifecycleResolver

Manual lifecycle state remains valid only as fallback when a workspace does not yet have a current subscription record.

Code-Owned Truth

4. Subscription State Catalog Entry

Persistence: none, code-owned
Ownership: product runtime configuration

Field Type Required Notes
id string yes Stable internal identifier
label string yes Operator-facing label
description string yes Short explanation for system and settings summaries
derived_lifecycle_state string yes One of the existing Spec 251 lifecycle states
needs_review_when_past_date bool yes Whether the record should surface explicit review-required wording when its key date is in the past

Behavior matrix:

Subscription state Derived lifecycle state Key date surfaced Notes
trial trial trial_ends_at Current trial posture
active active_paid current_period_ends_at Current paid period
past_due grace current_period_ends_at Commercial grace posture
cancel_at_period_end active_paid current_period_ends_at Still active, but cancellation is pending
ended suspended_read_only current_period_ends_at Commercial access has ended

Derived Truth

5. Workspace Subscription Summary

Persistence: none, derived at runtime
Owner: WorkspaceSubscriptionResolver

Field Type Required Notes
workspace_id int yes Workspace being evaluated
subscription_present bool yes Whether a current record exists
state string no Current subscription state when present
label string no Operator-facing state label
billing_reference string no Optional reference
status_reason string no Operator-visible explanation
key_date_label string no Trial ends or Current period ends
key_date datetime no Current relevant date
needs_review bool yes True when a date-sensitive state is past its visible date
source string yes One of workspace_subscription, workspace_setting, or default_active_paid
fallback_status bool yes True when the summary is not backed by a current subscription record
derived_lifecycle_state string yes Existing lifecycle state consumed downstream

6. Effective Commercial Lifecycle Decision

Persistence: none, derived at runtime
Owner: existing WorkspaceCommercialLifecycleResolver

The lifecycle decision remains the shared gate shape from Spec 251, but its source changes:

  • If a subscription record exists, the lifecycle source becomes workspace_subscription.
  • If no subscription record exists, the current workspace_setting or default_active_paid source remains.

Ordering rules:

  1. Resolve the underlying entitlement substrate.
  2. Resolve the lifecycle source from subscription truth when present, otherwise from fallback manual lifecycle truth.
  3. If the substrate already blocks the action, keep the substrate block.
  4. If the substrate allows the action, apply the lifecycle outcome from the resolved lifecycle state.

Supporting Derived View Models

7. System Workspace Subscription View Model

Consumer: ViewWorkspace

Contains:

  • current subscription summary
  • derived lifecycle summary
  • fallback indicator when no subscription exists
  • last-change attribution
  • mutation affordance metadata for Update subscription truth

8. Workspace Settings Subscription Summary View Model

Consumer: WorkspaceSettings

Contains:

  • current commercial posture
  • whether it is subscription-backed or fallback-backed
  • next relevant date
  • concise explanation only

State Transitions

There is no multi-row ledger in v1. State changes are explicit updates to the current workspace subscription record plus audit entries.

From To Trigger Consequence
no record any valid state platform operator creates current subscription truth workspace becomes subscription-backed
trial active platform operator transition derived lifecycle moves from trial to active_paid
active past_due platform operator transition derived lifecycle moves to grace
active cancel_at_period_end platform operator transition derived lifecycle stays active_paid, but period end becomes important context
past_due ended platform operator transition derived lifecycle moves to suspended_read_only
any state any other valid state platform operator update current subscription truth changes in place and is auditable

Boundaries Explicitly Preserved

  • No invoice, payment, or provider-sync persistence exists in this slice.
  • No multi-record historical subscription ledger exists in this slice.
  • No direct subscription gate shape exists on onboarding or review-pack surfaces; lifecycle remains the only gate.
  • Existing view and download access to already-generated review packs, evidence, and review history stays governed by the current lifecycle and RBAC rules.