PSA/ee/docs/plans/2026-03-16-service-period-first-billing-and-cadence-ownership/RECURRING_SERVICE_PERIOD_PARITY_COMPARISON.md
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

2.1 KiB

Recurring Service-Period Parity Comparison

F243 defines how staged rollout compares legacy derived recurring timing outputs to the persisted recurring service-period schedule.

This comparison is schedule parity, not invoice parity:

  • it compares recurring service periods plus their due invoice windows
  • it ignores persisted-record-only metadata such as provenance, revision ids, and invoice linkage
  • it exists to prove that materialized schedules still match legacy client-cadence behavior before runtime cutover

Normalized Identity

Parity comparison normalizes both sides onto the same identity:

  • scheduleKey
  • periodKey

The canonical helpers live in shared/billingClients/recurringServicePeriodKeys.ts.

Derived service periods build those keys from:

  • tenant
  • source obligation type / id
  • cadence owner
  • due position
  • canonical [servicePeriod.start, servicePeriod.end) bounds

Persisted rows use the stored scheduleKey and periodKey directly.

Drift Types

shared/billingClients/recurringServicePeriodParity.ts reports three explicit drift kinds:

  • missing_persisted_period
  • unexpected_persisted_period
  • invoice_window_mismatch

Those are the schedule-level rollout questions that matter before the runtime cutover:

  • did materialization miss a service period the legacy engine still derives?
  • did materialization create a service period the legacy engine would not derive?
  • did both sides agree on the service period but disagree on when it becomes due?

Comparison Scope

The default comparison scope includes active schedule rows only:

  • generated
  • edited
  • locked
  • billed

superseded and archived rows are excluded from default parity checks because they represent historical or replaced schedule state, not the active schedule being validated for rollout.

Deliberate Boundary

This checkpoint still does not:

  • backfill historical billed coverage into persisted rows
  • switch live invoice generation to the persisted ledger
  • compare full invoice outputs or tax/discount subtotals at the persisted-schedule layer

Those remain sequenced behind F244 and F256.