← back to Costa Rica
cycle 23 docs: YOLO_NOTES ledger — cr_iban dead-payout fix + live-mode test coverage
a52a312565aaee5bd63752e819ba39e9fcdfaabf · 2026-09-24 03:22:45 -0700 · Steve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TouFkmUGKHtwqpZwgReVic
Files touched
Diff
commit a52a312565aaee5bd63752e819ba39e9fcdfaabf
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Sep 24 03:22:45 2026 -0700
cycle 23 docs: YOLO_NOTES ledger — cr_iban dead-payout fix + live-mode test coverage
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TouFkmUGKHtwqpZwgReVic
---
YOLO_NOTES.md | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/YOLO_NOTES.md b/YOLO_NOTES.md
index f920ccf..d80aabf 100644
--- a/YOLO_NOTES.md
+++ b/YOLO_NOTES.md
@@ -497,3 +497,24 @@ Canonical apply order now: `004_marketplace → 005_apple → 006_contacts → 0
**Cost:** $0 (local + one Cody pass — which ran an actual negative-test verification instead of a positive-only rubber-stamp).
**Backlog:** two gated decisions still on Steve's desk (host-claim approval + reconciler cron — 3 and 2 cycles unanswered respectively). Provider-honored idempotency key + sig-encoding (live-only); processorFee schema prerequisite. With this item closed, the concrete-deferred backlog is now empty — next cycle should be another cold audit or a fresh area (Expo/RN app-side review, App Store submission checklist, or KYC-status dead-code cleanup noted back in cycle 9).
+
+---
+
+## Cycle 23 — cold Cody payout-leg audit: cr_iban dead-payout path closed (TK-10346)
+
+**Surface:** the settlement leg (lib/payouts.js + payout_methods + SINPE/Plaid rail selection) — a different surface from cycle 19's charge-leg audit.
+
+**Bug (verified 4 code links):** `kind='cr_iban'` was registerable (route + DB kind CHECK) but the IBAN rail was never wired. It routes to `rail='sinpe'`, and `tilopay.payout()` builds `{phone: method.sinpe_phone}` — a cr_iban method has no phone, so a bank/IBAN host received a `{phone:null}` transfer → **silently $0** (or a stuck 'processing') in LIVE. Invisible in every existing test (all sandbox).
+
+**Fix (mirrors the blessed plaid_ach pattern — registerable, fails loud in live):**
+- `migrate_012_payout_method_completeness.sql`: CHECK that sinpe_movil carries a sinpe_phone and cr_iban carries a cr_iban (DB backstop; 0 prior dev violations). plaid_ach intentionally unconstrained.
+- `lib/payouts.js`: `cr_iban && liveMode` throws before the phone-less transfer → payout row marked 'failed' + error surfaced.
+- `routes/app.js` POST /host/payout-methods: 400 an incomplete method before any INSERT.
+
+**Cody gate caught a hole in my own fix:** the headline runtime throw had **zero test coverage** (every payouts test ran sandbox/liveMode=false). Added `test/payouts-live-cr-iban.test.js` — forces LIVE (env creds + require-cache reset across tilopay/payments/payouts) and proves BOTH directions: cr_iban+live throws the guard AND lands the row 'failed'; sinpe_movil+live does NOT false-fire (faked /sinpe/transfer is reached, row → 'processing'). This is a genuine negative test — remove the guard and it goes red (real fetch, wrong error). Suite **203 → 205**, serial green. Commit `789a630`.
+
+**Cody's 2nd finding → DECISION memo (not auto-fixed):** the fix is one leg of two. cr_iban (and plaid_ach identically) stays registerable-but-unpayable, and there's NO host-facing payout-status surface (no `GET /host/payouts`, no notification) — so a `status='failed'` row is loud in Postgres, silent to the host. Blast radius today = zero (pre-launch, no live callers). Drafted `2026-09-24-TK-10346-costa-payout-visibility-DECISION.md` (A: reject at registration / B: keep + add visibility leg / C: keep as-is = current default). Did NOT unilaterally flip customer-facing registration behavior.
+
+**Cost:** $0 (local + one Cody pass that ran an actual negative-test verification, not a positive-only rubber-stamp).
+
+**Backlog:** THREE gated items now on Steve's desk — host-claim approval (unanswered), reconciler cron (unanswered), and the new payout-visibility decision. Plus live-only preflight (provider idempotency key + sig-encoding) and the processorFee schema prerequisite. Concrete-deferred code backlog is empty again — next cycle: another cold audit on a fresh surface (ingest/scraper scripts, App Store submission checklist, or a refund-after-payout state-machine guard).
← 789a630 costa-rica: close the cr_iban dead-payout path — completenes
·
back to Costa Rica
·
costa-rica: fix stored XSS on public/place.html — escape eve d79ae5e →