Baren81 Devlog

I Fixed the Code Six Times. It Never Ran Once.

Devlog card: "Fixed six times. Never ran once" — The payment sheet that would not open.

Written by

in

Call the Cat: Delivery — launch devlog

TL;DR: The AI fixed the code six times. All six times, that code never ran. Before asking “why won’t it work,” I needed to ask “did it even start.”

The buy button just said “Opening checkout…” and stopped there.

The button froze and nothing happened

I added a permanent 2x-speed item to the delivery game — a real $3 purchase. I pushed a Google Play internal test build and tapped buy. The button changed to “Opening checkout…” and stayed that way. No payment sheet, no error. Tapping again did nothing.

Google sign-in worked fine in the same build. Ads loaded. Only the purchase was broken.

Two days went into this. I handed it to the AI, it changed something, I shipped a new build, I tested again. Six rounds. Version numbers climbed from 1.0.10 to 1.0.17. The symptom never moved once.

Fixing code that never runs leaves the symptom exactly where it started, version after version.
Fixing code that never runs leaves the symptom exactly where it started, version after version.

Six confident guesses from the AI

Going back through the log, the AI named a different cause every round — and every one sounded reasonable.

Round What the AI blamed What it actually did
1 The call to the native billing bridge was missing Restored the call
2 No timeout, so the screen never unfroze Added a watchdog timer
3 The app’s cert fingerprint wasn’t registered in the console Registered it
4 An unused event listener was stalling startup Removed it
5 Not enough logging to diagnose Added native logs
6 Wrong product ID Created a second product

All six started from “this should fix it.” All six moved on to the next guess without confirming what actually ran on the device after the change. So did I — I was only watching the screen.

Six rounds, six different causes named, six fixes applied — same symptom every time.
Six rounds, six different causes named, six fixes applied — same symptom every time.

The question that changed everything

On day two I stopped asking “why won’t the checkout open” and asked instead: “did the payment function even start?”

Android lets you plug a phone into USB and read everything the app logs, live. Round five had already added native logging inside the payment path, so all I had to do was tap buy and watch that log.

Zero lines.

No request logged. No product lookup. No failure. Not even an error. The button had visibly changed state, but the code that handles payment had never once started running.

That’s when two days of fixes turned out to be meaningless — every change had been made below the line where execution actually stopped.

The cause was one word in front of a function

Our game is built as a web app wrapped for Android. The web side reaches Android features through a bridge object it fetches at startup.

The function that fetched that bridge looked like this:

async function getPaymentBridge() {
  return paymentBridge;   // hands back the bridge object as-is
}

async means “the result arrives later.” When a JavaScript function like that returns something, the engine checks whether the thing it got back is itself another pending operation — by asking whether it has a method called then.

The bridge object was built to answer “yes” to any method name you asked it for. Since the web side can’t know ahead of time which native features exist, the bridge answers “I have that” for anything, then forwards the real call once you actually invoke it.

So when the engine asked “do you have then?”, the bridge said yes. JavaScript took that as a second pending task and waited for it to finish. That task never existed in the first place, so it never finishes.

One line for it: I was waiting to cross a bridge, and the thing I was waiting on was the bridge itself.

The answer was already sitting in the same file

The most deflating part: the working login code was in the same repository the whole time.

Login (worked) Payment (frozen)
Where the bridge is fetched Once, at the top of the file Inside a function, called every time
Is the function async? No Yes

Login just stored the bridge object in a variable at the top of the file. No waiting, no confusion. Payment wrapped the same step in a function, and that function had async on it.

“Login works, only payment is broken” was exactly this difference. For two days we read that symptom as “the console settings are missing something on the payment side only.”

Same app, same bridge — one path works, the other waits forever, and the only difference is one keyword.
Same app, same bridge — one path works, the other waits forever, and the only difference is one keyword.

What to copy — the “did that code even run” ladder

Before fixing anything, check these from the bottom up. If a lower rung is empty, fixing a higher rung won’t change the symptom.

Rung Check How If it stops here
1 Did the button register the tap Does the screen text/state change UI code problem
2 Did our code start Add a log at the first line of the function Call condition / branching problem
3 Did the request reach the native layer Is there an entry log on the device Bridge / connection problem — this is where we were stuck
4 What did the native layer answer Log the response as-is Console / account / product setup problem

The 'did that code even run' ladder — four rungs, checked bottom to top.
The “did that code even run” ladder — four rungs, checked bottom to top.

This time rungs 1 and 2 passed. Rung 3 was zero lines. The AI spent two days fixing rung 4 — console, product, certificate — which the code never even reached.

On Android, checking rung 3 is one line once the phone is plugged in over USB. Swap in your own app’s log tag.

adb logcat -s YourAppLogTag:V

One line to hand the AI before you ask it to fix a store feature

Since this happened, I now attach this line whenever I hand off a payment or login task:

“Before you change anything, show me a device log proving that code actually ran. If there’s no entry log, don’t touch what comes after it — find out why it never started.”

That line does one thing: it makes the AI produce evidence before it acts on a plausible-sounding guess. Every one of the six guesses this time was logically reasonable. None of them had been confirmed on the device.

Where this leaves things

Code that compiles, passes tests, ships to the store, and responds to a tap doesn’t mean the feature ran. This time the payment function stopped one line before it could start, and the reason was a single keyword.

Fixing based on the symptom alone gets you six fixes and the same symptom. We burned two days before checking the device log — once we checked it, the real fix took thirty minutes. We just looked at the wrong thing first.

What’s confirmed so far is that the checkout sheet opens on a real device. A completed test purchase, restore after reinstall, and refund entitlement removal are still unverified. Code can’t tell me those.

Solo dev of Call the Cat, shipped after 178 days
on Google Play
and Steam.
Now building Call the Cat: Delivery and Call the Cat: Galaxy, in public.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *