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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
30 KiB
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
AutomaticInvoicescrash. - (2026-03-19) Treat fully migrated environments as the source of truth. The migration dropping
client_contract_lineshas 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:
contractsas client-owned header,contract_linesas canonical line obligation,client_contractsas assignment/lifecycle layer. - (2026-03-19) The likely post-drop recurring identity target is
contract_line_idfor the line plusclient_contract_id/client_contractsfor assignment windowing. Ifclient_contract_linesurvives 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 throughrecurring_service_periods -> contract_lines -> contracts.owner_client_idbecause the invoice/service-period window is already materialized. Materialization-gap detection still needsclient_contractsfor active assignment windows, but mapscontract_line_idback into the compatibility fieldclient_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, aliascontract_line_idtoclient_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 livecontract_line_id. Shared helper functions should build that identity instead of re-encoding it inline.
Discoveries / Constraints
- (2026-03-19)
AutomaticInvoices500s in a migrated environment becausegetAvailableRecurringDueWork()still queriesclient_contract_linesin 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_linesand related tables are redundant, but the create flow still inserts intoclient_contract_linesandclient_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, aliasingcontract_line_idback toclient_contract_line_idfor compatibility. - (2026-03-19) Tests currently mask the mismatch. Some tests explicitly mock or assert
client_contract_linesas 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'andclient_contract_line_ideven 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 assertclient_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 fromclient_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_daysisNOT NULL DEFAULT 30after the renewal-config migration. The contract wizard create flow must mirror that default instead of insertingnull. - (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 ascontract_line, so live selector normalization must accept both post-drop obligation labels while continuing to resolve the survivingcontract_line_id. - (2026-03-20 00:25 EDT)
buildRecurringServicePeriodPeriodKey()was assuming string dates, but Knex can surfaceperiod_start_date/period_end_dateasDateobjects in DB-backed due-work gap detection. The helper now normalizes both strings andDateinstances. - (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_linesaccess. That let the first checkpoint verify behavior rather than only string-match source code. - (2026-03-19 23:16 EDT)
AutomaticInvoicescan be validated meaningfully without a database fixture by letting the component call the realgetAvailableRecurringDueWork()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.tswas the next stale runtime caller. Its query still started fromclient_contract_lines, andserver/src/test/unit/billing/updateClientBillingSchedule.test.tsencoded 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 useobligation_type = 'client_contract_line'with the survivingcontract_line_idasobligation_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_linesandclient_contracts, butcontractWizardActions.tsstill carried an unused helper that wroteclient_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 againstclient_contract_linesbefore 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 isclient_contracts -> contracts -> contract_lines. - (2026-03-19 23:49 EDT)
server/src/lib/api/services/ContractLineService.tsno longer has liveclient_contract_linesreads/writes. Unassign, activation, usage metrics, analytics, in-use checks, and overlap validation now all resolve throughcontract_lines,contracts, andclient_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.tswas 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_linesplus 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.tsstill 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 requireclient_contract_linesorclient_contract_servicesto 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.tshad drifted behind the new helper-based linkage implementation. The assertions needed to move from inlineobligation_type/obligation_idstring literals tobuildPostDropRecurringObligationCandidates(...)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.tscurrently 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
- open
- (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.tspackages/billing/src/actions/invoiceGeneration.tspackages/billing/src/actions/recurringServicePeriodActions.tspackages/billing/src/actions/clientCadenceScheduleRegeneration.tspackages/billing/src/services/invoiceService.tspackages/billing/src/services/bucketUsageService.tspackages/billing/src/actions/contractWizardActions.ts
- (2026-03-19) Additional cleanup inventory from agent sweep:
- live server/service cleanup:
server/src/lib/api/services/ContractLineService.tsserver/src/lib/reports/definitions/billing/overview.tspackages/billing/src/actions/creditActions.ts
- stale tests to rewrite:
server/src/test/integration/contractWizard.integration.test.tsserver/src/test/unit/billing/recurringInvoiceLinkage.static.test.tsserver/src/test/unit/billing/recurringDueWorkReader.integration.test.tsserver/src/test/unit/billing/updateClientBillingSchedule.test.tsserver/src/test/unit/billing/bucketUsageService.periods.test.tsserver/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*
- live server/service cleanup:
- (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 fromserver/)
- (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 fromserver/)
- (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 fromserver/)
- (2026-03-19 23:21 EDT) Verification for F006/T008:
pnpm exec vitest run src/test/unit/billing/recurringServicePeriodActions.test.ts(run fromserver/)
- (2026-03-19 23:26 EDT) Verification for F007/T009:
pnpm exec vitest run src/test/unit/billing/updateClientBillingSchedule.test.ts(run fromserver/)
- (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 fromserver/)
- (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 fromserver/)
- (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 fromserver/)pnpm exec vitest run src/test/integration/contractWizard.integration.test.tsfailed locally withECONNREFUSEDto127.0.0.1:5438/::1:5438because 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 fromserver/)
- (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 fromserver/)
- (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 fromserver/)
- (2026-03-19 23:59 EDT) Verification for F017 doc cleanup:
pnpm exec vitest run src/test/unit/docs/clientContractLinePostDropHygiene.test.ts(run fromserver/)
- (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 fromserver/)
- (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 fromserver/)
- (2026-03-20 00:13 EDT) Verification for T011/T026:
pnpm exec vitest run src/test/unit/billing/recurringInvoiceLinkage.static.test.ts(run fromserver/)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 fromserver/)
- (2026-03-20 00:12 EDT) Blocked DB-backed verification:
pnpm exec vitest run src/test/integration/contractWizard.integration.test.ts(run fromserver/) -> fails connecting to Postgres on port5438
- (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 fromserver/)
- (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 fromserver/)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 fromserver/)
Completed Items
- (2026-03-19 23:10 EDT) Completed
F001: persisted client-cadence due-work loading no longer joinsclient_contract_lines; the reader resolves contract/client metadata via survivingcontract_linesandcontracts.owner_client_id. - (2026-03-19 23:10 EDT) Completed
F002: client-cadence materialization-gap detection now loads active recurring obligations fromclient_contracts -> contracts -> contract_lines, preserving the legacyclient_contract_linelogical 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 anyclient_contract_linesaccess while still verifying persisted client cadence, persisted contract cadence, and client-cadence materialization gaps. - (2026-03-19 23:16 EDT) Completed
F003:AutomaticInvoicescan load recurring due work in a simulated migrated schema with noclient_contract_linestable 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 rendersAutomaticInvoiceswhile the real due-work action runs against mocked migrated-schema data and a hard failure on anyclient_contract_linesaccess. - (2026-03-19 23:19 EDT) Completed
F004/F005: selector-input preview/generate normalization for client-cadence windows no longer joinsclient_contract_lines; it resolves persisted windows through survivingcontract_linespluscontracts.owner_client_id. - (2026-03-19 23:19 EDT) Completed
T005/T006/T007: preview and generation coverage now explicitly fails on anyclient_contract_linesaccess 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 ofclient_contract_lines. - (2026-03-19 23:21 EDT) Completed
T008: service-period management view coverage now simulates a migrated schema by failing on anyclient_contract_linesaccess 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 fromclient_contracts -> contracts -> contract_linesand aliases the survivingcontract_line_idback toclient_contract_line_idfor compatibility, removing the dropped-table dependency from billing-schedule changes. - (2026-03-19 23:26 EDT) Completed
T009: billing-schedule regeneration coverage now seedsclient_contracts as cc, treatsclient_contract_linesas 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 queriesclient_contract_linesto derive client-cadence recurring obligation candidates. The live path now matches recurring service periods against the survivingcontract_line_idfor bothcontract_lineand compatibilityclient_contract_lineobligation types. - (2026-03-19 23:30 EDT) Completed
F009: bucket recurring period resolution now finds active client-cadence obligations throughclient_contracts -> contracts -> contract_lines, preserving compatibility-onlyclient_contract_lineobligation typing while using the survivingcontract_line_idas the recurring obligation id. - (2026-03-19 23:35 EDT) Completed
F010: removed the dead per-client line replication helper fromcontractWizardActions.tsand rewrote contract wizard coverage to validate the survivingclient_contracts,contract_lines, andcontract_line_service_*structures instead of the droppedclient_contract_lines/client_contract_servicestables. - (2026-03-19 23:39 EDT) Completed
F012: credit application no longer queriesclient_contract_linesbefore 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 fromclient_contracts -> contracts -> contract_linesinstead of treatingclient_contract_linesas a live fact source. - (2026-03-19 23:49 EDT) Completed
F011:ContractLineServicelive mutation/runtime paths no longer requireclient_contract_lines. Client-owned unassign/deactivate behavior now updates canonicalcontract_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 queryclient_contract_lines; they now join survivingcontract_lines -> contracts -> client_contractsinstead. - (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 retainedclient_contract_lineidentity is now explicitly compatibility-only. Shared helpers document that it always points at the survivingcontract_line_id, and live runtime paths consume that helper instead of implying a backingclient_contract_linestable. - (2026-03-19 23:59 EDT) Completed
F017: corrected the active runbook underee/docs/plans/2026-03-18-service-driven-invoicing-cutover/RUNBOOK.mdso recurring repair guidance now starts fromclient_contracts -> contracts -> contract_linesand describes post-drop client cadence without implyingclient_contract_linesis 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 treatclient_contract_linesorclient_contract_servicesas 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 bybucketUsageService.periods.test.ts, which throws immediately ifclient_contract_linesis queried and verifies client-cadence periods still resolve through recurring service periods. - (2026-03-20 00:14 EDT) Completed
T015andT027:contractLineService.clientOwnedMutation.test.tspluscontractLineService.postDrop.static.test.tscover live client-owned mutation paths and enforce that unassign/deactivate, overlap validation, analytics/in-use/overview source wiring no longer queryclient_contract_lines. - (2026-03-20 00:14 EDT) Completed
T016:creditActions.applyCredit.postDrop.test.tsproves client credit application succeeds whenclient_contract_linesthrows as missing. - (2026-03-20 00:14 EDT) Completed
T017:billingOverviewReport.postDrop.static.test.tsnow enforces the report definition's active-client metric joinsclient_contracts -> contracts -> contract_linesinstead ofclient_contract_lines. - (2026-03-20 00:14 EDT) Completed
T018:postDropRecurringObligationIdentity.test.tsguards 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
T019andT020:clientContractLineRuntimeSourceGuards.static.test.tsscans 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.tsenforces that client contract creation no longer inserts intoclient_contract_linesorclient_contract_services. - (2026-03-20 00:14 EDT) Completed
T026: the focusedbillingEngine.test.tsmigration regression proves client-owned contract loading still returns preserved and cloned assignment lines through the currentclient_contracts -> contracts -> contract_linesruntime shape. - (2026-03-20 00:31 EDT) Completed
T013andT014:contractWizard.integration.test.tsnow passes against a fully migrated schema with noclient_contract_lines/client_contract_servicestables. The test harness was updated for the current auth/tenant seams, and the runtime create path now falls back to migration-aligned defaults forrenewal_mode(manual) andnotice_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) plusinvoiceService.fixedPersistence.test.tsnow verify billed recurring detail rows backfill through the canonical survivingcontract_line_id, accepting both post-drop client-cadence obligation labels without queryingclient_contract_lines. - (2026-03-20 00:31 EDT) Completed
T024: the mixedAutomaticInvoicesbatch 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 requiredbilling_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, andAutomaticInvoicesno longer crash in a fully migrated schema withoutclient_contract_lines.
Links / References
ee/docs/plans/2026-03-16-client-owned-contracts-simplification/PRD.mdee/docs/plans/2026-03-18-service-driven-invoicing-cutover/PRD.mdee/docs/plans/2026-03-18-recurring-invoicing-hard-cutover/PRD.mdserver/migrations/20251207140000_drop_redundant_client_contract_tables.cjspackages/billing/src/lib/billing/billingEngine.tspackages/billing/src/actions/billingAndTax.tspackages/billing/src/actions/invoiceGeneration.tsserver/src/test/unit/billing/recurringInvoiceLinkage.static.test.tsserver/src/test/unit/billing/recurringDueWorkReader.integration.test.tsserver/src/lib/api/services/ContractLineService.tsserver/src/lib/reports/definitions/billing/overview.tsee/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 logicalclient_contract_lineidentity 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?