← all branches

fix/job-active

needs attentionviewing older commit
d4a8d84 · fullPR #314reviewed 2026-07-16 00:04 UTC0H · 3M · 5L · 3I
The branch
Purpose
Fix production incident (2026-07-14, Solfium RPU 077120901845): status-blind findByContractNumber picked a terminated history row over the active one, minting a junk site named after the RPU and a corpse SUC against it
Goal
Implement the RPU agreements model (design doc approved by Diego 2026-07-15): RPU = service point, contract rows = regime agreements, exactly one active per RPU. All resolution paths use active-first; future inversions self-heal via evidence-driven reactivation
Sub-goals
  • SG-1: findByContractNumberActiveFirst — adopt in job-intent shell (single+batch), cfe-jobs handler (create/retry/batch), public-v1 handler, wizard PATH-2 linking, payment-status fallback writers
  • SG-2: Terminated-only RPU guard — no site/SUC/subscriptions minted; job still dispatches; persist layer creates them when real bills arrive
  • SG-3: ReactivateContract decision + shell — strictly-newer RPU-wide evidence reactivates terminated match; demotes conflicting active row + re-points attachments atomically
  • SG-4: findNewestXmlPeriodEndForRpu — RPU-wide XML max for reactivation guard (forceRefresh re-downloads never flip status)
  • SG-5: loadPersistBatchContext fix — activeContractForRpu correctly finds active row OTHER than compound match (was null when compound match existed)
The changes (whole branch)
What
18 files across utility domain, cross-domain, 3 platform handlers, 1 lambda, and docs. Core: new ReactivateContract decision/shell branch, findByContractNumberActiveFirst query, 3 deduped reassign functions, RPU-wide bill query. No schema changes; no API wire-type reshapes; CommitCfeJobIntentResult.site/siteUtilityContract nullable (consumers verified).
Why
Live production incident + Diego's functionality-first sequencing decision (this merges before repair waves PR #306). Evidence-driven self-heal eliminates need for future repair scripts on this class of inversion.
Areas
domains/utility/src/utility-contract+3456domains/utility/src/bill+324domains/utility/src/monitoring-subscription+390domains/utility/src/payment-monitoring-subscription+390domains/utility/src/site-utility-contract+430domains/cross-domain/src+24015apps/platform/src/api/handlers+195services/utility/bills/cfe/src/handlers+31docs/development+2050
Blast
18 files, +925/−31 (excluding doc). Blast center: utility-contract.shells.ts (new ReactivateContract branch), cfe-job-intent.shells.ts (terminated guard), bill.queries.ts (RPU-wide query). No schema migration, no API contract reshape, no wire-type change.
production-fix sequenced-before-repair-waves-#306 billing-critical
typecheck· PR author verified: utility-domain, cross-domain, platform, utility-bills-cfe all greentests· 51/51 unit + 12/12 evidence integration + 23/23 intent integration — run against staging. 3 pre-existing failures in bill-file-presign.integration.test.ts (S3 creds) untouched by this PR.ci· No GHA CI runs found for this branch (self-hosted runner not running)coderabbit· No .coderabbit.yaml in repo

Findings · 11

correctness1

medium

Demotion updateWithVersion return unchecked — silent two-active-contracts race

domains/utility/src/utility-contract/utility-contract.shells.ts:864

The reactivation leg correctly gates: `if (!reactivated) return err(ContractErrors.databaseError('update'))`. The immediately following demotion — `await contractQueries.updateWithVersion(tx, demoted.id, { status: 'terminated' }, demoted.version)` — does not capture or check the return. updateWithVersion returns null when the WHERE id=... matches 0 rows (concurrent hard-delete). If demotion silently fails, the code still emits the 'superseded_by_reactivation' outbox event and proceeds to reassign SUCs/subscriptions from demoted.id to reactivated.id — leaving the RPU with two active contracts, one now orphaned from its attachments. The TerminateAndReplace branch has the same pre-existing gap; this PR adds another load-bearing instance. Fix: `const demotedResult = await contractQueries.updateWithVersion(tx, demoted.id, { status: 'terminated' }, demoted.version); if (!demotedResult) return err(ContractErrors.databaseError('update'));` before the outbox insert.

conventions2

info

No exhaustive switch on EnsureContractFromBillsResult.action — new members are invisible to type-checker at call sites

domains/utility/src/bill/bill.shells.ts:849

The action union is consumed via a series of if-guards, not an exhaustive switch. TypeScript won't catch a future caller that needs to handle 'reactivated' differently. The new member falls through the 'found' guard (siteLinksNeeded = true, fresh query) which is correct, but the absence of a compile-time gate means the semantics were verified by reading the code, not by the type system. Consider a switch with a `satisfies never` default on the action field in the callers, or a type-level constraint at the call site.

info

findByContractNumberActiveFirst name describes internal algorithm rather than semantic result

domains/utility/src/utility-contract/utility-contract.queries.ts:470

canonical-form.md convention: query names are findBy{Field} (the lookup key) or findActive{Entity}. 'ActiveFirst' describes the internal tiebreaking strategy, not the semantic output ('the canonical contract for this RPU'). A name like findCanonicalByContractNumber would be more idiomatic. Minor — the current name is clear and the JSDoc is thorough. Worth noting before it becomes the template for sibling queries.

tests5

medium

paymentSubscriptionQueries.reassignContractIdDeduped has zero test coverage

domains/utility/src/payment-monitoring-subscription/payment-monitoring-subscription.queries.ts:341

The new deduped variant (DELETE conflicting rows, then UPDATE remaining) is distinct from the pre-existing reassignContractId. The existing payment-monitoring-subscription integration tests cover only reassignContractId (non-deduped). The intent test at cfe-job-intent.integration.test.ts covers subscriptionQueries.reassignContractIdDeduped (monitoring subs) but not the payment variant. A bug in the DELETE subquery's inArray condition for the payment table would go entirely undetected. This function runs in the ReactivateContract+demote path — the higher-risk path for the repair-wave sequencing.

medium

findByContractNumberActiveFirst fallback branch (all-terminated → newest) not directly tested

domains/utility/src/utility-contract/utility-contract.queries.ts:470

The function is exercised only indirectly through the intent shell integration test ('resolves the ACTIVE regime row'). Branch 1 (return active row) is exercised, but branch 2 (all terminated → DESC createdAt LIMIT 1 fallback) is only covered incidentally. There is no test confirming: (a) DESC createdAt ordering picks the newest chapter, (b) soft-deleted rows are excluded from the fallback, (c) null is returned when the RPU has no non-deleted rows. This query replaced status-blind findByContractNumber in 4 handler/lambda locations — a regression in the ordering silently restores the original bug.

low

Demotion integration test does not verify SUC/subscription re-pointing through the shell

domains/utility/src/utility-contract/__tests__/ensure-contract-evidence.integration.test.ts:358

The 'demotes the different-regime active row on a reactivation swap' test checks demoted contract status and the outbox event, but the demoted contract has zero SUC or subscription rows — so the reassignContractIdDeduped calls are no-ops and the re-pointing logic is not exercised end-to-end. The intent test covers the dedup functions in isolation (cfe-job-intent.integration.test.ts line 873), but not wired through ensureContractFromBillsShell. A bug in any of the three deduped calls inside the ReactivateContract branch would pass all current tests.

low

commitCfeJobIntentBatchShell terminated-only guard untested at batch level

domains/cross-domain/src/__tests__/cfe-job-intent.integration.test.ts

The single-shell terminated-only path is covered (line 826: 'terminated-only RPU: dispatch context returned but NO site/SUC/subscriptions minted'). The batch shell has the same guard (cfe-job-intent.shells.ts ~line 365) with a continue-inside-loop pattern, but no batch-level test verifies: (a) a mixed batch where some RPUs are terminated-only and others are active creates artifacts only for the active ones, (b) all RPUs appear in the returned Map. The batch path is used in the bulk job creation flow.

low

findNewestXmlPeriodEndForRpu lacks a direct query-level test

domains/utility/src/bill/bill.queries.ts:1202

Exercised only indirectly through the reactivation shell tests. Unlike the single-contract findNewestXmlPeriodEnd (which has direct tests for null, non-xml exclusion, and max logic), the RPU-scoped variant adds a JOIN through utility_contracts filtered by contractNumber. No direct test verifies: (1) aggregation across multiple contracts for the same RPU, (2) exclusion of soft-deleted contracts, (3) exclusion of non-xml bills at the RPU scope.

improvement3

low

ensureContractForRpu fetches all rows to find newest terminated — single DESC query would suffice

domains/cross-domain/src/cfe-job-intent.shells.ts:514

When all chapters are terminated, the function calls findAllByContractNumber (fetches all, ASC) and takes all[all.length - 1]. findByContractNumberActiveFirst already encapsulates this logic (active first, falls back to DESC createdAt LIMIT 1). ensureContractForRpu could call findByContractNumberActiveFirst and derive isTerminated from result.status === 'terminated', eliminating the full-table fetch and the conceptual duplication.

low

Dynamic imports for subscription queries in ReactivateContract branch lack circular-dep justification

domains/utility/src/utility-contract/utility-contract.shells.ts:887

The await import('../monitoring-subscription/...') pattern is copied from the pre-existing TerminateAndReplace branch. monitoring-subscription.queries.ts does not import from utility-contract (confirmed: it imports only schema types from @batu/database), so there is no circular dependency to avoid. siteContractQueries — same role, same module tree — is imported statically at the top of the file with no issues. These can be converted to static top-level imports, eliminating the per-call dynamic resolution and making the import graph consistent with the rest of the file.

info

Active-first call-site comments slightly redundant across 5 locations

apps/platform/src/api/handlers/cfe-jobs.handler.ts:144

Each of the 5 call sites where findByContractNumber was replaced has a multi-line comment re-explaining the active-first rationale. The WHY is non-obvious and the first comment earns its place (CLAUDE.md: add when WHY is non-obvious). The 4 subsequent instances could be shortened to a single-line pointer: '// active-first — see rpu-agreements-model.md §2.2' since the canonical explanation already lives in the query JSDoc at utility-contract.queries.ts.

History · 6 commits

  1. 3e1361aneeds attentionincremental0H · 1M · 0L2026-07-16 01:14
  2. c7b4e31needs attentionincremental0H · 1M · 5L2026-07-16 01:03
  3. 2099ea7needs attentionincremental0H · 3M · 5L2026-07-16 00:53
  4. ce2dc3eneeds attentionincremental0H · 1M · 1L2026-07-16 00:44
  5. df0ef09needs attentionincremental0H · 2M · 3L2026-07-16 00:23
  6. d4a8d84needs attentionfull0H · 3M · 5L2026-07-16 00:04current