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

5.2 KiB

Recurring Service-Period Provenance

Purpose

F234 defines the authoritative provenance model for persisted recurring service-period records.

The goal is not just to label a row as "generated" or "edited". The persisted provenance payload must also explain:

  • whether the current row still matches generated cadence rules or now diverges from them
  • why the row exists in its current shape
  • whether it replaced an earlier persisted record version
  • whether a background materialization/regeneration run produced it or whether the change came from an explicit operator action

This keeps later edit, regeneration, repair, and support tooling work on one shared provenance vocabulary instead of ad hoc strings.

Authoritative Type Surface

The shared provenance contract now lives in:

  • packages/types/src/interfaces/recurringTiming.interfaces.ts
    • RECURRING_SERVICE_PERIOD_PROVENANCE_REASON_CODES
    • GeneratedRecurringServicePeriodReasonCode
    • UserEditedRecurringServicePeriodReasonCode
    • RegeneratedRecurringServicePeriodReasonCode
    • RepairRecurringServicePeriodReasonCode
    • IRecurringServicePeriodRecordProvenance
  • shared/billingClients/recurringServicePeriodProvenance.ts
    • isRecurringServicePeriodProvenanceReasonCode(...)
    • isRecurringServicePeriodProvenanceDivergent(...)
    • validateRecurringServicePeriodProvenance(...)

The type is intentionally discriminated by kind so field requirements cannot drift silently.

Provenance Kinds

Kind Meaning Diverges from source cadence rules?
generated First materialized version produced directly from current source rules. No
user_edited Operator changed a future period explicitly and the new row supersedes the generated or previously edited row. Yes
regenerated System replaced a prior future row because source recurrence rules or cadence inputs changed. Yes
repair Administrative or integrity repair row used to correct a broken ledger state explicitly. Usually yes; treat as exceptional.

isRecurringServicePeriodProvenanceDivergent(...) is the v1 helper for this policy: every kind except generated represents a row that no longer reflects untouched rule-derived cadence output.

Field Requirements By Kind

Kind reasonCode sourceRunKey supersedesRecordId
generated Required. Must be one of the generated reason codes. Required. Materialization should remain traceable to the generating run. Must be null/absent. The first generated row does not supersede anything.
user_edited Required. Must explain the operator-visible divergence. Optional. A user edit may happen outside the background generation run path. Required. An edit must point at the row it replaced.
regenerated Required. Must explain which rule/input change forced replacement. Required. Regeneration must stay traceable to the background run or explicit rebuild action. Required. Regeneration replaces an earlier future row.
repair Required. Must explain the repair class. Optional. A repair may or may not be tied to a formal run. Optional. Some repairs replace a prior row; others only correct metadata in place through a new revision.

The shared validator enforces the same baseline literally:

  • Generated provenance requires sourceRunKey
  • Generated provenance must not supersede an earlier record
  • User-edited provenance requires supersedesRecordId
  • Regenerated provenance requires sourceRunKey
  • Regenerated provenance requires supersedesRecordId

Reason-Code Catalog

generated

  • initial_materialization
  • backfill_materialization

user_edited

  • boundary_adjustment
  • invoice_window_adjustment
  • activity_window_adjustment
  • skip
  • defer

regenerated

  • source_rule_changed
  • billing_schedule_changed
  • cadence_owner_changed
  • activity_window_changed
  • backfill_realignment

repair

  • integrity_repair
  • invoice_linkage_repair
  • admin_correction

These reason codes are deliberately narrow. They explain why a persisted row differs without yet committing v1 to richer audit payloads or UI verb taxonomies that belong in later edit and permissions work.

Operational Meaning

The persisted provenance contract now answers the first support-grade questions directly:

  • Did the row come from untouched rule output or from a later override?
  • If it changed, was the change user-authored, regeneration-driven, or repair-driven?
  • What class of change happened?
  • Which prior row did this one supersede?
  • Which run key generated or regenerated it when a background process was involved?

This is the minimum v1 provenance needed before:

  • F238-F239 regeneration behavior
  • F245-F249 edit and conflict semantics
  • F253 permissions/audit requirements
  • F268 operator runbook work

Deliberate Non-Goals For F234

This checkpoint does not yet define:

  • actor identity, approval metadata, or full audit payloads for user edits
  • the UI verbs or form contracts for editing future periods
  • repair authorization policy
  • whether repairs are always new revisions versus metadata corrections on existing rows

Those remain sequenced behind the later edit, permission, and runbook passes.