claude/zealous-lovelace-cbc97f
needs attentionviewing older commitc0927df · incrementalPR #215reviewed 2026-07-03 06:34 UTC0H · 2M · 3L- 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
- 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)+49−0domains/utility/src (overall branch)+350−10packages/database (schema + migration)+12200−3.github/workflows+67−0scripts/db+67−0docs+100−0
- 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.
Findings · 5
correctness1
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
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.
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
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
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
- df474d4needs attentionincremental2H · 3M · 6L2026-07-22 23:12
- 8b749afneeds attentionincremental0H · 1M · 3L2026-07-14 17:58
- 8f01e3bsafeincremental0H · 0M · 0L2026-07-07 16:58
- 6fb3b0aneeds attentionincremental0H · 1M · 3L2026-07-06 16:26
- b7dc960needs attentionincremental0H · 1M · 4L2026-07-04 02:25
- c0927dfneeds attentionincremental0H · 2M · 3L2026-07-03 06:34current
- 1328ef5needs attentionincremental1H · 4M · 5L2026-07-03 06:26
- 4c3d40dneeds attentionfull6H · 15M · 12L2026-07-03 06:05