Accounts Payable on RevParPro: Launch Audit

Generated 2026-08-02 by Woz. Sources: production database (read-only queries), live app testing at app.revparpro.com as a QA test account, and a full code review of origin/main (commit e22b034) by three parallel review agents. Every number carries its source.
Active bills in queue
35
$19,282.26 outstanding, 18 overdue live UI accounting ledger, 2026-08-02
Bills ever posted to QBO
1
HOURP bill 9922 on Jul 31, readback verified ap_bill_postings
Days since last invoice email
3
All 6 activated inboxes past the 48h freshness alarm ap_intake_liveness()
Launch-blocking bugs found
3 code + 3 ops
Details below; none is a rewrite, all are small fixes

1. What is broken right now

These are the things that would make a Monday launch fail on day one, independent of code quality.

TypeProblemWhat actually happensEvidence
OPS P0 Invoice intake is silent everywhere Last invoice email processed anywhere: Jul 30, 8:54 PM UTC. CLLAL has had nothing for 9.2 days (222 hours). The four DFW inboxes (DALDL, DFWCE, DFWXY, DFWXF) were activated Jul 30 and have never received a single email. Either mail routing is broken or nobody is sending; nothing is watching, because the monitor script exists in the repo but is not deployed as a scheduled job. ap_intake_log; ap_intake_liveness(); scripts/ap-intake-health.mjs present on main, absent from live scripts dir
OPS P0 Posting switch is off, so approvals dead-end The AP_ALLOW_LIVE_POST secret is not set in the edge runtime (28 secrets checked, none AP related). Every approval will queue forever instead of creating a QuickBooks bill. Exactly 1 bill and 1 payment ($649.50) have ever gone live, on Jul 31, as a pilot. Worse, the approval response claims posting was queued when it never happens, so the UI reports success on a no-op. Supabase secrets API 2026-08-02; ap_bill_postings; ap-approve/index.ts:904-915 vs ap-post-bill/index.ts:922,997-1002
BUG P0 Today's perf change hides the portfolio from the AP queue Reproduced live with one session: at 5:16 PM CT the queue showed all 36 bills across 14 properties. After the 5:17 PM CT deploy of "perf(ap): remove blocking queue round trips", the same login sees only HOURP (11 bills), the property dropdown lists just two options, and the "All properties" label still claims it is showing everything. An accountant would conclude 25 properties have no payables. CLLAL's 13 live bills, including 4 approved and awaiting payment recording, become invisible. reproduced 2026-08-02 22:16Z vs 22:30Z, same JWT; commit e22b034 resolvePropertyScope refactor in _shared/ap-utils.ts; deploy run completed 22:17:59Z (gh run list)
OPS P1 A real $649.50 check is stuck mid-print Check run RPP-HOURP-20260731-332773 has sat in status "printing" since Jul 31, 6:50 PM UTC. The QuickBooks BillPayment (9924) already exists, but no check number was recorded and the run was never released or reconciled. If the physical check never printed, QuickBooks says the bill is paid and reality disagrees. ap_check_runs; ap_qbo_payment_run_payments, qbo_print_status='NeedToPrint'
OPS P1 31 invoices are aging in human queues 15 in accounting review (oldest 10.8 days), 12 ready for approval (oldest 12.2 days), 4 approved and waiting on payment recording since Jul 31 ap_invoices, last touch 2026-07-31 21:20 UTC. This is not an error pile, it is throughput near zero. 13 open invoices still need GL codes and 3 need vendor setup (PEPSICO among them, raised Jul 28). Launch week starts with a catch-up, not a clean slate. ap_invoices status counts 2026-08-02; vendor requests seen live in UI (PEPSICO, 4d old)

2. Bugs that exist in the code

Findings from three independent review passes (backend, frontend, live browser testing), each verified against the actual code with file and line. Severity reflects launch impact.

SeverityBugFailure scenarioWhere
Blocker Double payment race in QBO check runs Two clicks (double-click, or two tabs) resuming the same payment run can each create a BillPayment in QuickBooks. The database rejects the second bookkeeping write only after both QBO payments exist, leaving one payment invisible to RPP. The bill-posting path already solves this correctly with a compare-and-set; the payment path skipped it. A ten-line fix. ap-qbo-payment-runs/index.ts:845-877, 654-657; contrast ap-post-bill/index.ts:1132-1148
Blocker Check-run dialog never resets, second run unreachable After preparing and mailing one check run, the dialog keeps showing the old completed run forever. Selecting new bills and clicking "Prepare check run" reopens the stale run. Only a full page reload escapes. Breaks the core weekly payment loop on its second use. ApQboPaymentRunDialog.tsx:119; mounted permanently at ApAccountingLedger.tsx:908-917
High Approval note leaks onto the wrong bill A note typed on bill A can silently attach to bill B's audit trail when flipping between bills with next/prev, because the note field is not reset per bill. Wrong data in an approval audit trail during launch week is a trust killer. ApBillLifecycleDetail.tsx:72; missing key at ApApproval.tsx:993-1006
High Approve and co-sign failures are silent If an approval or co-sign is rejected by the backend (for example a version change race), the confirm dialog shows no error at all; the spinner ends and nothing happens. The error renders behind the modal. This is the highest-stakes button in the module failing without feedback. ApApproval.tsx:524-526; ApConfirmDialog.tsx:19-101 has no error display
High GL coding silently read-only for some accounting logins Reproduced live: an accounting-role login without per-property AP assignments gets a 403 from the GL accounts lookup, and every GL dropdown renders disabled with no message. The user cannot recode lines and is never told why. At minimum it needs a visible explanation; if such users should be able to code, it is an authorization bug. reproduced 2026-08-02: rpc/get_ap_coding_accounts returned 403 twice, dropdowns disabled; grant chain in migrations 327/328/332/338
High Tracked-only payment RPC skips the payment-recorder gate Any accounting-role user can call the single-payment database function directly and record a tracked-only payment for a property they are not the assigned payment recorder for. The edge function and batch path enforce the narrower capability; the single RPC forgot to. Bookkeeping-only exposure (no money moves), but it violates the stated control contract. migration 413:235-238 and 447-452 vs edge check in ap-record-non-qbo-payment/index.ts:124-144
Medium Three AP functions missing from the CI deploy list ap-vendors, ap-document-rotate, and qbo-ap-backfill are not in the deploy workflow. qbo-ap-backfill runs nightly and feeds the duplicate gate; a stale manual deploy silently weakens duplicate detection. Three one-line additions. .github/workflows/deploy.yml:103-126; nightly cron in migration 405:191-231
Medium Malformed email vanishes without a trace The intake worker has no top-level error handling. An email that fails parsing bounces to the sender but leaves zero record in RPP, so a GM's "I sent it" and RPP's "never saw it" are both true and nobody can prove what happened. workers/ap-intake/src/index.js:359-360
Medium Double-submit can duplicate ACH payment records For non-check payments without a user-entered reference, the auto-generated reference is unique per submission, so the dedupe key can never fire. A double-clicked $100 partial ACH records twice. Checks are safe (check number is the reference). migration 415:528-534 vs unique key in 413:136
Medium Blocker codes render as raw jargon The expected steady-state blockers ("qbo_total_mismatch", "gl_code_missing" and 7 more) are missing from the label map and render as "Qbo total mismatch" style text to accounting staff. The action buttons are plain English; the headline labels are not. src/lib/apLifecycle.ts:52-80 vs codes emitted in _shared/ap-qbo-posting.ts:391
Medium Payment dialog wipes typed data on background refresh Leave the non-QBO payment dialog open 5+ minutes (for example while sending the ACH at the bank), come back, and every typed date, reference, and note resets to defaults. The tracked-check dialog already snapshots correctly; this one does not. ApNonQboPaymentDialog.tsx:57-74 vs snapshot pattern at ApAccountingLedger.tsx:374
Medium "Outstanding" number overstates what is owed The KPI sums full totals for every unpaid lane, ignoring partial payments and including bills still in review that may be rejected. An accounting figure that does not match the checkbook. ApAccountingLedger.tsx:133-134 vs correct remaining_balance use in ApOperationsLedger.tsx:48-50
Medium History is hard-wired to the year 2026 Date filters validate and clamp to 2026 only. On Jan 1, 2027 the history ledger cannot show any new record. A time bomb, not a launch-day issue. ApApproval.tsx:84, 356-357; ApHistoryLedger.tsx:271-286
Medium iPad: merge radio looks dead, review loses side-by-side Below 1280px the duplicate-merge radio appears unselected when tapped (two same-name radio groups fight), and the invoice-vs-PDF compare stacks vertically forcing constant scrolling. The team reviews on iPads; this deserves a deliberate pass. ApInvoiceDetail.tsx:1109-1114 (radios), :1564 (xl breakpoint), ApInlineDocumentPreview.tsx:85
Low Smaller items Stale extraction states have no automatic recovery or monitor; QBO token refresh can strand a rotated token (forces a visible reconnect, fail-closed); shared links to bills outside the current view show a wrong empty state; "Junaid" is hard-coded in vendor-page copy; dead code (unused ApQueueTable, unreachable co-sign branch); history pagination flashes; 20-bill print runs fetch PDFs one at a time. ap-extract/index.ts:797-815 + scripts/ap-extract-run.mjs:64; qbo-client.ts:73-91; ApApproval.tsx:422; ApVendors.tsx:475,1320; useApApproval.ts:1072-1240
What was tested and how

Backend review covered all 14 AP edge functions, the shared QBO client, the Cloudflare intake worker, AP migrations 405-433, and the release-gate fixture suite. Frontend review covered both AP pages, all 17 AP components, both hooks, and routing guards. Live testing signed into production as the QA test account (temporarily granted an accounting role, reverted after, change logged below), walked the queue, invoice detail, blocked view, accounting ledger, history, and vendors pages, and captured every failing network call. Database checks were strictly read-only SELECTs against production. No approve, post, or payment action was triggered; one accidental draft line-type edit was reset with the built-in Reset control before ever being submitted, which incidentally proved the correction-versioning guard works.

3. What must happen to launch this week

In order. Items 1-4 are the actual gate; the rest hardens the launch. Effort estimates are engineering judgment, not measurements.

  1. Fix or revert today's queue-scoping regression. Same-session repro above; suspect the resolvePropertyScope refactor in e22b034. Until then, accounting cannot see the portfolio. (hours)
  2. Wake up intake and prove it end to end. Send a test invoice through every activated alias (3 Houston QBO hotels, CLLAL, 4 DFW), confirm each lands in the queue, and schedule the existing ap-intake-health.mjs monitor as a cron with alerting so silence pages someone. The DFW aliases have never processed one email, so their first proof is part of launch. (half a day, mostly verification)
  3. Turn posting on deliberately. Set AP_ALLOW_LIVE_POST for the QBO hotels, decide manual-post-from-UI as the flow (that is what the code actually does), and fix the response that falsely reports posting was queued. (small, but it is the go-live decision itself)
  4. Resolve the stuck $649.50 check run (release it or void it) and ship the two payment-race fixes: compare-and-set on payment creation and the check-run dialog reset. Do this before the first real weekly check run. (day)
  5. Fix the two approval-trust bugs: per-bill note reset and visible errors in the confirm dialog. (hours)
  6. Close the control gaps: revoke direct access to the single-payment RPC, add the three missing functions to CI deploy, decide what non-assigned accounting logins should see instead of silently disabled GL dropdowns. (hours)
  7. Clear the human backlog with Accounting: 13 invoices need GL codes, 3 need vendor setup, 12 await approval, 4 await payment recording (idle since Jul 31), plus the Jul 29 batch items already written up in AP-NEXT-STEPS.md ($7,874.13 across 11 bills). The software cannot launch a queue nobody drains. (accounting time, not code)

4. What can be improved (after launch)

Product polish

  • Plain-English labels for all posting blocker codes (the fallback currently prints developer strings).
  • A deliberate iPad pass: fix the merge radio, bring side-by-side review below 1280px, or lean into the lightbox.
  • Outstanding KPI should use remaining balance and exclude bills still in review.
  • Loading skeletons for the all-properties landing (today it is one full-screen spinner over what can be a 1,500-invoice load), keep-previous-page on history pagination, parallel PDF fetches for batch printing.
  • Un-hard-code the year 2026 and the name "Junaid" from UI copy.

Pipeline resilience

  • Wrap the intake worker so a failed email leaves an audit row instead of vanishing.
  • Automatic re-drive for invoices stuck in extracting or extractor_error (1 sits there now, from Jul 30), plus a backlog monitor.
  • Idempotency key on payment batch submission to kill the ACH double-record window.
  • Filter obvious signature-logo attachments at the worker instead of relying on downstream auto-reject.
  • Slow-burn: the extraction depends on one LLM key (GEMINI_API_KEY); worth a fallback before AP is business-critical.

5. How the team will feel about it

Honest read: once the queue bug is fixed and intake is flowing, this launches well. The daily loop is genuinely fast, the language is mostly human, and the scary parts (paying money twice, posting the wrong total) are protected by real controls. The risks to sentiment are the silent failures and the backlog they inherit on day one.

Junaid, Talha, Asad (Accounting: review, code, approve, pay)

Will like: a routine single-line invoice is 6 clicks across two people (4 accounting, 2 approver), and the trusted-vendor batch path amortizes to 4 click-path traced through ApInvoiceDetail/ApBillLifecycleDetail by frontend review. Blocked bills say what to do next on the button itself ("Choose duplicate outcome", "Code additional tax GL"). Corrections auto-create a new version that someone else must approve, which protects them. Check printing has stock reservation, a "HOLD: do not sign or mail" banner until verification, and print-then-attest reconciliation. The 2026 history ledger (437 records, page 1 of 5 seen live) with direct QuickBooks links answers "did we already pay this" in seconds.

Will hate: the second check run of the week hitting a dead dialog; an approval failing with zero feedback; typed payment details wiped by a background refresh; developer jargon in blocker headlines; and day one starting with 31 aged invoices, 18 of them past due ap_invoices counts; live UI. Fix list above addresses all of these.

GMs at the hotels (email invoices in, approve their property's spend)

Will like: forwarding an email is the whole intake job, duplicates are caught automatically (5 caught to date, zero false intake failures ever ap_intake_log outcomes: 88 created, 5 duplicate, 4 no_attachment), and their approval screen is two clicks with a typed-total confirmation only over $2,500 ap_policies org_cosign_threshold=2500.

Will hate: "I emailed it and it never showed up." That is exactly the current state: nothing has arrived since Jul 30, and a malformed email leaves no trace to investigate. Until intake is proven live and monitored, do not announce this to GMs; one lost invoice in week one defines the product for them.

Ace and Alyssa (ownership)

Will like: the money controls are real. A bill cannot post unless printed lines plus explicit tax equal the total, enforced independently in the app layer and the database; every posted bill, attachment, and payment is read back from QuickBooks and verified field by field; tracked-only entities (BCS, DFW) are hard-blocked from ever touching QuickBooks; and posting requires a fresh MFA session. One bill and one $649.50 payment went through the full pipeline end to end and reconciled ap_bill_postings readback 2026-07-31; strengths verified by backend review.

Should know: AP development is running hot: three perf commits shipped straight to production this afternoon without review, during this audit, and one of them caused the queue regression. Speed is the house style, but AP is the one module where a bad deploy means missed or double payments. A minimum bar of "reviewed before deploy" for AP-touching commits during launch week is cheap insurance.

6. What is already good (keep it)

7. Verified vs assumed

Verified

  • All database numbers: read-only SELECTs against production on 2026-08-02 (tables cited inline).
  • Queue regression: reproduced in one live session straddling the 22:17:59Z deploy, same login, same request.
  • GL coding 403 and disabled dropdowns: observed live with captured network calls.
  • Code findings: every file:line above was read in the origin/main worktree by the review agents.
  • AP_ALLOW_LIVE_POST absent: live secrets listing on 2026-08-02.

Assumed (worth checking)

  • Root-cause attribution of the queue regression to e22b034's scope refactor is the strongest suspect but not line-proven; the fix task should confirm against the three perf commits shipped today (c474ac5, 0a08fdd, e22b034).
  • Whether real accounting users (who do have per-property AP assignments) are also affected by the regression; the repro account had none.
  • Why intake is silent: broken routing vs nobody sending over a weekend. The test-email pass in item 2 answers it.
  • Effort estimates in section 3 are judgment, not measured.
  • The blank PDF preview seen in the headless test browser is almost certainly a headless artifact (the signed document URL returned 200); confirm once in a real browser.