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

30 KiB

Scratchpad — Client Contract Line Post-Drop Cutover

  • Plan slug: client-contract-line-post-drop-cutover
  • Created: 2026-03-19

What This Is

Keep a lightweight, continuously-updated log of discoveries and decisions made while implementing this plan.

Prefer short bullets. Append new entries as you learn things, and also update earlier notes when a decision changes or an open question is resolved.

Decisions

  • (2026-03-19) Scope includes both runtime fixes and cleanup. The user explicitly requested that the plan cover broader stale code/tests/docs cleanup, not just the immediate AutomaticInvoices crash.
  • (2026-03-19) Treat fully migrated environments as the source of truth. The migration dropping client_contract_lines has already run in the target branch/env, so runtime code must adapt to the surviving structure instead of restoring compatibility with removed tables.
  • (2026-03-19) The plan assumes the intended live structure is: contracts as client-owned header, contract_lines as canonical line obligation, client_contracts as assignment/lifecycle layer.
  • (2026-03-19) The likely post-drop recurring identity target is contract_line_id for the line plus client_contract_id / client_contracts for assignment windowing. If client_contract_line survives at all, it should survive only as compatibility metadata, not as a live storage dependency.
  • (2026-03-19 23:10 EDT) For getAvailableRecurringDueWork(), client-cadence persisted rows can resolve directly through recurring_service_periods -> contract_lines -> contracts.owner_client_id because the invoice/service-period window is already materialized. Materialization-gap detection still needs client_contracts for active assignment windows, but maps contract_line_id back into the compatibility field client_contract_line_id.
  • (2026-03-19 23:26 EDT) Client-cadence schedule regeneration can use the same surviving structure as due-work gap detection: load active obligations from client_contracts -> contracts -> contract_lines, alias contract_line_id to client_contract_line_id, and defer the actual obligation identity migration to the later F014/F015 work.
  • (2026-03-19 23:49 EDT) ContractLineService.validateNoOverlappingAssignments() should validate duplicates against active cloned lines already present on the target client-owned contract, not against a removed per-client line table. After the drop, per-line assignment windows no longer exist independently from the client-owned contract lifecycle.
  • (2026-03-19 23:56 EDT) The resolved post-drop identity rule is: client cadence keeps obligation_type = 'client_contract_line' only as passive compatibility metadata, while the canonical surviving obligation id is always the live contract_line_id. Shared helper functions should build that identity instead of re-encoding it inline.

Discoveries / Constraints

  • (2026-03-19) AutomaticInvoices 500s in a migrated environment because getAvailableRecurringDueWork() still queries client_contract_lines in both persisted due-work loading and materialization-gap detection.
  • (2026-03-19) Recurring preview/generate, recurring service-period inspection, and client-cadence regeneration also still query client_contract_lines.
  • (2026-03-19) Contract wizard comments say client_contract_lines and related tables are redundant, but the create flow still inserts into client_contract_lines and client_contract_services.
  • (2026-03-19) The billing engine has already partially moved to the post-drop model and loads line truth via client_contracts -> contracts -> contract_lines, aliasing contract_line_id back to client_contract_line_id for compatibility.
  • (2026-03-19) Tests currently mask the mismatch. Some tests explicitly mock or assert client_contract_lines as expected live behavior, including a static linkage guard.
  • (2026-03-19) The deeper unresolved design issue is recurring identity: client cadence still widely uses obligation_type = 'client_contract_line' and client_contract_line_id even though the physical table is gone.
  • (2026-03-19) Additional live cleanup targets identified: server/src/lib/api/services/ContractLineService.ts, server/src/lib/reports/definitions/billing/overview.ts, packages/billing/src/actions/creditActions.ts, and a long tail of credit/invoice integration tests that still seed or assert client_contract_lines.
  • (2026-03-19) Stale operational guidance also exists in ee/docs/plans/2026-03-18-service-driven-invoicing-cutover/RUNBOOK.md, which still starts repair SQL from client_contract_lines.
  • (2026-03-19) Low-priority hygiene cleanup remains worthwhile because tracked backup files and teardown-only fixtures still keep the dropped table visible in active engineering flows.
  • (2026-03-20 00:20 EDT) client_contracts.notice_period_days is NOT NULL DEFAULT 30 after the renewal-config migration. The contract wizard create flow must mirror that default instead of inserting null.
  • (2026-03-20 00:24 EDT) Client-cadence selector normalization cannot assume persisted recurring_service_periods.obligation_type = 'client_contract_line'. DB-backed coverage still materializes some client-cadence rows as contract_line, so live selector normalization must accept both post-drop obligation labels while continuing to resolve the surviving contract_line_id.
  • (2026-03-20 00:25 EDT) buildRecurringServicePeriodPeriodKey() was assuming string dates, but Knex can surface period_start_date / period_end_date as Date objects in DB-backed due-work gap detection. The helper now normalizes both strings and Date instances.
  • (2026-03-19 23:10 EDT) The existing due-work reader test harness was flexible enough to simulate a migrated schema by throwing on any attempted client_contract_lines access. That let the first checkpoint verify behavior rather than only string-match source code.
  • (2026-03-19 23:16 EDT) AutomaticInvoices can be validated meaningfully without a database fixture by letting the component call the real getAvailableRecurringDueWork() action against mocked migrated-schema rows. That catches regressions in the UI load path instead of only checking a prebuilt mock payload.
  • (2026-03-19 23:19 EDT) Preview/generate selector normalization for client cadence can follow the same post-drop lookup as due-work loading: recurring_service_periods -> contract_lines -> contracts.owner_client_id. The selector-input path only needs to prove the persisted service-period window belongs to the client; it does not need a dropped assignment row.
  • (2026-03-19 23:21 EDT) Service-period inspection uses the same obligation-context rule: persisted client-cadence rows can resolve display metadata directly from contract_lines -> contracts.owner_client_id. No live inspection path needs a dropped client assignment row once the recurring service period already exists.
  • (2026-03-19 23:26 EDT) packages/billing/src/actions/clientCadenceScheduleRegeneration.ts was the next stale runtime caller. Its query still started from client_contract_lines, and server/src/test/unit/billing/updateClientBillingSchedule.test.ts encoded that exact base table in its fake transaction responses.
  • (2026-03-19 23:29 EDT) Invoice-detail linkage was still widening client-cadence obligation candidates through client_contract_lines. In the post-drop model that lookup is redundant because live client-cadence recurring service periods already use obligation_type = 'client_contract_line' with the surviving contract_line_id as obligation_id.
  • (2026-03-19 23:30 EDT) Bucket period resolution had the same stale assumption: it only needed an active client assignment window plus the canonical contract line and bucket config, but it still started that lookup from client_contract_lines.
  • (2026-03-19 23:35 EDT) Contract wizard create flow was already using surviving contract_lines and client_contracts, but contractWizardActions.ts still carried an unused helper that wrote client_contract_lines, client_contract_services, and related dropped tables. The integration test still asserted those dropped tables as expected output.
  • (2026-03-19 23:39 EDT) applyCreditToInvoice() still contained a pure guard read against client_contract_lines before updating invoice/client balances. That read was not used for business logic and would hard-fail in migrated schemas.
  • (2026-03-19 23:39 EDT) The billing overview report definition still counted active billing clients by joining client_contract_lines, even though the active client-owned model is client_contracts -> contracts -> contract_lines.
  • (2026-03-19 23:49 EDT) server/src/lib/api/services/ContractLineService.ts no longer has live client_contract_lines reads/writes. Unassign, activation, usage metrics, analytics, in-use checks, and overlap validation now all resolve through contract_lines, contracts, and client_contracts.
  • (2026-03-19 23:56 EDT) Client-cadence recurring identity had drifted into several inline string/tuple shims across due-work, regeneration, linkage, bucket resolution, selector normalization, service-period repair, and billing-engine materialization. A shared helper in shared/billingClients/postDropRecurringObligationIdentity.ts was enough to unify those paths without changing persisted compatibility types.
  • (2026-03-19 23:59 EDT) The March 18 service-driven invoicing runbook still contained operator SQL starting from client_contract_lines plus cutover wording that implied a bridge table remained the live client-cadence source. The fix only needed localized wording/SQL edits; the rest of the runbook still applies.
  • (2026-03-20 00:08 EDT) server/src/test/unit/contractLineDisambiguation.test.ts still encoded pre-drop bucket-overlay heuristics after the runtime helper switched to normalized post-drop rows. The stale test only needed mock-shape updates (bucket_overlay.config_id, normalized dates) rather than production code changes.
  • (2026-03-20 00:08 EDT) A targeted static guard over tracked tests was enough to enforce F016: reject positive assertions that require client_contract_lines or client_contract_services to exist/live, while still allowing migrated-schema tests to mention the dropped tables in negative assertions and missing-table harnesses.
  • (2026-03-20 00:14 EDT) server/src/test/unit/billing/recurringInvoiceLinkage.static.test.ts had drifted behind the new helper-based linkage implementation. The assertions needed to move from inline obligation_type/obligation_id string literals to buildPostDropRecurringObligationCandidates(...) plus candidate mapping.
  • (2026-03-20 00:14 EDT) DB-backed integration verification is partially blocked locally because the test Postgres target is not listening on 127.0.0.1:5438 / ::1:5438. contractWizard.integration.test.ts currently skips/fails at suite setup before exercising migrated-schema behavior.

Commands / Runbooks

  • (2026-03-19) Reproduce current failure:
    • open /msp/billing?tab=invoicing&subtab=generate
    • observe relation "client_contract_lines" does not exist
  • (2026-03-19) Find live dropped-table references:
    • rg -n "client_contract_lines|client_contract_services|client_contract_line_pricing|client_contract_line_discounts" packages/billing/src server/src/lib -g '*.ts' -g '*.tsx'
  • (2026-03-19) Primary runtime files identified:
    • packages/billing/src/actions/billingAndTax.ts
    • packages/billing/src/actions/invoiceGeneration.ts
    • packages/billing/src/actions/recurringServicePeriodActions.ts
    • packages/billing/src/actions/clientCadenceScheduleRegeneration.ts
    • packages/billing/src/services/invoiceService.ts
    • packages/billing/src/services/bucketUsageService.ts
    • packages/billing/src/actions/contractWizardActions.ts
  • (2026-03-19) Additional cleanup inventory from agent sweep:
    • live server/service cleanup:
      • server/src/lib/api/services/ContractLineService.ts
      • server/src/lib/reports/definitions/billing/overview.ts
      • packages/billing/src/actions/creditActions.ts
    • stale tests to rewrite:
      • server/src/test/integration/contractWizard.integration.test.ts
      • server/src/test/unit/billing/recurringInvoiceLinkage.static.test.ts
      • server/src/test/unit/billing/recurringDueWorkReader.integration.test.ts
      • server/src/test/unit/billing/updateClientBillingSchedule.test.ts
      • server/src/test/unit/billing/bucketUsageService.periods.test.ts
      • server/src/test/unit/billing/invoiceService.fixedPersistence.test.ts
      • multiple credit and invoice integration tests under server/src/test/infrastructure/billing
    • docs/runbook/hygiene cleanup:
      • ee/docs/plans/2026-03-18-service-driven-invoicing-cutover/RUNBOOK.md
      • tracked backups server/src/test/infrastructure/billing/invoices/billingInvoiceGeneration_tax.test.ts.bak*
  • (2026-03-19 23:10 EDT) Verification for F001/F002/T001/T002/T003:
    • pnpm exec vitest run src/test/unit/billing/recurringDueWorkReader.integration.test.ts (run from server/)
  • (2026-03-19 23:16 EDT) Verification for F003/T004:
    • pnpm exec vitest run src/test/unit/billing/recurringDueWorkReader.integration.test.ts src/test/unit/billing/automaticInvoices.recurringDueWork.ui.test.tsx (run from server/)
  • (2026-03-19 23:19 EDT) Verification for F004/F005/T005/T006/T007:
    • pnpm exec vitest run src/test/unit/billing/invoiceGeneration.preview.test.ts src/test/unit/billing/invoiceGeneration.selectorInputGenerate.test.ts (run from server/)
  • (2026-03-19 23:21 EDT) Verification for F006/T008:
    • pnpm exec vitest run src/test/unit/billing/recurringServicePeriodActions.test.ts (run from server/)
  • (2026-03-19 23:26 EDT) Verification for F007/T009:
    • pnpm exec vitest run src/test/unit/billing/updateClientBillingSchedule.test.ts (run from server/)
  • (2026-03-19 23:29 EDT) Verification for F008 linkage cleanup:
    • pnpm exec vitest run src/test/unit/billing/invoiceService.fixedPersistence.test.ts src/test/unit/billing/recurringInvoiceLinkage.static.test.ts (run from server/)
  • (2026-03-19 23:30 EDT) Verification for F009 bucket period resolution:
    • pnpm exec vitest run src/test/unit/billing/bucketUsageService.periods.test.ts (run from server/)
  • (2026-03-19 23:35 EDT) Verification for F010 contract wizard cleanup:
    • pnpm exec vitest run src/test/unit/billing/contractWizard.postDrop.static.test.ts (run from server/)
    • pnpm exec vitest run src/test/integration/contractWizard.integration.test.ts failed locally with ECONNREFUSED to 127.0.0.1:5438 / ::1:5438 because the DB-backed test Postgres was not available.
  • (2026-03-19 23:39 EDT) Verification for F012/F013:
    • pnpm exec vitest run src/test/unit/billing/creditActions.applyCredit.postDrop.test.ts src/test/unit/billing/billingOverviewReport.postDrop.static.test.ts (run from server/)
  • (2026-03-19 23:49 EDT) Verification for F011/F019:
    • pnpm exec vitest run src/test/unit/api/contractLineService.clientOwnedMutation.test.ts src/test/unit/api/contractLineService.postDrop.static.test.ts (run from server/)
  • (2026-03-19 23:56 EDT) Verification for F014/F015:
    • pnpm exec vitest run src/test/unit/billing/postDropRecurringObligationIdentity.test.ts src/test/unit/billing/recurringDueWorkReader.integration.test.ts src/test/unit/billing/updateClientBillingSchedule.test.ts src/test/unit/billing/invoiceService.fixedPersistence.test.ts src/test/unit/billing/bucketUsageService.periods.test.ts src/test/unit/billing/invoiceGeneration.preview.test.ts src/test/unit/billing/invoiceGeneration.selectorInputGenerate.test.ts src/test/unit/billing/recurringServicePeriodActions.test.ts src/test/unit/api/contractLineService.clientOwnedMutation.test.ts src/test/unit/api/contractLineService.postDrop.static.test.ts (run from server/)
  • (2026-03-19 23:59 EDT) Verification for F017 doc cleanup:
    • pnpm exec vitest run src/test/unit/docs/clientContractLinePostDropHygiene.test.ts (run from server/)
  • (2026-03-20 00:08 EDT) Verification for F016/F020/T022/T023/T028:
    • pnpm exec vitest run src/test/unit/contractLineDisambiguation.test.ts src/test/unit/docs/clientContractLinePostDropHygiene.test.ts src/test/unit/docs/clientContractLineTestAssertions.static.test.ts (run from server/)
  • (2026-03-20 00:12 EDT) Verification for T012/T016/T017/T018/T019/T020/T021/T027:
    • pnpm exec vitest run src/test/unit/billing/clientContractLineRuntimeSourceGuards.static.test.ts src/test/unit/billing/contractWizard.postDrop.static.test.ts src/test/unit/billing/creditActions.applyCredit.postDrop.test.ts src/test/unit/billing/billingOverviewReport.postDrop.static.test.ts src/test/unit/billing/postDropRecurringObligationIdentity.test.ts src/test/unit/billing/bucketUsageService.periods.test.ts src/test/unit/api/contractLineService.clientOwnedMutation.test.ts src/test/unit/api/contractLineService.postDrop.static.test.ts (run from server/)
  • (2026-03-20 00:13 EDT) Verification for T011/T026:
    • pnpm exec vitest run src/test/unit/billing/recurringInvoiceLinkage.static.test.ts (run from server/)
    • pnpm exec vitest run --coverage.enabled=false src/test/unit/billingEngine.test.ts -t "resolves preserved and cloned assignment lines from each assignment contract after migration" (run from server/)
  • (2026-03-20 00:12 EDT) Blocked DB-backed verification:
    • pnpm exec vitest run src/test/integration/contractWizard.integration.test.ts (run from server/) -> fails connecting to Postgres on port 5438
  • (2026-03-20 00:23 EDT) DB-backed verification after discovering the active local Postgres listener on 127.0.0.1:57433:
    • DB_HOST=127.0.0.1 DB_PORT=57433 DB_USER_ADMIN=postgres DB_PASSWORD_ADMIN=postpass123 DB_USER_SERVER=app_user DB_PASSWORD_SERVER=postpass123 pnpm exec vitest run --coverage.enabled=false src/test/integration/contractWizard.integration.test.ts (run from server/)
  • (2026-03-20 00:31 EDT) Final DB-backed recurring verification:
    • DB_HOST=127.0.0.1 DB_PORT=57433 DB_USER_ADMIN=postgres DB_PASSWORD_ADMIN=postpass123 DB_USER_SERVER=app_user DB_PASSWORD_SERVER=postpass123 pnpm exec vitest run --coverage.enabled=false src/test/integration/billingInvoiceTiming.integration.test.ts -t "T033/T078|T034/T079|T085|T017/T019/T050/T077/T080/T084|T080: mixed batch generation from AutomaticInvoices" (run from server/)
    • pnpm exec vitest run --coverage.enabled=false src/test/unit/billing/invoiceService.fixedPersistence.test.ts -t "canonical client-contract-line identity|canonical contract-line identity" (run from server/)

Completed Items

  • (2026-03-19 23:10 EDT) Completed F001: persisted client-cadence due-work loading no longer joins client_contract_lines; the reader resolves contract/client metadata via surviving contract_lines and contracts.owner_client_id.
  • (2026-03-19 23:10 EDT) Completed F002: client-cadence materialization-gap detection now loads active recurring obligations from client_contracts -> contracts -> contract_lines, preserving the legacy client_contract_line logical identity only as compatibility metadata.
  • (2026-03-19 23:10 EDT) Completed T001/T002/T003: due-work reader coverage now simulates a fully migrated schema by failing on any client_contract_lines access while still verifying persisted client cadence, persisted contract cadence, and client-cadence materialization gaps.
  • (2026-03-19 23:16 EDT) Completed F003: AutomaticInvoices can load recurring due work in a simulated migrated schema with no client_contract_lines table because its server action path no longer depends on that table for due-work loading.
  • (2026-03-19 23:16 EDT) Completed T004: the UI test now renders AutomaticInvoices while the real due-work action runs against mocked migrated-schema data and a hard failure on any client_contract_lines access.
  • (2026-03-19 23:19 EDT) Completed F004/F005: selector-input preview/generate normalization for client-cadence windows no longer joins client_contract_lines; it resolves persisted windows through surviving contract_lines plus contracts.owner_client_id.
  • (2026-03-19 23:19 EDT) Completed T005/T006/T007: preview and generation coverage now explicitly fails on any client_contract_lines access while verifying client-cadence selector-input preview, client-cadence selector-input generation, and contract-cadence regression behavior.
  • (2026-03-19 23:21 EDT) Completed F006: recurring service-period management view resolves client-cadence obligation metadata from surviving contract-owned structures instead of client_contract_lines.
  • (2026-03-19 23:21 EDT) Completed T008: service-period management view coverage now simulates a migrated schema by failing on any client_contract_lines access while still returning client/contract/line context for a client-cadence schedule.
  • (2026-03-19 23:26 EDT) Completed F007: client-cadence schedule regeneration now loads active recurring obligations from client_contracts -> contracts -> contract_lines and aliases the surviving contract_line_id back to client_contract_line_id for compatibility, removing the dropped-table dependency from billing-schedule changes.
  • (2026-03-19 23:26 EDT) Completed T009: billing-schedule regeneration coverage now seeds client_contracts as cc, treats client_contract_lines as missing, and still verifies regenerated recurring service periods are materialized and superseded correctly.
  • (2026-03-19 23:29 EDT) Completed F008: invoice-detail linkage no longer queries client_contract_lines to derive client-cadence recurring obligation candidates. The live path now matches recurring service periods against the surviving contract_line_id for both contract_line and compatibility client_contract_line obligation types.
  • (2026-03-19 23:30 EDT) Completed F009: bucket recurring period resolution now finds active client-cadence obligations through client_contracts -> contracts -> contract_lines, preserving compatibility-only client_contract_line obligation typing while using the surviving contract_line_id as the recurring obligation id.
  • (2026-03-19 23:35 EDT) Completed F010: removed the dead per-client line replication helper from contractWizardActions.ts and rewrote contract wizard coverage to validate the surviving client_contracts, contract_lines, and contract_line_service_* structures instead of the dropped client_contract_lines / client_contract_services tables.
  • (2026-03-19 23:39 EDT) Completed F012: credit application no longer queries client_contract_lines before applying client credit to an invoice. The live path now updates credit balances directly from invoice/client/credit-tracking state, which already contains the required domain information.
  • (2026-03-19 23:39 EDT) Completed F013: the billing overview report definition now derives active billing clients from client_contracts -> contracts -> contract_lines instead of treating client_contract_lines as a live fact source.
  • (2026-03-19 23:49 EDT) Completed F011: ContractLineService live mutation/runtime paths no longer require client_contract_lines. Client-owned unassign/deactivate behavior now updates canonical contract_lines, and duplicate assignment checks are scoped to the surviving client-owned contract structure.
  • (2026-03-19 23:49 EDT) Completed F019: server-side contract-line service behaviors for unassign/deactivate, usage analytics, overview counts, in-use checks, and overlap validation no longer query client_contract_lines; they now join surviving contract_lines -> contracts -> client_contracts instead.
  • (2026-03-19 23:56 EDT) Completed F014: defined a shared post-drop recurring obligation helper (shared/billingClients/postDropRecurringObligationIdentity.ts) and rewired due-work, regeneration, selector normalization, linkage, bucket resolution, service-period inspection, and billing-engine settlement code to use that canonical identity rule instead of inline string shims.
  • (2026-03-19 23:56 EDT) Completed F015: the retained client_contract_line identity is now explicitly compatibility-only. Shared helpers document that it always points at the surviving contract_line_id, and live runtime paths consume that helper instead of implying a backing client_contract_lines table.
  • (2026-03-19 23:59 EDT) Completed F017: corrected the active runbook under ee/docs/plans/2026-03-18-service-driven-invoicing-cutover/RUNBOOK.md so recurring repair guidance now starts from client_contracts -> contracts -> contract_lines and describes post-drop client cadence without implying client_contract_lines is still a live runtime dependency.
  • (2026-03-20 00:08 EDT) Completed F016: updated stale unit/integration test coverage so post-drop contract-line helpers and test-suite guards no longer treat client_contract_lines or client_contract_services as expected live runtime structures.
  • (2026-03-20 00:08 EDT) Completed F020: removed tracked backup artifacts, corrected cleanup-only integration fixtures/teardowns, and added hygiene assertions so active development/runbook flows no longer point engineers back at dropped client-contract line tables.
  • (2026-03-20 00:14 EDT) Completed T012: bucket period resolution is guarded by bucketUsageService.periods.test.ts, which throws immediately if client_contract_lines is queried and verifies client-cadence periods still resolve through recurring service periods.
  • (2026-03-20 00:14 EDT) Completed T015 and T027: contractLineService.clientOwnedMutation.test.ts plus contractLineService.postDrop.static.test.ts cover live client-owned mutation paths and enforce that unassign/deactivate, overlap validation, analytics/in-use/overview source wiring no longer query client_contract_lines.
  • (2026-03-20 00:14 EDT) Completed T016: creditActions.applyCredit.postDrop.test.ts proves client credit application succeeds when client_contract_lines throws as missing.
  • (2026-03-20 00:14 EDT) Completed T017: billingOverviewReport.postDrop.static.test.ts now enforces the report definition's active-client metric joins client_contracts -> contracts -> contract_lines instead of client_contract_lines.
  • (2026-03-20 00:14 EDT) Completed T018: postDropRecurringObligationIdentity.test.ts guards the shared helper and verifies due-work, regeneration, linkage, bucket resolution, and service-period paths all consume the same canonical post-drop identity helper.
  • (2026-03-20 00:14 EDT) Completed T019 and T020: clientContractLineRuntimeSourceGuards.static.test.ts scans tracked billing actions/services and rejects any runtime query-like use of dropped client-contract line tables.
  • (2026-03-20 00:14 EDT) Completed T021: contractWizard.postDrop.static.test.ts enforces that client contract creation no longer inserts into client_contract_lines or client_contract_services.
  • (2026-03-20 00:14 EDT) Completed T026: the focused billingEngine.test.ts migration regression proves client-owned contract loading still returns preserved and cloned assignment lines through the current client_contracts -> contracts -> contract_lines runtime shape.
  • (2026-03-20 00:31 EDT) Completed T013 and T014: contractWizard.integration.test.ts now passes against a fully migrated schema with no client_contract_lines / client_contract_services tables. The test harness was updated for the current auth/tenant seams, and the runtime create path now falls back to migration-aligned defaults for renewal_mode (manual) and notice_period_days (30).
  • (2026-03-20 00:31 EDT) Completed T010: DB-backed reverse/delete repair coverage (T033/T078, T034/T079, T085) proves recurring invoice reversal and hard-delete reopen linked service periods without relying on dropped client-line tables.
  • (2026-03-20 00:31 EDT) Completed T011: DB-backed client-cadence linkage coverage (T029) plus invoiceService.fixedPersistence.test.ts now verify billed recurring detail rows backfill through the canonical surviving contract_line_id, accepting both post-drop client-cadence obligation labels without querying client_contract_lines.
  • (2026-03-20 00:31 EDT) Completed T024: the mixed AutomaticInvoices batch happy path now materializes both client- and contract-cadence service periods before selection, and DB-backed generation succeeds from the canonical selector input in a schema where the dropped client-line tables are absent.
  • (2026-03-20 00:31 EDT) Completed T025: contract-cadence preview, generation, and recurring history remain functional after the post-drop cleanup, with selector-input execution windows and persisted service periods staying coherent without a required billing_cycle_id.
  • (2026-03-20 00:31 EDT) Completed F018: after widening client-cadence selector normalization to both post-drop obligation labels and hardening recurring period-key normalization for DB date objects, the remaining DB-backed billing/runtime paths exercised by contract wizard creation, recurring repair, and AutomaticInvoices no longer crash in a fully migrated schema without client_contract_lines.
  • ee/docs/plans/2026-03-16-client-owned-contracts-simplification/PRD.md
  • ee/docs/plans/2026-03-18-service-driven-invoicing-cutover/PRD.md
  • ee/docs/plans/2026-03-18-recurring-invoicing-hard-cutover/PRD.md
  • server/migrations/20251207140000_drop_redundant_client_contract_tables.cjs
  • packages/billing/src/lib/billing/billingEngine.ts
  • packages/billing/src/actions/billingAndTax.ts
  • packages/billing/src/actions/invoiceGeneration.ts
  • server/src/test/unit/billing/recurringInvoiceLinkage.static.test.ts
  • server/src/test/unit/billing/recurringDueWorkReader.integration.test.ts
  • server/src/lib/api/services/ContractLineService.ts
  • server/src/lib/reports/definitions/billing/overview.ts
  • ee/docs/plans/2026-03-18-service-driven-invoicing-cutover/RUNBOOK.md

Open Questions

  • Should client-cadence recurring obligations migrate fully to obligation_type = 'contract_line', or is a logical client_contract_line identity still required as compatibility metadata?
  • Which deprecated server/package services should be fixed in this plan versus deferred once live billing/runtime paths are stable?
  • Which historical plans/design notes should remain untouched as historical artifacts, and which still influence operator or engineer behavior enough that they need explicit correction now?