← all branches

claude/zealous-lovelace-cbc97f

needs attentionviewing older commit
c0927df · incrementalPR #215reviewed 2026-07-03 06:34 UTC0H · 2M · 3L
The branch
Purpose
Add a nightly pg_cron reconcile-and-repair backstop for the utility_contract_overview read-model, which is trigger-maintained and only susceptible to drift via trigger-bypass operations (logical-replication, TRUNCATE, ALTER TABLE DISABLE TRIGGER). The branch makes drift observable (always expected to be 0) and auditable.
Goal
Ship migration 0053 with pg_cron job, audit table, TS wrapper, integration tests, watchdog GHA, and ops runbook — all as an ADR-020 follow-up.
Sub-goals
  • SG-1: utility_contract_overview_reconcile() SQL function — recompute + repair + audit log
  • SG-2: utility_contract_overview_reconcile_runs audit table (SECURITY DEFINER + RLS)
  • SG-3: pg_cron schedule — extension-guarded, nightly 03:17 CDMX
  • SG-4: runOverviewReconcile TS wrapper in utility-contract-overview.queries
  • SG-5: Integration test — injects each drift kind and asserts detect→repair→audit
  • SG-6: CI drift watchdog GHA (.github/workflows/uco-drift-watchdog.yml)
  • SG-7: Ops runbook + ADR-020 follow-up marked implemented
The changes (whole branch)
What
This incremental commit adds the unit test file utility-contract-overview.reconcile.unit.test.ts — 49 lines covering runOverviewReconcile driver-boundary behavior that the integration suite cannot reach (no-row invariant guard + string count coercion).
Why
loop-review findings from the previous commit (1328ef52) requested unit-level coverage for the two driver-boundary behaviors the SQL-always-returns-one-row invariant makes unreachable in integration tests.
Areas
domains/utility (test only)+490domains/utility/src (overall branch)+35010packages/database (schema + migration)+122003.github/workflows+670scripts/db+670docs+1000
Blast
15 files across the branch, +12727/−3 total (bulk is generated migration SQL); this incremental commit is 1 file, +49 lines, no prod code changed.
pg_cron not enabled on staging/prod yet — required out-of-band step post-merge migration 0053 self-skips cron scheduling on environments without pg_cron extension
typecheck· not run in this reviewci· CI status unavailable (PAT scope)coderabbit· no .coderabbit.yaml in repo

Findings · 5

correctness1

low

NULL count fields silently coerce to 0 or NaN — not pinned by a test

domains/utility/src/utility-contract-overview/__tests__/utility-contract-overview.reconcile.unit.test.ts:29

Number(null) === 0 and Number(undefined) === NaN. If the SQL function returns NULL for a count column (e.g. due to a future schema change), the TS wrapper would silently produce 0 or NaN without any test catching it. The test only exercises the string-to-number coercion path ('3' → 3). Adding a fixture with a null count field and asserting NaN or a thrown error would pin this boundary. Not a current bug since the SQL function is documented to return non-null aggregates.

conventions2

medium

Query function throws instead of returning Result<T,E> — violates ADR-016

domains/utility/src/utility-contract-overview/__tests__/utility-contract-overview.reconcile.unit.test.ts:24

The test asserts rejects.toThrow(/returned no row/) for the no-row invariant guard in runOverviewReconcile. domain-patterns.md and ADR-016 list 'Throwing exceptions for business errors' as an explicit anti-pattern: all fallible operations must return Result<T,E>. An infrastructure-level invariant break (SQL function returned no row) should be modeled as err({ _tag: 'ReconcileInvariantViolation', statusCode: 500 }). The test correctly covers the boundary but is pinning a violation of the project's error-handling convention. Fixing requires updating the production query to return Result, then updating both this unit test and the integration sibling.

low

Non-standard double-qualifier test file name (.reconcile.unit.test.ts)

domains/utility/src/utility-contract-overview/__tests__/utility-contract-overview.reconcile.unit.test.ts:1

The canonical naming pattern is {entity}.{layer}.test.ts (e.g. .decisions.test.ts, .queries.test.ts, .integration.test.ts). The double qualifier .reconcile.unit. is unique in the repo — the prior commit's sibling .reconcile.integration.test.ts set this precedent. The .unit. qualifier is redundant for a no-DB stub file; the conventional choice would be utility-contract-overview.reconcile.test.ts or utility-contract-overview.queries.unit.test.ts.

tests1

medium

Stub's execute return is a plain array — postgres.js RowList protocol not modeled

domains/utility/src/utility-contract-overview/__tests__/utility-contract-overview.reconcile.unit.test.ts:19

The stub returns a plain JS array from execute. The real implementation does Array.from(result as Iterable<ReconcileRow>) which works because arrays are iterable. However, postgres.js's RowList carries additional properties (e.g. columns, count) that the stub silently omits. A comment noting this deliberate simplification, or narrowing stubDb to DbOrTx so future editors understand the contract, would prevent confusion. Not a current bug — Array.from on a plain array behaves correctly.

improvement1

low

stubDb accepts unknown[] — column-name typos silently produce NaN/0

domains/utility/src/utility-contract-overview/__tests__/utility-contract-overview.reconcile.unit.test.ts:19

stubDb accepts unknown[], so a typo in a column name (e.g. drift_ccount) silently produces Number(undefined) = NaN. Typing the rows parameter as Partial<Record<'drift_count'|'missing_count'|'orphaned_count'|'mismatch_count'|'repaired_count'|'run_id', string>>[] would make the compiler catch field-name drift, mirroring the SQL column contract the test is meant to pin.

History · 8 commits

  1. df474d4needs attentionincremental2H · 3M · 6L2026-07-22 23:12
  2. 8b749afneeds attentionincremental0H · 1M · 3L2026-07-14 17:58
  3. 8f01e3bsafeincremental0H · 0M · 0L2026-07-07 16:58
  4. 6fb3b0aneeds attentionincremental0H · 1M · 3L2026-07-06 16:26
  5. b7dc960needs attentionincremental0H · 1M · 4L2026-07-04 02:25
  6. c0927dfneeds attentionincremental0H · 2M · 3L2026-07-03 06:34current
  7. 1328ef5needs attentionincremental1H · 4M · 5L2026-07-03 06:26
  8. 4c3d40dneeds attentionfull6H · 15M · 12L2026-07-03 06:05