Nobody has started this yet — be first.
Business impact
This project's entire pitch is "you can observe retry/idempotency/DLQ behavior with zero external dependencies" via the built-in flaky downstream simulator. If the simulator quietly drifts from what it's documented to do, every curl walkthrough, demo, and integration test that relies on "429 on attempts 1-2, success on attempt 3" (the README's own simulated-profiles table) gets a different attempt count than advertised. That's a small, contained blast radius today, but it's exactly the kind of drift that erodes trust in a staging/demo environment over time -- QA or a new teammate re-running the documented walkthrough gets confused by a result that doesn't match the docs, and starts questioning whether the real bug is somewhere else entirely.
Problem
The rate-limited-429 simulated failure profile is documented (and the package doc comment on flakyDownstreamHandler describes) as: attempts 1 and 2 return 429 Too Many Requests with a Retry-After header, attempt 3 and beyond return 200 OK. In the current code, only attempt 1 returns 429 -- attempt 2 already succeeds.
Current behavior
POST /internal/flaky-downstream with X-Simulate: rate-limited-429 and X-Attempt: 2 returns 200 OK.
Expected behavior
The same request should return 429 Too Many Requests (with Retry-After: 1) -- only X-Attempt: 3 and beyond should return 200.
Steps to reproduce
go run ./cmd/server &
curl -i -X POST localhost:8080/internal/flaky-downstream
-H "X-Simulate: rate-limited-429" -H "X-Attempt: 2"
Why this matters
A downstream that recovers a full attempt "early" compared to what's documented means anything timing its expected retry count against the README (a demo script, an onboarding exercise, this very task list's own task 15 test, which forces early exhaustion and depends on knowing exactly which attempt succeeds) gets a result one attempt off from what it expected.
Suggested approach
Look at the "rate-limited-429" case in flakyDownstreamHandler (internal/server/flaky.go) and compare its attempt-boundary condition to the "fail-twice-then-succeed" case immediately below it -- one of the two uses a different comparison operator than the other, and only one of them matches the documented "fails attempts 1-2" contract.
Acceptance criteria
Verification
go test ./test/ -run TestTask01 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/rate-limited-429-off-by-oneFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/rate-limited-429-off-by-oneSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8080POST /v1/chargesTo run against Redis (the multi-replica-safe backend) with zero local setup:
docker compose up --build # app + redis, hot-reloading on save
Work the tickets in tasks/ (ordered easy → hard); each names one Go test in test/:
make test-race # go test -race -count=1 ./...
go test ./test/ -run TestTask01 -v # a single ticket
Level 1
Fix a bug
Read existing behaviour, correct it.