skills / other

grilling

what it does

Grill the user relentlessly about a plan or design. Use when the user wants to stress-test a plan before building, or uses any 'grill' trigger phrases.

Verified Before and after: 2 models 1 try per case View source Download .zip

Compare the answers

Answers from Claude Sonnet 5.5 and GPT-6.1 Sol · try 1 of 1

Same request, same model, once with the skill and once without. Read both and judge for yourself.

“I'm about to build rate limiting for our public API. Stress-test the design with me first.”

Source material given with the request

Design draft: - Limit: 100 requests per minute per API key - Counters stored in memory on each API server - We run 6 API servers behind a round-robin load balancer - Over the limit: return HTTP 429 with a Retry-After header - Enterprise keys get 1,000 per minute
with the skill
The biggest hole is the interaction between "counters in memory on each API server" and "6 servers behind round-robin", so I'll start there. With independent counters, each server lets a key make 100 requests per minute. Round-robin spreads requests roughly evenly, so a client can make about **600 requests per minute** before any server returns a 429 (6,000 for enterprise). The counters also reset on every deploy or restart, and they drift as servers are added or removed (autoscaling would change the effective limit). **Question 1: How strict does the 100/min limit need to be?** Is it a hard contract (billing, abuse protection, a promised SLA) or a rough guardrail where being off by some margin is acceptable? My recommendation is to treat it as a hard limit and move the counters to a shared store, such as Redis with an atomic increment or a token bucket in a Lua script. That costs one extra network hop per request (typically around 1 ms), but the limit becomes accurate, survives deploys, and doesn't depend on server count. The alternative is to keep local counters and set each server's limit to 100/6 ≈ 17. That is cheap, but it breaks with uneven traffic, with a client pinned to one server by keep-alive connections, and whenever the server count changes. I'd only choose it if approximate limiting is truly acceptable. Which way are you leaning?
without
# Stress test: the design has one serious flaw and several gaps ## 1. In-memory counters across 6 servers (the main problem) Each server counts independently, so the limit isn't 100/min, it's up to **600/min** (6,000 for enterprise). It's also inconsistent: - Round-robin is only even on average. A client can get a 429 from one server while the other five would still accept the request, so the same client sees flaky behavior. - Deploys, crashes, and autoscaling reset counters. A restart hands out fresh quota. - Dividing the limit by N (about 17/server) fixes the overage but breaks when N changes or traffic is uneven. It also punishes bursty clients unfairly. **Options:** | Approach | Tradeoff | |---|---| | Central store (Redis) with atomic ops | Accurate; adds ~1ms and a dependency | | Sticky routing by API key at the LB | No shared state, but hot keys overload one server and failover resets counters | | Local counters with periodic sync | Fast, approximate; more complex, so only worth it at very high scale | For 6 servers and a public API, Redis is the pragmatic default. ## 2. Algorithm choice
Show the rest (47 more lines)
"100 per minute" is underspecified: - **Fixed window** allows 2x bursts at window boundaries (100 at 12:00:59, 100 at 12:01:00). - **Sliding window counter** or **token bucket** is smoother. Token bucket also lets you separate sustained rate from burst allowance. - Do you want a short-term cap too (e.g., 20/sec)? 100 requests in one second might hurt your backend even though it's "within limits." ## 3. If you add Redis, decide these up front - **Failure mode:** If Redis is down, do you fail open (no limiting) or closed (outage)? Usually fail open, with a local per-server fallback limit and an alert. - **Atomicity:** Use a Lua script or an atomic command. A naive `INCR` then `EXPIRE` can leave keys with no TTL if the process dies between them. - **Clock source:** Don't rely on app-server clocks for window boundaries. Use Redis time or TTL-relative logic. - **Latency budget:** This adds a network hop to every request. Set a tight timeout (e.g., 5–10ms) and fail open past it. ## 4. Things the draft doesn't cover - **Invalid or missing keys:** If you only limit by valid key, attackers can hammer you with random keys. Add a coarse per-IP limit *before* key lookup, and don't create counters for unrecognized keys (memory growth). - **Per-key vs per-account:** If enterprise customers or free users can make multiple keys, per-key limits are trivially bypassed. Decide what the billing/abuse unit actually is. - **Tier lookup:** Don't hardcode "enterprise = 1,000." Store limits per key or plan, cache them, and allow per-customer overrides. Someone will ask for 2,500 within a month. - **Request cost:** A cheap GET and an expensive search or export count the same. Consider weighted costs or separate limits for expensive endpoints, plus a concurrency limit. - **Do 429s count?** If rejected requests consume quota, a client that retries aggressively never recovers. Usually they shouldn't count, but then you need to be sure the rejection path is cheap. - **Volumetric abuse:** Per-key limiting isn't DDoS protection. That belongs at the CDN/WAF/edge. ## 5. Client-facing behavior - `Retry-After` must be accurate. With local counters it's wrong by construction. With token bucket, compute time until the next token. - Return rate-limit headers on **all** responses, not just 429s (`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, per the IETF draft, or the common `X-RateLimit-*`). Clients can then self-throttle. - Thundering herd: if 500 clients all get "retry in 12s," they all return at once. Document that clients should add jitter, and consider adding some server-side. - Use a structured error body with a stable error code. ## 6. Rollout and observability - Ship in **shadow mode** first: compute and log "would have limited" without enforcing. This shows how many real customers you'd break, which is often a surprise. - Metrics: limit hits per key/tier, limiter latency, Redis errors, fail-open count. - Plan for customer communication, since someone currently doing 300/min will start getting 429s. ## Where I'd start Redis-backed token bucket (or sliding window counter) via Lua script, limits looked up from plan config, fail-open with local fallback, IP limiting ahead of auth, standard headers, and a shadow-mode launch. ## Questions that would change the design 1. Is the limit a **billing/contractual guarantee** or just **backend protection**? Exactness matters much more for the former. 2. Do you already run Redis (or similar), and is the API multi-region? 3. Do you autoscale, or is it a fixed 6 servers? 4. Is the limit per key or per customer account? 5. Do you have endpoints with wildly different costs? Which of these do you want to dig into first?

Answers are shown exactly as the model wrote them, from the first try, and nothing is picked by hand.

The models

2 models

Claude Sonnet 5.5

made by Anthropic

claude-sonnet-5-5 · revision not reported by the provider

1 try per case · tested 3 Nov 2026

GPT-6.1 Sol

made by OpenAI

gpt-6.1-sol · revision not reported by the provider

1 try per case · tested 3 Nov 2026

The tasks

written by Carte Blanche
on-call-rotation-change

“We're changing our on-call setup next month. Interview me about the plan until you're sure it holds up.”

pricing-change-plan

“Before I take this to the founders, grill me on it.”

rate-limiter-design

“I'm about to build rate limiting for our public API. Stress-test the design with me first.”

recipe-app-plan

“Grill me on this plan before I start building.”

second-bakery-location

“I'm opening a second location for my bakery next spring. Poke holes in the plan, but go one thing at a time so I can actually think.”

Install

Download the .zip and unzip it. Put its files, in a folder named grilling, here:

Claude Code~/.claude/skills/Every project. For one project only, use .claude/skills/ inside it.
Codex~/.agents/skills/Every project. For one repository only, use .agents/skills/ inside it.
Cursor~/.cursor/skills/Cursor also reads the Claude Code and Codex folders above.

Then start a new session and ask for what the skill does. Other agents that read SKILL.md have their own skills folder; check their docs.

Coming after launch: get skills from inside your agent, no download needed.