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
60 lines
4.8 KiB
Markdown
60 lines
4.8 KiB
Markdown
# 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.
|