Scope
Create cross-system reconciliation cron that detects gaps between transfers and downstream systems.
v2 Revision: Reconciliation Ownership Split (2026-03-22)
Ownership clarification: Reconciliation is jointly owned:
- Query implementations live in
convex/payments/cashLedger/reconciliation.ts (Cash Ledger owns)
- Cron scheduling lives in
convex/payments/transfers/reconciliation.ts (Payment Rails owns)
What to Build
In convex/payments/cashLedger/reconciliation.ts (extending existing module):
findOrphanedConfirmedTransfers() — Query transferRequests with status = "confirmed" older than 5 min, join against cash_ledger_journal_entries.transferRequestId. Missing entries → retry bridge effect → SUSPENSE after 3 failures.
findOrphanedReversedTransfers() — Same for status = "reversed" with missing REVERSAL journal entries.
findStaleOutboundTransfers() — Confirmed outbound transfers where linked dispersalEntry still pending.
findAmountMismatches() — Journal entries where amount ≠ linked transfer amount.
In convex/payments/transfers/reconciliation.ts (new file):
- Cron registration: every 15 minutes, calls the above query functions and triggers self-healing.
Key Change from Original Scope
The reconciliation queries are NOT standalone — they extend the existing cashLedger/reconciliation.ts module which already has reconcileObligationSettlementProjectionInternal() and getControlBalancesByPostingGroup(). This ensures a single home for all financial reconciliation.
Dependencies
- ENG-199 (Cash Ledger Bridge —
transferRequestId must exist on journal entries)
Acceptance Criteria
- Cron runs every 15 min without timeout
- Orphaned transfers detected within 5 min of confirmation
- Effect retry attempted before SUSPENSE escalation
- Reconciliation is idempotent
- Existing obligation-level reconciliation unaffected
Scope
Create cross-system reconciliation cron that detects gaps between transfers and downstream systems.
v2 Revision: Reconciliation Ownership Split (2026-03-22)
Ownership clarification: Reconciliation is jointly owned:
convex/payments/cashLedger/reconciliation.ts(Cash Ledger owns)convex/payments/transfers/reconciliation.ts(Payment Rails owns)What to Build
In
convex/payments/cashLedger/reconciliation.ts(extending existing module):findOrphanedConfirmedTransfers()— QuerytransferRequestswithstatus = "confirmed"older than 5 min, join againstcash_ledger_journal_entries.transferRequestId. Missing entries → retry bridge effect → SUSPENSE after 3 failures.findOrphanedReversedTransfers()— Same forstatus = "reversed"with missingREVERSALjournal entries.findStaleOutboundTransfers()— Confirmed outbound transfers where linkeddispersalEntrystillpending.findAmountMismatches()— Journal entries where amount ≠ linked transfer amount.In
convex/payments/transfers/reconciliation.ts(new file):Key Change from Original Scope
The reconciliation queries are NOT standalone — they extend the existing
cashLedger/reconciliation.tsmodule which already hasreconcileObligationSettlementProjectionInternal()andgetControlBalancesByPostingGroup(). This ensures a single home for all financial reconciliation.Dependencies
transferRequestIdmust exist on journal entries)Acceptance Criteria