Found by two independent reviews and checked by a third. Nothing was run against a live system, so each note is phrased as a question to verify. The third note concerns a change made during review, and is written with that in mind.
We trained an AI agent for Gumroad. It reviews each PR merged to antiwork/gumroad with knowledge of how the codebase has evolved and which changes have caused bugs before.
10 def perform 11 return unless Rails.env.production? 12 return if Feature.active?(:disable_stripe_balance_check_notification) 13 14 balance_check = StripeBalanceCheckService.new 15 16 was_needed = $redis.get(RedisKey.stripe_balance_topup_needed) == "true" 17 $redis.set(RedisKey.stripe_balance_topup_needed, balance_check.topup_needed?) 18 19 if balance_check.topup_needed? 20 notify(balance_check, "red") 21 elsif was_needed 22 # Yesterday's alert asked for money; say so when it's no longer needed. 23 notify(balance_check, "green") 24 end 25 end
How to verify. Start with the flag set to true and a sufficient balance. Make the payout listing raise a connection error on the first attempt and succeed on the second. Run the job twice. No notification is sent at all.
A possible fix. Build the message first and write the flag only after the notification is enqueued, or rescue around the flag write so a failure leaves the previous state in place.
12 PAYOUT_RUN_WDAYS = [2, 3, 4, 5].freeze 13 14 def initialize(now: Time.current) 15 @now = now.utc 16 @payout_end_date = User::PayoutSchedule.next_scheduled_payout_end_date 17 @upcoming_payouts_cents = PayoutEstimates.estimate_gumroad_held_stripe_cents(@payout_end_date) 18 19 balance = Stripe::Balance.retrieve 20 @available_cents = usd_cents(balance.available) 21 # Pending sales settle within a couple of business days -- inside the weekly payout window -- 22 # so they fund the upcoming payouts. Available-only over-reports the top-up and fires false alarms.
How to verify. Freeze time on a Friday afternoon, set the previous alert state, and create an eligible seller with a large unpaid balance dated after the current cutoff and insufficient Stripe funds. The estimate for the announced cycle omits that balance.
A possible fix. Derive the estimate's cutoff from the same cycle the message announces, so the dates and the amount always describe one cycle.
61 # tells the reader where the money went. 62 def swept_to_bank_last_day_cents 63 @swept_to_bank_last_day_cents ||= Stripe::Payout.list( 64 created: { gte: (@now - 1.day).to_i }, 65 limit: 100 66 ).auto_paging_each.sum do |payout| 67 payout.currency == Currency::USD && payout.status == "paid" ? payout.amount : 0 68 end 69 end 70
45 "Stripe paid #{formatted_dollar_amount(balance_check.swept_to_bank_last_day_cents)} out to Gumroad's bank in the last 24 hours; " \ 46 "those automatic sweeps are what draw the balance down.",
Evidence in the repository. The repository's own fixture records this: a payout of 99,959,940 cents with status pending, while available drops from 202,959,940 to 103,000,000 cents in the same exchange. Gumroad's stuck-payouts job also treats a payout as still processing until its arrival date has passed.
How to verify. Add a service spec with a USD bank payout created an hour ago whose status is pending or in_transit. The swept total returns zero while the available balance already reflects it.
A possible fix. Either keep the paid-only figure and drop the causal clause, or report both numbers, settled and in flight, so the sentence and the figure agree.
The agent learns from every merged PR and from every reply. When a finding is confirmed or fixed, that pattern is weighted up; when one is refuted, the correction is fed back and the agent stops raising it. Each review is more precise than the one before it.
Paying is optional. If this was useful, you can pay what you believe is fair: https://buy.stripe.com/4gMeVdfpVgl18Td4hg5AQ00. Reply on the pull request if you want to keep receiving our recommendations on every merged PR. The findings stand either way.