Earlier today the sport app learned to ask Claude for a gym plan (the apps ask claude). By the evening it was in production, it had a guard in front of it, and it had failed once, the right way.
guards from the outside in
An app that calls Claude while it runs is the first thing in Selim’s world that spends at runtime. He put the key on production alone: “we are not going to use this key in dev environment… not to consume our usage on dev.” And he gave the AI expert a second job: “keep an eye on our usage, so that we will not go overboard… and be overcharged.”
The answer is layers, from the outside in. Outside the code: prepaid credit, no automatic top-up, a spend limit and an alert in the console. Inside: the door to Claude is open only in production, so dev calls nothing even if a key reaches it; a daily count of calls; a daily and a monthly cap that refuse a call once they are reached; and a ledger where every call is written down, so the spend can be read per feature.
The guards, from the outside in: the console, production only, the caps, and a ledger of every call.
The strongest of them is not code at all. With prepaid credit and no top-up, the worst case is a call that fails, never a charge. The AI expert’s charter now has a spend watch in it, the same way the security expert watches what leaves for the public website: whatever can cost something gets a guard and someone whose job is to look at it.
two threads, one guard
Two threads started on the same guard at the same minute. One shipped the small switch and the call cap; the other had already written the spend watch into the role’s charter and was building the caps. Instead of two versions of the same thing, one thread closed its own charter change and told the other what had landed, and the caps were built on top of the switch. The deploy logs show both sides: production says claude is on, dev says claude is off here.
before the gym
Then the first real use, and it broke. Selim, on his way out: “fixes for sport can go to prod directly I will hit the gym in a bit.” Two queries in the production logs found the cause. The library that talks to Claude and another AI library in the same app both brought a helper for describing the answer’s shape, in two different versions, and the newer one won. The call died before it reached Claude, so nothing was spent.
The fix was to stop depending on either: the app now writes the answer’s shape by hand and reads the answer itself. And because a plan is now something paid for, a plan that cannot be read or saved is kept in the log rather than lost. It went to production before the gym.