Simple outside. Rigorous inside.
The visual system for Receipts and static mockups of its two screens. Contrast below is computed from the token values when the page renders, not typed in by hand.
Principles
Borrowed from how Apple makes complex things feel obvious.
- One thing per screenEach screen has a single primary action in black. Everything else steps back.
- Plain wordsFound, Partly, Missing, Off-limits, Can’t tell. No jargon, no scores.
- Detail on requestSummaries first. Lines of code, reasons and sources open when you ask for them.
- Quiet motionThings rise in gently and sheets float up. All of it stops if you prefer less motion.
Screen 1: what you asked for
What you said on one side, the receipt on the other. A vague phrase becomes a question for you, not a requirement.
Add rate limiting to the login route, five attempts per minute per IP, and return a 429 with Retry-After. It should survive a restart. Do not touch the signup flow. Add tests. And maybe log the blocked attempts?
What you asked for
- R1Limit POST /api/login to 5 attempts per minute per IP address.Done when A 6th attempt inside 60 seconds returns 429.
- R2Blocked responses include a Retry-After header.Done when The header value is the seconds left in the window.
- R3Tests cover the limit and the window reset.
- R4Limiter state survives a server restart.
Leave alone
N1Do not change the signup flow.
Needs your answer
Q1Should blocked attempts be logged?
You said "maybe", so it was not turned into a requirement.
Screen 2: the check
One answer per item, with the lines that support it, then what your agent claimed. Tap any status to open its sheet.
- Pasted changes
- Your agent's summary
R1Limit POST /api/login to 5 attempts per minute per IP.
A limiter keyed by client IP guards the login handler.
src/app/api/login/route.tsL14-L18if (!limiter.allow(ip)) return tooManyRequests(limiter.retryAfter(ip))R2Blocked responses include a Retry-After header.
The header is set, but no test checks it.
src/server/http/responses.tsL31headers: { 'Retry-After': String(seconds) }R3Tests cover the limit and the window reset.
None of the changed files are tests.
R4Limiter state survives a server restart.
Where the limiter stores state is set by config that is not in the changes.
What your agent said
- “Added tests for the limiter.”0 of 3 changed files are tests.Not true. The evidence says otherwise.
- “All tests pass.”No test output was provided.Can't tell. Nothing you provided proves it either way.
- “Signup is untouched.”No signup files changed.Backed up. The evidence agrees with what the agent said.
Controls and sheets
Pill buttons with an orange focus ring (press Tab). Status badges open a frosted sheet that rises over a soft blur. Escape or a tap outside closes it.
Material
Frosted glass with a bright top edge, over soft ambient light. It turns solid when blur is unsupported or reduced transparency is on. Code always sits on a solid surface.
Frosted panel
Whatever sits behind is softened, so text on top stays easy to read.
Type
Geist for words, Geist Mono for code and commit hashes. Large, tight headlines. Calm 15 to 17 pixel body text.
Proof, not promises.
Rate-limit the login endpoint
Body text is generous so a long receipt still reads easily.
src/app/api/login/route.ts L14-L18
Colour and contrast
White, black and one orange. Orange is a fill, an accent and the focus ring. Orange text uses a deeper shade so it stays readable. Every pair passes WCAG AA, and a test keeps it that way.
| Sample | Tokens | Use | Contrast |
|---|---|---|---|
| Body text | --ink on --canvas | All primary text | 17.39:1 AAA |
| Secondary text | --muted on --canvas | Descriptions, metadata | 5.31:1 AA |
| Primary button | --canvas on --ink | Main call to action | 17.39:1 AAA |
| Accent button | --accent-on on --accent | Orange fill, black text | 5.91:1 AA |
| Orange text | --accent-text on --surface | Requirement codes, emphasis | 5.18:1 AA |
| Evidence found | --ok on --ok-wash | Verdict badge | 4.76:1 AA |
| Partial | --warn on --warn-wash | Verdict badge | 4.84:1 AA |
| Not found | --bad on --bad-wash | Verdict badge | 5.91:1 AA |
| Violates non-goal | --breach on --breach-wash | Verdict badge | 6.88:1 AA |
| Unverifiable | --unknown on --unknown-wash | Verdict badge | 7.03:1 AAA |
| Sample | Tokens | Use | Contrast |
|---|---|---|---|
| Body text | --ink on --canvas | All primary text | 18.15:1 AAA |
| Secondary text | --muted on --surface | Descriptions, metadata | 7.13:1 AAA |
| Accent button | --accent-on on --accent | Orange fill, black text | 6.94:1 AA |
| Orange text | --accent-text on --surface | Requirement codes, emphasis | 7.93:1 AAA |
| Evidence found | --ok on --ok-wash | Verdict badge | 9.83:1 AAA |
| Partial | --warn on --warn-wash | Verdict badge | 10.39:1 AAA |
| Not found | --bad on --bad-wash | Verdict badge | 6.64:1 AA |
| Violates non-goal | --breach on --breach-wash | Verdict badge | 7.92:1 AAA |
| Unverifiable | --unknown on --unknown-wash | Verdict badge | 6.63:1 AA |