Checkout coupon code: from loose request to spec packet

This is the homepage example expanded into a full case: one vague request becomes a bounded spec, implementation slices, acceptance criteria, and evidence reviewers can check before merge.

RiskMoney, totals, invalid states
BoundaryOne coupon before payment
EvidenceValidation, total, rollback checks

The request before the spec

Weak ticket

Add coupon codes to checkout.
Make sure invalid codes do not break payment.

Spec-first rewrite

Feature: Checkout coupon code
Owner: Web Checkout
Status: Ready for review

Context:
- Checkout UI lives in apps/web/routes/checkout.
- Totals are computed in packages/billing/totals.ts.
- Coupon table exists but is not wired into checkout.

Goal:
- Let signed-in users apply one valid coupon before payment.
- Show invalid, expired, and already-used codes inline.

Non-goals:
- No coupon admin UI.
- No coupon stacking.
- No rewrite of billing totals.

The packet reviewers actually need

tasks.md

- [ ] Add coupon lookup by code and user.
- [ ] Validate expired, disabled, and reused codes.
- [ ] Recompute checkout total with one discount.
- [ ] Show inline errors without clearing payment form.
- [ ] Add feature flag for rollback.

acceptance-criteria.md

- Given a valid coupon
  When the user applies it before payment
  Then the discounted total is shown and used for payment.

- Given an expired code
  When the user applies it
  Then an inline error appears and the original total remains.

test-evidence.md

Automated:
- coupon validation unit test
- checkout total integration test
- reused coupon regression test

Manual:
- screenshot for valid code
- screenshot for invalid code
- rollback flag documented

AI coding guardrail

Implement only coupon application at checkout.
Do not add admin UI, stacking, price rules, or totals refactors.
Every changed file must map to one task and one acceptance criterion.

Why this catches rework early

It protects billing boundaries

The spec names the totals module and blocks unrelated refactors before an AI coding tool broadens the task.

It makes errors testable

Expired, reused, and invalid coupons become concrete acceptance paths rather than late QA discoveries.

It gives release a stop signal

The rollback flag and evidence list tell reviewers how to pause the change if checkout metrics move.

Use this pattern on your next checkout change

Start with the generator, then paste the packet into the issue or PR before asking an AI coding assistant to implement it.

Editorial note

This case is intentionally small and concrete: the goal is to show the spec boundary that prevents AI-generated implementation drift.