TenantAtlas/specs/274-billing-subscription-truth/research.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

4.8 KiB

Research: Billing & Subscription Truth Layer v1

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

Decision 1: Use a dedicated workspace-owned subscription table, not more workspace-setting keys

  • Decision: Persist current subscription truth in a new workspace_subscriptions table with one current row per workspace.
  • Rationale: Subscription truth now has its own lifecycle, date fields, audit trail, and future sync seam. Keeping it inside WorkspaceSetting would continue to blur fallback lifecycle truth, plan settings, and current subscription truth into one settings bucket.
  • Alternatives considered:
    • More keys under entitlements.*: rejected because the record now has independent lifecycle meaning and no longer behaves like a small plan override.
    • Broad customer-account or billing model: rejected because the current release needs one current subscription record only, not a full billing domain.

Decision 2: Keep WorkspaceCommercialLifecycleResolver as the only runtime gate

  • Decision: WorkspaceCommercialLifecycleResolver remains the one gate consulted by onboarding and review-pack surfaces.
  • Rationale: Specs 247 and 251 already proved the current gate shape. Introducing direct subscription checks on onboarding or review-pack surfaces would create a second gate and immediate drift.
  • Alternatives considered:
    • Direct subscription checks on each surface: rejected because that would duplicate lifecycle logic and blur business-state versus entitlement-state reasoning.
    • Leaving lifecycle fully manual even after adding a subscription record: rejected because it would leave two competing commercial truths alive at once.

Decision 3: Subscription truth becomes the upstream lifecycle source when present

  • Decision: When a workspace has a current subscription record, lifecycle state is derived from that record. When no subscription record exists, the current manual lifecycle overlay remains the fallback source.
  • Rationale: This keeps current behavior stable for untouched workspaces while letting new subscription-backed workspaces stop relying on manual lifecycle state.
  • Alternatives considered:
    • Force every workspace to get a subscription record immediately: rejected because the repo already has live manual lifecycle semantics and the narrow slice should not require a bulk migration workflow.
    • Add subscription truth as read-only evidence without feeding lifecycle: rejected because it would preserve duplicated truths and force operators to reconcile them manually.

Decision 4: Use one current record and audit as history

  • Decision: V1 stores one current subscription record per workspace and relies on AuditLog for change history.
  • Rationale: The current operator problem is understanding the current posture and driving the current lifecycle gate, not browsing historical subscription revisions in-product.
  • Alternatives considered:
    • Multi-row history table or event ledger: rejected because the current slice would become a billing-history feature rather than a source-of-truth follow-through.
    • No history beyond the row itself: rejected because auditability is required for commercial truth changes.

Decision 5: Keep the current mutation surface on the system workspace detail page

  • Decision: Reuse ViewWorkspace for subscription mutation and keep WorkspaceSettings read-only.
  • Rationale: Specs 247 and 251 already separated admin-plane self-understanding from platform-plane commercial control. This slice should preserve that separation.
  • Alternatives considered:
    • Add an admin-plane subscription form: rejected because it would create a second commercial control plane.
    • Add a new system billing page: rejected because the existing workspace detail page is already the commercial-truth drilldown.

Decision 6: No automatic timers, provider sync, or invoice logic in v1

  • Decision: Period dates are visible and testable, but the product does not auto-transition subscription state, sync from an external system, or introduce invoices in this slice.
  • Rationale: The current need is durable commercial truth and one shared runtime source, not an automation engine.
  • Alternatives considered:
    • Webhook or provider sync placeholders: rejected because they would widen the slice into provider-boundary work.
    • Automatic transitions when trial or period end passes: rejected because the repo has no current scheduling or operator-review contract for that behavior.

Final Research Outcome

  • Current-release truth requires a dedicated current subscription record.
  • Runtime gating remains on the existing lifecycle resolver.
  • The implementation should stay on the current system and admin surfaces.
  • No provider, invoice, portal, or automation work is needed for this slice.