Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

4.0 KiB

Recurring Invoicing Hard Cutover Architecture Notes

Final Invariant

Recurring invoice execution identity is canonical service-period or execution-window identity only.

In steady state:

  1. cadence ownership and source rules generate persisted recurring_service_periods
  2. ready recurring work is read only from persisted recurring_service_periods
  3. preview, generate, run, history, reverse, and delete flows identify recurring work by canonical execution-window or service-period identity
  4. billing_cycle_id is never required to discover recurring work, prevent duplicates, explain recurring history, or repair recurring invoice linkage

Canonical Runtime Model

The hard cutover keeps a single recurring model in application code:

  • recurring_service_periods is the recurring ledger and the only ready-work substrate
  • recurring due rows, run targets, and retry identity are derived from canonical schedule or service-period identity
  • missing materialization is an explicit repairable failure, not a compatibility invoice row
  • history and reversal operate on invoice-linked recurring service-period records, not billing-cycle handles

Retained Role Of client_billing_cycles

client_billing_cycles remain valid product data, but only in these roles:

  • client cadence administration and anchor management
  • source-rule input when cadence_owner = client
  • optional historical or read-side context when an older invoice still carries bridge metadata

client_billing_cycles do not remain valid in these roles:

  • recurring due-work substrate
  • recurring execution identity
  • duplicate-prevention key for recurring invoices
  • primary object for recurring history, reverse, delete, preview, or generate operations

Required Schema Posture

The hard cutover treats recurring-service-period storage as required schema, not rollout-era optional schema.

  • recurring invoice paths must assume recurring_service_periods and related linkage structures exist
  • missing service-period rows are diagnosed as data repair work
  • code must not catch missing table or missing column errors to rebuild recurring work from client_billing_cycles

invoices.billing_cycle_id Deprecation Posture

The current deprecation posture is:

  • invoices.billing_cycle_id may remain physically present for now
  • it is passive historical or client-context metadata only
  • no live recurring path may use it to decide what recurring work exists, whether it is duplicate, how it is previewed or generated, or how recurring linkage is repaired
  • later physical removal can happen after historical read-side cleanup is complete, but live recurring code must already behave as though the bridge is gone

Historical Read-Side Strategy

Historical invoices may still lack complete canonical recurring linkage. The hard cutover handles that only on the read side:

  • when canonical recurring detail periods exist, treat the invoice as canonical_recurring
  • when canonical detail is absent but the invoice must remain readable, treat it as financial_document_fallback
  • when related source invoice context cannot be resolved at all, surface missing_source_context

This is a migration/read model strategy, not a live recurring execution mode:

  • do not synthesize new recurring due work from client_billing_cycles
  • do not rebuild live recurring identity from invoice-header billing_cycle_id
  • do not backfill fake recurring service periods just to make historical invoices look canonical
  • use fallback states only to keep historical reads, exports, and lineage views diagnosable while cleanup/backfill work proceeds separately

Anti-Regression Rules

Future recurring changes should preserve these boundaries:

  • shared recurring execution contracts do not require billingCycleId
  • recurring UI copy talks about service periods or execution windows, not invoiced billing cycles
  • exports and invoice reads derive recurring semantics from canonical recurring detail and service-period data
  • tests should prove client-cadence and contract-cadence recurring work remain operable without a billing-cycle bridge