Reconciliation Runbook
The reconciliation engine (financial_integrity, migration 017/021/022) evaluates invariant checks over D1 and records findings + deduplicated exceptions. It never auto-repairs money — only state-surfacing fixes (stale lease release) are automatic; everything else stays needs_review/exception until an operator acts.
Running a scan
http
POST /api/v1/admin/reconciliation/run # start/resume {run_id?}
GET /api/v1/admin/reconciliation/reports
GET /api/v1/admin/reconciliation/report/:id1
2
3
2
3
- A run is
running(resumable via cursor),completed,degraded(some check ERROR/INCOMPLETE — partial coverage, never reported as completed), orfailed. - The 5-minute cron also runs: outbox dispatch → settlement sweep → one recon run → audit assurance.
yp integrity status|report|verify(devpay-cli) is a read-only wrapper over these endpoints.
Check findings → exceptions
Each check maps findings to reconciliation_findings rows; the engine then syncs them into reconciliation_exceptions by fingerprint:
- New fingerprint →
OPENexception + event. - Re-observed →
occurrence_count+1(no new row); fromREVERIFYING/terminal →REOPENED. - Absent + producing check completed while
REVERIFYING→RESOLVED.
Triage workflow
http
GET /api/v1/admin/financial-integrity/summary # severity counts, stale items
GET /api/v1/admin/financial-integrity/exceptions?status=OPEN
POST .../exceptions/:id/transition {to_status:"TRIAGED", reason:"..."}1
2
3
2
3
auto_fixedfindings (stale outbox/inbox lease, orphan settlement claim release) — verify follow-on delivery, then close viaCLOSED_AS_FALSE_POSITIVEonly if truly benign; otherwise continue the state machine.needs_reviewfindings (money/entitlement drift) — pick a recovery op from financial-integrity or fix manually, then moveREMEDIATING → REVERIFYINGand re-run recon orRUN_RECON_VERIFY.- Alert stub: ≥3 consecutive
failed/degradedruns produce analert:recon_failuresaudit evidence record (external notification is PLANNED — checkaudit_evidencevia admin endpoints, not email/Slack).
Fail-closed guarantees
- A missing DB row or query error is never treated as clean — checks report
ERROR, runs reportdegraded. INCOMPLETEchecks reportremaining>0— never silently pass partial scans.