Verified against Cursor · 2026-08-13
Brief Cursor's Agent mode to build a Node worker service that survives a restart mid-job
A brief for a Node.js background or queue-consumer service that states idempotency requirements, graceful shutdown behavior, and what 'processed' actually means, so Agent mode doesn't ship a worker that quietly double-processes a job or drops one on restart.
The prompt
Ready to copy — highlighted parts are example details you can swap.
SERVICE invoice-export-worker — a Node background worker that generates a PDF invoice for a completed order and uploads it to the customer-facing document store. QUEUE / TRIGGER SOURCE An SQS queue with at-least-once delivery and no ordering guarantee across messages. WHAT COUNTS AS "PROCESSED" — the exact moment a job may be considered done and safely acknowledged The generated PDF's checksum has been written to the invoices.exports table with status='complete' — not merely that the upload API call returned 200. IDEMPOTENCY REQUIREMENT A second delivery of the same orderId must not generate or upload a second PDF; it should detect the existing complete export and no-op. FAILURE MODE TO AVOID a customer receiving two different invoice PDFs for the same order, or an order silently never getting an invoice at all Build this so a process restart, a crash mid-job, or a duplicate delivery from An SQS queue with at-least-once delivery and no ordering guarantee across messages. never produces a customer receiving two different invoice PDFs for the same order, or an order silently never getting an invoice at all. Specifically: 1. Only acknowledge or remove a job from the queue after the PROCESSED_DEFINITION above is actually true, not after the work is merely dispatched or after an in-memory step completes that hasn't been durably recorded yet — an early acknowledgment is how a crash between "dispatched" and "durably done" loses a job silently. 2. Implement the idempotency requirement as a real check against durable state (a processed-ids table, a unique constraint), not a best-effort in-memory guard that resets to empty on every restart — an in-memory-only guard protects against a same-process retry and nothing else, which is not the case that actually matters here. 3. Handle SIGTERM by stopping acceptance of new jobs, finishing or safely abandoning the in-flight one according to PROCESSED_DEFINITION, and exiting — do not let the process get killed with a job half-applied and no record of how far it got. 4. Log enough at each job's start and completion to answer "was this specific job id processed, and exactly once, or not" from logs alone after the fact, without needing to reproduce the failure to find out. WHEN DONE Walk through what happens, step by step, if the process is killed at three points: before dispatch, mid-processing, and after processing but before acknowledgment. State explicitly whether each scenario results in the job being lost, double-processed, or correctly handled exactly once.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
A straightforward "build a worker that processes X from a queue" brief tends to produce code that's correct on the happy path — a message arrives, gets processed, gets acknowledged — because that's the only path a quick manual test naturally exercises, while the failure modes that actually matter for a background service, a restart mid-job, a duplicate delivery, a crash between two specific steps, never surface under that kind of testing since they require literally killing the process at a precise moment, which nobody does by hand during a normal review. Stating a literal PROCESSED_DEFINITION turns "handle it correctly" into a checkable line: does the code acknowledge before or after that specific durable condition, rather than an implicit assumption that's easy to get subtly wrong — acknowledging on dispatch instead of on durably-recorded completion is a one-line difference that looks harmless in a diff and only manifests as silent data loss under a crash that's hard to reproduce on demand. The instruction to implement idempotency against durable state rather than in-memory targets a specific and common shortcut: an in-memory Set of processed ids is trivial to write, passes any test that doesn't literally restart the process, and provides zero protection against exactly the restart scenario the idempotency requirement exists for in the first place — the two implementations look identical in a demo and diverge only in production, under precisely the condition hardest to catch in code review. Requiring the walk-through of process-killed-at-three-points at the end forces explicit reasoning about the code's failure behavior instead of letting a reader assume it handles it because the happy path looks fine — a background worker's actual correctness lives almost entirely in these restart-boundary cases, which a review skimming the main logic will not naturally exercise.
What you get back
Process killed before dispatch: message remains unacknowledged in SQS, redelivered after visibility timeout — no job lost, no duplicate created since nothing started.
Process killed mid-processing (PDF generated, not yet uploaded): message still unacknowledged, redelivered. On redelivery, checks invoices.exports for this orderId, finds no 'complete' row, re-generates and uploads — correctly handled, no duplicate customer-facing invoice since the previous attempt never reached upload.
Process killed after upload but before acknowledgment: message redelivered. Checks invoices.exports, finds a 'complete' row with a matching checksum already written before the crash, no-ops and acknowledges — correctly handled exactly once, no second PDF generated or uploaded.
Verified against
Cursor 2.2 · 2026-08-13
Changelog
- 2026-08-13 — Initial publish, verified against Cursor 2.2 Agent mode building an SQS-backed Node worker.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
