A season ending early, weather, quality below shipping standard — fresh-grocery e-commerce has a cancellation scenario nobody else has: cancelling a whole batch of orders at once and issuing store credit as compensation. Parent-and-child order structures multiplied by three coupon types and discount caps mean calculating each one by hand takes days, and a wrong number is a customer complaint. As PM, I first scoped 12 refund rules the traditional way — then overturned my own plan: no back-office button, no scenarios hard-coded into a pile of if-else. Instead, an AI tool that stops and asks a human whenever it isn't sure about an order, so the rules grow after launch. The real output of this project was breaking the conventional development assumption that every rule has to be settled before development and carved into code. And this one — from rule design to code — I wrote myself.
CHALLENGE 01
The trouble is in calculating the refund amount. Orders come in two structures — single, or parent-and-child (split); coupons come in three kinds — percentage, fixed-amount, and single-item discount codes; percentage coupons also carry a discount cap that varies by member tier — over the cap you refund the original price, under it you refund the discounted price. Multiply the three dimensions and every order needs someone to look up its structure, sum the linked orders, check the cap, compute the amount, issue the invoice, write a note, and convert to store credit. A batch takes days, and a miscalculation is a complaint.
CHALLENGE 02
The requirement arrived in a perfectly orthodox shape: ops wanted a "batch convert to store credit" button in the back office. I started scoping it the orthodox way too — expand every scenario into rules, and landed on 12 refund rules (single × parent × child, each multiplied by no coupon, percentage coupon over/under cap, fixed-amount coupon, discount code). Then things started going wrong:
Enumeration is a race you're destined to slowly lose. Every meeting adds rules and subtracts confidence — and even if you finish the list, the real world will hand you an order you've never seen the day after launch.
Even with AI helping me scope, the nature of the road hadn't changed: a PM and an AI enumerating together is still enumerating.
TURN
On the third round I stopped and flipped the problem: rather than spend weeks understanding every special case, let the user confirm with the AI what to do when they actually meet that order.
Traditional development has a shape: scenarios are all settled in the requirements phase and become a pile of if-else in the code — and every new scenario afterwards means changing code, testing, and redeploying. For this project I threw that shape away entirely. So the final decision: no back-office button. Ops drive the AI through conversation, and the AI calls tools to execute. The flow:
The key is "must-stop": when an order isn't covered by the rules, the AI doesn't guess, doesn't calculate, doesn't execute — it stops and asks ops to check with customer service first. If customer service's answer is a general rule, the AI opens a PR to write it into the rules, which I review — and the next time that kind of order shows up, it no longer asks.
Requirement confirmation is no longer a "phase" — it becomes a loop. The system doesn't need to be omniscient on launch day. It only needs to be honest, and then get smarter with use.
DESIGN
"Let AI execute refunds" sounds dangerous, so I don't put the money's safety in the rules — I put it in the architecture:
The safety of the money is an architecture problem. Judging scenarios is a content problem. Mix the two and you build a system that's neither safe nor flexible.
BUILD
The developer field on this project has my name in it — five tools (look up, cancel, issue store credit, issue invoice, write note), step-by-step guardrails, the permission table, audit writes — the whole thing from rule design to code, with AI as my development partner.
Sign-off wasn't "it runs": 724 unit tests all passing (12 rules × every amount combination, each check written as a test), two end-to-end rounds on the test server across 10 orders with real structures, and the back-office edit history verified line by line. Along the way I also fixed a small bug in existing code — store-credit records weren't writing the creating user.
IMPACT
The first layer of impact is the numbers: days of manual calculation became minutes of batch execution, and the amounts stopped being wrong.
But I care more about the second layer: ops saw a new shape of requirement with their own eyes. Before, asking engineering for a feature meant the PM had to spell out every scenario in the requirements phase, with weeks of back-and-forth. Now they know there's another way to ask — "build me an AI tool" — where the requirements phase only defines the boundaries (what must never happen, what to do when unsure), and special cases are left to be confirmed in use.
What changed isn't one feature. It's the cost structure of every internal requirement this company raises from here on.
LESSONS
Rather than teach the system every scenario, teach it to admit what it doesn't know. A system that honestly says "I'm not sure about this one" is far safer than one that always produces an answer — and it turns launch from "betting we've enumerated everything" into "starting to accumulate".
STACK
C# (5 MCP tools mounted on the existing back-office system, reusing the existing cancellation and invoice services), Claude Skill (GitHub version control + PR review), allowlist-based tool permissions (SQL-effective, no deployment), dual audit writes (SQL back-office edit history + Mongo operation log), 724 unit tests.
Internal tool; source not public.