{"id":"code-dev-ts-spec-to-typed-stub","audience":"developers","category":"coding","track":"coding","technique":"constraints","title":"Turn a rate-limit spec into a typed TypeScript stub","free":false,"task":"You're splitting the rate-limiter ticket: you write the types and signature today, and Ines implements it tomorrow. Your team's names are fixed: an exported type LimitDecision with exactly allowed: boolean and retryAfterMs: number | null, and an exported function checkLimit(userId: string, now: Date): LimitDecision whose body is only throw new Error(\"not implemented\"). Reply with exactly 1 code block of at most 10 lines, no imports or classes, and under 20 words of text.","materialLabel":"The spec from the ticket (attached to your prompt automatically)","material":"TICKET API-318: Per-user rate limit for the export endpoint\nReporter: Gil Mendes   Sprint: 44   Repo: lumen-api (TypeScript 5.4, Node 20)\n\nProblem\nSome users hit POST /exports hundreds of times a minute from a script,\nand the export workers fall over. We need a per-user limit.\n\nRules\n- Each user gets 10 export requests per rolling 60 seconds.\n- Request number 11 inside the window is refused.\n- When refused, tell the client how many milliseconds until the oldest\n  request in the window expires, so the UI can show \"try again in 12s\".\n- When allowed, there's nothing to wait for.\n- Admin users are NOT exempt (we checked with Priscila).\n- Storage: in memory for now; Redis later (API-322).\n\nNotes from refinement\n- Someone suggested returning a plain boolean; rejected, the UI needs\n  the wait time.\n- Gil wants the time passed in so tests don't need fake timers.\n- We'll call it from the route handler like:\n    const decision = checkLimit(req.user.id, new Date());\n    if (!decision.allowed) return res.status(429).json(decision);\n\nOut of scope\n- Different limits per plan\n- Limits on other endpoints\n- Persisting counts across restarts\n\nAcceptance\n- 10 calls in 60s allowed, 11th refused with a wait time.\n- After the window passes, calls are allowed again.","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":10},{"type":"maxProseWords","max":20}],"mistakes":[{"title":"Getting a full implementation","why":"Ask for 'this function' and the AI builds the whole limiter with a Map and timestamps, which is tomorrow's ticket for someone else.","fix":"Say: \"Stub only: the body is just throw new Error(\\\"not implemented\\\").\"","checks":["r2","maxCodeLines"]},{"title":"Letting the AI pick names","why":"The AI invents names like RateLimitResult or retryAfter, and the route handler won't compile against them.","fix":"Give them: \"type LimitDecision { allowed: boolean; retryAfterMs: number | null }\".","checks":["r0"]},{"title":"A different signature","why":"The AI may read the time itself or take a request object, which breaks Gil's no-fake-timers plan.","fix":"Add: \"Signature exactly checkLimit(userId: string, now: Date): LimitDecision.\"","checks":["r1"]},{"title":"Extras around the code","why":"The AI adds JSDoc, usage examples and a design note.","fix":"Add: \"One code block, no imports, no comments, no text.\"","checks":["codeBlocks","maxProseWords"]}],"promptHints":[{"label":"Asks for a stub only","patterns":["stub","not implemented","no (logic|implementation)","signature"]},{"label":"Names the return type","patterns":["LimitDecision"]},{"label":"Gives the fields and types","patterns":["retryAfterMs","number \\| null","allowed"]},{"label":"Gives the exact signature","patterns":["checkLimit\\(","userId: string","now: Date"]},{"label":"Limits the extras","patterns":["no imports","no comments","only the code","one code block"]}],"hint":"A stub is useful only if the names match what the next person codes against. Spell out the type, the signature and that the body stays empty.","rubricLabels":["Exports a type (or interface) named LimitDecision with exactly two fields, allowed: boolean and retryAfterMs: number | null, and nothing else","Exports function checkLimit(userId: string, now: Date): LimitDecision with exactly those parameter names, types and return type","The function body is only throw new Error(\"not implemented\"): no rate-limit logic, storage, Map, constants, classes or imports anywhere"]}