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

185 lines
16 KiB
Markdown

---
description: "Task list for Billing & Subscription Truth Layer v1"
---
# Tasks: Billing & Subscription Truth Layer v1
**Input**: Design documents from `specs/274-billing-subscription-truth/`
**Prerequisites**: `specs/274-billing-subscription-truth/spec.md`, `specs/274-billing-subscription-truth/plan.md`, `specs/274-billing-subscription-truth/checklists/requirements.md`, `specs/274-billing-subscription-truth/research.md`, `specs/274-billing-subscription-truth/data-model.md`, `specs/274-billing-subscription-truth/quickstart.md`, `specs/274-billing-subscription-truth/contracts/workspace-billing-subscription-truth.logical.openapi.yaml`
**Tests**: REQUIRED (Pest). Keep proof bounded to one new `Unit` family under `tests/Unit/Entitlements/` plus focused extensions to current `Feature` families for system, settings, onboarding, and review-pack behavior.
**Operations**: Reuse the existing `WorkspaceCommercialLifecycleResolver` and current review-pack `OperationRun` path. No new run type, no queue family, and no direct subscription gate outside lifecycle are allowed.
**RBAC**: Non-members and wrong-plane actors remain `404`; in-scope actors missing capability remain `403`. `/system` owns mutation; `/admin` remains read-only or contextual for subscription truth.
**Shared Pattern Reuse**: Reuse `WorkspaceEntitlementResolver`, `WorkspaceCommercialLifecycleResolver`, `ViewWorkspace`, `WorkspaceSettings`, `ManagedTenantOnboardingWizard`, `ReviewPackService`, and the current audit foundation. Do not create a second commercial control plane.
**Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration remains unchanged in `apps/platform/bootstrap/providers.php`. No new panel, no new globally searchable resource, and no new asset strategy are allowed.
**Organization**: Tasks are grouped by user story so the source-of-truth entity, lifecycle continuity, and read-only admin summary remain independently implementable and testable. This package is a bounded follow-through over Specs 247 and 251, not a billing-engine rewrite.
**Review Outcome**: `acceptable-special-case`
**Workflow Outcome**: `keep`
**Test-governance Outcome**: `keep`
## Test Governance Checklist
- [x] Lane assignment stays `fast-feedback` and `confidence` and remains the narrowest sufficient proof.
- [x] New or changed tests stay in the existing `apps/platform/tests/Unit/Entitlements/`, `apps/platform/tests/Feature/System/`, `apps/platform/tests/Feature/Filament/Settings/`, `apps/platform/tests/Feature/Onboarding/`, and `apps/platform/tests/Feature/ReviewPack/` families.
- [x] Shared helpers stay cheap by default; only one new factory is expected.
- [x] Planned validation commands cover subscription truth, lifecycle precedence, admin summary, and gate continuity without widening into browser or heavy-governance lanes.
- [x] The declared surface test profile remains `standard-native-filament`, `shared-detail-family`, and `monitoring-state-page` only.
- [x] Any drift toward providers, invoices, a portal, or a second runtime gate resolves as `reject-or-split`, not hidden scope.
## Phase 1: Setup (Shared Context)
**Purpose**: Confirm the current commercial truth seams before any implementation change.
- [x] T001 Review `specs/274-billing-subscription-truth/spec.md`, `specs/274-billing-subscription-truth/plan.md`, `specs/274-billing-subscription-truth/checklists/requirements.md`, `specs/274-billing-subscription-truth/research.md`, `specs/274-billing-subscription-truth/data-model.md`, `specs/274-billing-subscription-truth/quickstart.md`, `specs/247-plans-entitlements-billing-readiness/spec.md`, and `specs/251-commercial-entitlements-billing-state/spec.md` together so the slice stays on the current commercial foundations.
- [x] T002 [P] Confirm the current lifecycle and admin/system surface seams in `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php` and `apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php`.
- [x] T003 [P] Confirm the current runtime gate seams in `apps/platform/app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php`, `apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php`, and `apps/platform/app/Services/ReviewPackService.php`.
- [x] T004 [P] Confirm the current audit and authorization seams in `apps/platform/app/Services/Audit/WorkspaceAuditLogger.php`, `apps/platform/app/Support/Auth/PlatformCapabilities.php`, and `apps/platform/app/Services/Settings/SettingsWriter.php`.
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Lock the new persisted source of truth and shared mapping before surface changes begin.
**Critical**: No user-story work should begin until this phase is complete.
- [x] T005 [P] Add failing unit coverage in `apps/platform/tests/Unit/Entitlements/WorkspaceSubscriptionResolverTest.php` and extend `apps/platform/tests/Unit/Entitlements/WorkspaceCommercialLifecycleResolverTest.php` to prove state validation, lifecycle mapping, fallback precedence, and review-required date behavior.
- [x] T006 [P] Extend `apps/platform/tests/Feature/System/ViewWorkspaceEntitlementsTest.php`, `apps/platform/tests/Feature/System/Spec113/AuthorizationSemanticsTest.php`, and `apps/platform/tests/Feature/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.php` to lock the system mutation surface, admin read-only summary, and plane semantics.
- [x] T007 [P] Extend `apps/platform/tests/Feature/Onboarding/ManagedTenantOnboardingEntitlementTest.php`, `apps/platform/tests/Feature/ReviewPack/ReviewPackEntitlementEnforcementTest.php`, and `apps/platform/tests/Feature/ReviewPack/ReviewPackGenerationTest.php` to prove subscription-backed lifecycle continuity and no-run behavior on blocked starts.
- [x] T008 Create `apps/platform/database/migrations/*_create_workspace_subscriptions_table.php`, `apps/platform/app/Models/WorkspaceSubscription.php`, `apps/platform/database/factories/WorkspaceSubscriptionFactory.php`, and the `Workspace` relation so one current subscription record exists per workspace.
- [x] T009 Implement `apps/platform/app/Services/Entitlements/WorkspaceSubscriptionResolver.php` to expose current subscription summary, fallback status, next relevant date, and derived lifecycle mapping.
- [x] T010 Refactor `apps/platform/app/Services/Entitlements/WorkspaceCommercialLifecycleResolver.php` so subscription truth becomes the upstream lifecycle source when present and existing manual lifecycle state remains the fallback when absent.
**Checkpoint**: One persisted current subscription source exists and the shared lifecycle resolver can consume it without changing any surface yet.
---
## Phase 3: User Story 1 - Record one current workspace subscription truth centrally (Priority: P1)
**Goal**: Authorized platform users can create or update one current subscription record from the existing system workspace detail page.
**Independent Test**: Open the system workspace detail page, save subscription truth, and verify summary plus audit output update without touching onboarding or review-pack flows.
### Tests for User Story 1
- [x] T011 [P] [US1] Extend `apps/platform/tests/Feature/System/ViewWorkspaceEntitlementsTest.php` to prove create, update, validation, explicit confirmation, fallback visibility, and derived lifecycle rendering on the system page.
- [x] T012 [P] [US1] Extend `apps/platform/tests/Feature/System/Spec113/AuthorizationSemanticsTest.php` to prove wrong-plane or non-member requests remain `404` and in-scope actors missing the dedicated capability remain `403`.
### Implementation for User Story 1
- [x] T013 [US1] Update `apps/platform/app/Filament/System/Pages/Directory/ViewWorkspace.php` so the page renders current subscription truth, derived lifecycle, next relevant date, stale-date needs-review visibility, and one bounded confirmation-protected `Update subscription truth` action.
- [x] T014 [US1] Extend `apps/platform/app/Services/Audit/WorkspaceAuditLogger.php` and related audit metadata so subscription changes are attributable with old state, new state, actor, and status reason.
- [x] T015 [US1] Narrow or hide the existing manual lifecycle mutation affordance on the system page whenever a current subscription record exists, preserving the existing confirmation-protected `Change commercial state` action only as explicit fallback behavior when no subscription record is present.
**Checkpoint**: The system workspace detail page becomes the one durable commercial source surface.
---
## Phase 4: User Story 2 - Keep current runtime gates but source them from subscription truth when present (Priority: P1)
**Goal**: Existing onboarding and review-pack behavior remains on one lifecycle gate while subscription-backed workspaces stop relying on manual lifecycle state.
**Independent Test**: Seed one subscription-backed workspace and one fallback workspace, then confirm both current gate families still use one lifecycle decision with the correct source and no second gate.
### Tests for User Story 2
- [x] T016 [P] [US2] Extend `apps/platform/tests/Feature/Onboarding/ManagedTenantOnboardingEntitlementTest.php` to prove subscription-backed lifecycle mapping, fallback continuity, and business-state messaging on the completion step.
- [x] T017 [P] [US2] Extend `apps/platform/tests/Feature/ReviewPack/ReviewPackEntitlementEnforcementTest.php`, `apps/platform/tests/Feature/ReviewPack/ReviewPackGenerationTest.php`, `apps/platform/tests/Feature/ReviewPack/ReviewPackDownloadTest.php`, and `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspacePackAccessTest.php` to prove subscription-backed lifecycle mapping, no-run behavior on blocked starts, suppressed queued or terminal notifications when blocked, unchanged queued-start UX when allowed, and unchanged current pack view or download access.
### Implementation for User Story 2
- [x] T018 [US2] Update `apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php` so the current completion summary identifies subscription-backed versus fallback lifecycle truth without adding direct subscription checks.
- [x] T019 [US2] Update `apps/platform/app/Services/ReviewPackService.php` and any current review-pack start helpers so the existing lifecycle gate consumes the new source but keeps current start semantics unchanged.
- [x] T020 [US2] Review all in-scope gate surfaces and remove any local or duplicate subscription-state checks so lifecycle remains the only runtime gate.
**Checkpoint**: Subscription truth changes the upstream lifecycle source only; runtime gates stay singular and unchanged in shape.
---
## Phase 5: User Story 3 - Show a read-only commercial summary on workspace settings (Priority: P2)
**Goal**: Workspace operators can inspect the current commercial posture without gaining billing controls.
**Independent Test**: Open workspace settings as an authorized member and verify the summary shows subscription-backed or fallback-backed truth, the next relevant date, and no mutation controls.
### Tests for User Story 3
- [x] T021 [P] [US3] Extend `apps/platform/tests/Feature/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.php` to prove the new read-only subscription summary, fallback indicator, and absence of admin-plane mutation controls.
### Implementation for User Story 3
- [x] T022 [US3] Update `apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php` so the existing page renders the read-only subscription summary using the shared resolver output.
- [x] T023 [US3] Confirm the admin-plane summary stays read-only and does not create a second commercial mutation surface or local commercial vocabulary.
**Checkpoint**: Workspace operators can understand the current posture without inheriting a new billing UI.
---
## Phase 6: Polish & Cross-Cutting Validation
**Purpose**: Validate the bounded slice and stop without widening scope.
- [x] T024 [P] Run `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.php`.
- [x] T025 [P] Run `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/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.php`.
- [x] T026 [P] Run `export 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.php`.
- [x] T027 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`.
- [x] T028 [P] Review touched code to confirm Filament stays on Livewire v4, provider registration remains unchanged in `apps/platform/bootstrap/providers.php`, no globally searchable resource is added, no asset strategy changes appear, and no second runtime gate slipped in.
- [x] T029 [P] Record the final guardrail and test-governance outcome in the active feature close-out without reopening provider sync, invoices, portal work, or a second control plane.
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 1 (Setup)**: no dependencies; start immediately.
- **Phase 2 (Foundational)**: depends on Phase 1 and blocks all user stories.
- **Phase 3 (US1)**: depends on Phase 2 and establishes the durable source of truth.
- **Phase 4 (US2)**: depends on Phase 2 and should land with US1 so the new source and current runtime gates stay aligned.
- **Phase 5 (US3)**: depends on Phase 2 and should land after US1 so the admin summary consumes the finished shared resolver.
- **Phase 6 (Polish)**: depends on all desired user stories being complete.
### User Story Dependencies
- **US1 (P1)**: independently testable after Phase 2 and delivers the central source-of-truth surface.
- **US2 (P1)**: independently testable after Phase 2 and should ship with US1 so subscription truth actually changes runtime source.
- **US3 (P2)**: independently testable after Phase 2 and can follow US1 once the shared resolver output is stable.
### Within Each User Story
- Write the listed Pest coverage first and make it fail for the intended gap.
- Keep implementation inside the current model, resolver, system page, settings page, onboarding, review-pack, and audit seams named above.
- Re-run the narrowest relevant validation command after each story checkpoint before moving on.
---
## Implementation Strategy
### Suggested MVP Scope
- MVP = **US1 + US2 together**. The feature is only useful when a current subscription source exists and the existing lifecycle gate actually consumes it.
### Incremental Delivery
1. Complete Phase 1 and Phase 2.
2. Deliver US1 so the durable source and system mutation surface exist.
3. Deliver US2 so current gates become subscription-backed where appropriate.
4. Add US3 as the read-only admin summary.
5. Finish with the focused validation and drift-review tasks in Phase 6.
### Team Strategy
1. Settle persistence and resolver shape first.
2. Parallelize failing tests within each story before runtime edits.
3. Serialize merges around `ViewWorkspace`, `WorkspaceCommercialLifecycleResolver`, and `WorkspaceSettings` so the commercial vocabulary stays coherent.
---
## Deferred Follow-Ups / Non-Goals
- payment-provider or webhook synchronization
- invoice or payment ledger persistence
- customer-facing billing portal or workspace self-serve billing controls
- schedule-driven trial or period-end transitions
- a historical subscription browser or multi-record billing timeline