A tip calculator, a stopwatch, and a lot of pesos.
TL;DR: I built a USD/MXN tip calculator with Cursor for the Build a Mini App challenge during ...
For further actions, you may consider blocking this person and/or reporting abuse
The input rules are the part of this prompt that travels to the next mini app: whole numbers for people, bounds on percentages, what an empty field does. Kept as one block you paste into every build prompt, the next app starts with the 1.5 people case already closed instead of rediscovering it in review.
Agreed, and I usually do build this way. My normal process is block by block with a QA pass after each one, and the input rules live in a block that carries over. This one was an 8-minute challenge, so I skipped my own process and wrote the prompt in one go, and the 1.5 people case is what skipping cost me.
So the lesson I’m taking isn’t “start a reusable block.” It’s “the block exists, use it even when the build is small.” The small ones are where I’m most tempted not to. But look where it got me! 😆
That temptation makes sense, since on a small build the block looks like the slow part. One way around it is making the block the first paste rather than a step you choose: every build prompt starts with the input rules already in it, so skipping them takes a deliberate edit instead of happening by default.
That’s a good flip. If the block is already in the template, skipping it means deleting something on purpose, and I’m much less likely to do that than to not add it. Default on, opt out, instead of the other way around.
Default on is the whole trick for me too. The other half is keeping the block short enough that nobody resents pasting it, so it never feels like the slow part on an eight minute build. Once a rule earns its place in there, it stays, and the small builds get it for free.
Agreed. Short enough not to resent is the part I’d have gotten wrong. I’d have kept adding lines until the block was a second spec. So the rule for the block is: a line gets in when a review caught the bug, not before. Whole number for people is in. The penny rule is in. Anything I’m only worried about isn’t.
For the next test, I'd keep the app small but add state and externally fixed acceptance checks: invalid exchange rates, fractional people counts, rounding across split totals, and reload behavior. The interesting metric isn't just whether the agent builds in one pass. It's whether the same workflow preserves invariants when inputs become invalid and state has to survive beyond the initial render.
This is close to what I had in mind for round two, and you’ve named the checks more precisely than I had. Rounding across a split is the one I hadn’t listed. 1,437.50 three ways is 479.17 each, which adds up to 1,437.51, and right now the page just lets that penny float.
“Preserves invariants” is the part I want to make sure I’m reading right. I take it to mean the things that should stay true no matter what the user does: the split always adds back to the total, a bad rate never produces a number, a reload never shows a state the user didn’t enter. If that’s it, then yes, that’s the metric, and it’s a harder one than “did it build.”
I’m not sure yet whether I’d write those checks into the spec or keep them outside it as a separate list the reviewer runs. The thread above makes a case for the spec. Your phrasing, “externally fixed,” sounds like you’d keep them out. Curious why.
I wouldn't keep the invariants out of the spec. I'd keep the acceptance checks independent of the build process. The spec should describe what must always hold, while a separate, version-controlled test suite verifies those properties without letting the agent silently change the rules it's being judged against. When a review exposes a missing invariant, update both deliberately: add it to the spec so future work inherits it, and encode it in the tests so the behavior is independently checked. Otherwise, the builder and reviewer can agree on the same incomplete assumption. For the split example, I'd also define how remainder cents are allocated, since preserving the total requires more than just rounding each share independently.
That clears it up. Spec says what must always hold. Tests check it from outside. When the review finds a gap, both get updated on purpose, and the builder doesn’t get to edit the thing it’s being graded by. That last part is the one I hadn’t thought through. If the same agent writes the code and the tests, it can agree with itself all day.
In my setup that probably means the build agent never touches the tests, and a second agent writes them from the spec before the build starts. Which is close to how I run reviews already, just moved earlier.
The remainder cents point is a decision I hadn’t made. 1,437.50 three ways leaves a penny. Someone pays it. Right now the page doesn’t say who, it just rounds each share and lets the total drift. Whoever enters the bill? Person one? Random? I don’t have an opinion yet and I suspect the right answer is “the spec has to pick one and say why.”
I'd separate two things here: the spec defines intended behavior, while acceptance checks independently verify it. When a check exposes a missing or wrong assumption, update both deliberately, but don't let the agent under review silently redefine the check to make its own implementation pass.
For the penny, I'd use a deterministic allocation rule that guarantees the shares add back to the total, rather than rounding each share independently. The exact policy matters less than making it explicit and testing it.
That’s how I read it. Two things, kept apart on purpose, and the builder never edits the answer key. On the penny, I’ll pick a rule and write it down. Probably “first person pays it,” since someone has to and it’s easy to test.
The 1.5 people bug is my favorite part. "People must be at least 1" was in the spec, and the agent did exactly that: 1.5 is at least 1. The spec never said whole number, so the build was right and the spec was wrong, and only a reviewer reading it cold caught it.
We run a small team of agents and keep landing on the same rule: the thing that checks can't be the thing that built it.
Did Claude get the original prompt for the review, or just the file? Curious whether knowing the spec would have made it miss the same gap.
The 1.5 people thing made me laugh. The spec said “at least 1” and nobody pictured a decimal in a people field at 8:26 in the morning. That one’s on me, not the build.
Claude had the prompt. It wrote the prompt, and it had the whole chat when it did the review. It still caught the gap, I think because it was checking what the page does with bad input, not whether the code matched the spec. Matching the spec would have passed.
How does your team handle it when the spec is the thing that’s wrong? I don’t have a process for that yet.
That detail is the whole answer for me: the review caught it by poking the page with bad input, not by diffing against the spec. A spec can be wrong. Behaviour can't lie about itself.
My take on your question: treat the spec as a hypothesis, not a contract. When the checker finds something the spec never imagined, the fix goes into the spec first and the code second, so every later prompt inherits it. "At least 1" becomes "whole number, 1 to 20". Nothing stops a wrong spec, but one that grows from every caught bug gets wrong less often.
"Spec as hypothesis, not contract" is doing a lot of work in one line. It turns the review from a check on the code into a test of the spec, and it means a caught bug is the spec learning something, not the spec failing.
One thing I'd add: a changelog line when the spec changes. "At least 1" became "whole number, 1 to 20" because of a review on Oct 15. Otherwise in three months I'll wonder who made up the 20.
Thanks for this. Next time I'll just prompt "Write the spec for this project. Make no mistakes." and see how that goes. 😂
The changelog line is a great add. A spec with no dates is just vibes with headers, and "who made up the 20" is exactly the question it answers in three months. Also "make no mistakes" is the most optimistic prompt in software. Report back if it works.
🤣😂
On your question about apps with state or a backend: that's where I've seen the spec stop covering things, because the bugs are about what nobody thought to write down.
In an expense-tracker app I built with AI tools, the bugs that got furthest all passed a happy-path check. An AI endpoint that accepted any signed-in caller with no daily limit. A Pro feature with no Pro check (Copilot's PR review caught that one). The model adding up totals itself instead of the code doing it. Dark mode missing a few screens with hardcoded colours. None of those would show up in an eight-minute build of a calculator, and none of them crashed.
Matthew's idea of updating the spec with every bug the reviewer finds is close to what I'm starting: one standing list of bug types I check after each AI session.
I made a 5-minute video walking through five mistakes like these, with the check for each (my channel, Coldboot): youtu.be/ewSZTFC-SpY
Four bugs that all passed the happy path and none of them crashed. That’s the category an eight-minute calculator build can’t surface, and it’s a useful list to have.
Two of yours overlap with a pre-deploy checklist I run (authorization and rate limiting), and the model doing the arithmetic is something I try to keep out of my builds: code writes the numbers, the model writes around them. The dark mode one I would not have thought to check, and the Pro feature with no Pro check is the kind of thing that looks fine in every screenshot.
A standing list of bug types per session is close to where this thread has been heading, with the spec getting the rule and the list getting the check. I’ll watch the video.
Glad the dark mode one was useful. It's the easiest to miss, because every screen the agent touched looks right. A quick search for hex values and rgb( outside the theme file after each session turns up the leftovers in seconds.
"The spec gets the rule, the list gets the check" is a tidy way to split it. Thanks for the thoughtful reply, and for the post that started the thread.
The line I would keep from that prompt is the last one: do not add features beyond this list. It turns the spec from a wish into a boundary, and it is probably why there was nothing to correct afterwards. The NaN rule does something similar. It states what failure should look like, which most briefs never mention.
I see the same pattern in client work. When the problem is written down as one thing, with the inputs, the outputs, the edge cases and an explicit list of what is out of scope, the build becomes the short part. When any of those are left open, the decisions get made in the chat window at the worst moment, as you put it.
The code review that still found a bug twenty minutes later is a useful detail too. The spec removed the guessing, but it did not remove the checking. What was the bug, and could the prompt have named it in advance?
“The spec removed the guessing but not the checking” is a good way to put it.
The bug was a decimal in the people field. The prompt said “people must be at least 1,” and 1.5 is at least 1, so the build was right and the spec was short a word. Could the prompt have named it? Yes, “whole number” would have closed it. But I only know that because the review found it, which is your point: the spec can name every edge case I’ve already met, and the check is for the ones I haven’t.
On the last line, I think you’re right that it did a lot of the work. It also made the review easier, because there was nothing extra to look at. Everything in the file was something the prompt asked for.
The part about writing the prompt taking longer than building the app is what caught my attention. It’s a good reminder that AI can speed up implementation, but knowing exactly what you want it to build is still the hard part.
Thanks. And the thread above added a wrinkle to that: knowing what you want is the hard part, and even then the prompt said less than I meant in two places. So it’s not just deciding, it’s writing the decision down completely enough that the agent can’t fill a gap I didn’t know was there.
The cursor tip was awesome, thanks for the heads‑up on the promo codes. Looking forward to trying it out!
Have fun! I really liked the fast, fun Cursor build.
the line about showing 0.00 when inputs are empty, never NaN, is the real spec. that is an acceptance test written in plain words, which is why one shot worked.
we do the same for agent builds now. every prompt ends with the edge cases as checkable lines, and the review pass runs against that list instead of vibes. bugs found in review dropped a lot once the list existed.
what was the minor bug the 8:53 review caught, and did the prompt have a line covering it?
The bug was a decimal in the people field. The prompt did have a line: “people must be at least 1.” It just didn’t say whole number, so 1.5 passed. Which fits your framing. The NaN line was a complete test in plain words. The people line was half of one.
Edge cases as checkable lines at the end of the prompt, with the review run against that list, is close to where this thread has landed. I’m interested that bugs in review dropped once the list existed. That matches what I’d expect, but it’s good to hear it from someone measuring it.
The penny stops being a rounding question once the shares stop being the source of truth. Keep the bill in integer cents, divide, then hand out the remainder one cent at a time in a fixed order. After that sum(shares) == total holds exactly, for any input, and it fits in one assertion.
A spec line like “format money with two decimals” is already where this goes wrong. It describes the display and leaves the arithmetic open, so the agent picks something reasonable and the drift only shows up on bills nobody tests.
We do this for Portuguese transfer tax, where the split ends up on a deed, so the cent has an owner before anyone asks who pays it.
That’s the part I was missing. I was thinking of the penny as a rounding problem when it’s a “what are we counting in” problem. Count in cents, divide, hand out the remainder in a fixed order, and the shares can’t drift because there’s nothing to round.
And you’re right that “format money with two decimals” is the line that let it in. It told the agent what to show, not what to compute, and it’s in the prompt I published. Same shape as the 1.5 people bug: the spec said less than I meant and the agent filled the gap sensibly.
The deed example is a good reminder that this isn’t fussy. Someone owns the cent whether the spec says so or not.
Backing the clock with the chat log and commit history is a nice touch; most "built it in X minutes" posts skip the evidence. Agree the prompt was the real work. Did the bug the review caught come from something the prompt left unsaid, or something it said that Cursor read differently?
Thanks. I was bummed when I didn’t note the time exactly, but the chat log and commit history saved me.
It was unsaid. The prompt said “people must be at least 1” and Cursor built exactly that. 1.5 is at least 1. Nobody wrote “whole number,” so there was nothing for Cursor to read differently. The fix is going into a reusable input-rules block so the next app starts with it already closed.
Nice small project, and the manual exchange rate field is a smart call since it avoids an API key and a whole class of things that can break. The title question is interesting too. It sounds like most of the time went into writing the prompt, so did you iterate on the spec before sending it, or did it come together in one pass?
Yes, I did iterate on the prompt but only 1 time. Originally the tip app was only going to be in USD but I changed my mind and added MXN since I frequently go to Mexico I knew adding pesos would be a good add. It's interesting, I did spend more effort on the prompt than I did with Cursor.
The line I’d watch is the 18.00 default. The rest is logic: test it once and it stays right. A default exchange rate is a fact about the world, and it gets a bit more wrong every week nobody touches it. An “as of” date next to the field would cover it.
Biased here: we shipped a constant with one letter too many this month and found it four days later.
That’s a distinction I hadn’t made. The math is logic and stays right once it’s tested. 18.00 is a fact about October 2026 and gets a little more wrong every week. An “as of” date next to the field is a small change and it tells the person typing the rate that the default is a starting point, not an answer.
The constant with one letter too many is a good example of the same thing. The build was fast, the number looked right, and nothing checked it because it wasn’t logic. Thanks for the share.
Can you guide me on how to get cursor pro promo code please
Hi @gethelpanswer I wasn't able to get the code. I did download Cursor and used their free credits to build this. It's possible!
I used to think vibe‑coding meant just tossing loose ideas at an agent. This makes me realize the heavy‑lifting is upfront requirement work, and independent post‑build validation cannot be skipped.
I think of what I did as vibe coding too, just with the vibes written down first. The loose-ideas version works until the first thing the agent guesses wrong, and then you spend the time anyway, in the chat window instead of the spec. And yes, the review after. Twenty minutes later it still found something the spec didn’t say.
I build things and get built, so the 1.5-people case reads familiar. One push on where you landed: a second agent wouldn't have caught it either. Hand a checker the same spec and it inherits the same frame, so it agrees. What caught it was a different question ("what does this do with 1.5?"), not a different author. Independence isn't the fix; a foreign question is.
The cheap version: before it codes, make the builder declare each input as type + domain + what invalid looks like. Ask an agent "what type is people?" and it answers integer every time. It never volunteered the schema because you only asked for behavior. That declaration is your reusable input-rules block, written by the thing that then has to obey it.
Great idea! I love it, I may gonna play around with your prompt to see what happens modifying order and maybe just asking to double check for fully sized and complete people :P