Inngest Brownfield Audit
Use this skill when asked to inspect an existing codebase, add
Inngest "where it makes sense", migrate fragile background work, or find
durability gaps before making changes.
This is an agent-first workflow. Do the audit from evidence in the repo, name
the specific files and call sites that drove each conclusion, and make small
integration moves that preserve current behavior.
When to Trigger
Use this skill for requests like:
- "Audit this repo for Inngest opportunities"
- "Add Inngest to this codebase"
- "Make our webhooks / cron jobs / background tasks reliable"
- "Find places where work can be lost on deploy or process crash"
- "Replace fragile polling, delayed jobs, or fire-and-forget promises"
- "Make this AI workflow / agent durable"
If the user is starting from scratch instead of a brownfield repo, use
inngest-setup, inngest-durable-functions, inngest-events,
inngest-steps, and, for AI workflows, the agent patterns in this skill. Use
inngest-agent-evals when the request includes scoring, sessions, experiments,
deferred scorers, Insights, or outcome-based evaluation.
Audit Loop
Map the project shape.
- Read
package.json, workspace files, app/router structure, server entry
points, deployment config, and test scripts.
- Identify framework: Next.js App Router, Next.js Pages Router, Express,
Hono, Fastify, Remix, SvelteKit, Astro, NestJS, worker-only service, or
other.
- Detect package manager and TypeScript conventions before adding files.
Find existing Inngest usage.
- Search for
inngest, createFunction, serve(, /api/inngest,
INNGEST_, step.run, step.sleep, step.waitForEvent,
step.sendEvent, step.invoke, step.ai, inngest.send, and
@inngest/realtime.
- If Inngest exists, inspect version, client config, serve endpoint,
registered functions, event naming, env vars, and v3/v4 API shape before
changing anything.
Find durability gaps.
- Search for fire-and-forget work:
void someAsync(), un-awaited promises,
.then( chains, setTimeout, setInterval, detached jobs after HTTP
response, and background work in route handlers.
- Search for cron and schedulers:
cron, node-cron, agenda, bull,
bullmq, bee-queue, qstash, sqs, temporal, trigger.dev,
deployment cron config, and scheduled API routes.
- Search for webhooks and at-least-once producers: Stripe, Clerk, GitHub,
Slack, Shopify, HubSpot, Linear, Svix, and generic
webhook.
- Search for long-running work: PDF generation, exports, video/image
processing, embeddings, bulk email, imports, ETL, sync jobs, polling loops,
retries, and external API calls.
- Search for AI agent shapes: tool loops, LLM calls, streaming tokens,
human approval, multi-step reasoning, vector search, eval loops, scoring,
experiment assignment, user-feedback signals, and provider calls that need
rate limits or retry-safe state.
Classify each candidate.
- P0: user-visible loss, duplicate charge/email/action, timeout, missed
webhook, or crash-prone workflow.
- P1: fragile but recoverable background work, manual retry burden,
noisy 429s, or poor observability.
- P2: cleanup, ergonomics, or future migration opportunity.
- For each candidate, record: file, current trigger, side effects,
idempotency key, failure mode, recommended Inngest primitive, migration
size, and confidence.
Choose the smallest safe integration.
- Prefer one vertical slice over a wide rewrite.
- Keep existing domain functions and data models where possible.
- Add an Inngest client and serve endpoint only once.
- Move side effects into
step.run one boundary at a time.
- Make event IDs and database writes idempotent before adding retries.
- Add tests around existing behavior and the new event/function boundary.
Useful Discovery Commands
Run commands that fit the repo. Prefer rg; keep output focused.
rg -n "inngest|createFunction|step\\.|serve\\(|/api/inngest|INNGEST_" .
rg -n "setTimeout|setInterval|Promise\\.all|void [a-zA-Z0-9_]+\\(|\\.then\\(" .
rg -n "cron|node-cron|schedule|bull|bullmq|bee-queue|agenda|qstash|sqs" .
rg -n "webhook|stripe|svix|clerk|github|shopify|slack|hubspot|linear" .
rg -n "retry|backoff|poll|status|timeout|429|rate limit|rate-limit" .
rg -n "openai|anthropic|ai\\.|generateText|streamText|tool|agent|embedding" .
When the repo is large, narrow searches to app source directories and exclude
generated/vendor folders.
Brownfield Decision Matrix
| Existing shape |
Inngest fit |
Primary primitives |
| HTTP handler does slow side effects before responding |
Emit event, return fast |
inngest.send, event trigger, step.run |
| Webhook must acknowledge quickly but process reliably |
Verify signature, emit idempotent event |
Event ID, step.run, retries |
| Cron job loses progress midway |
Cron-triggered durable function |
Cron trigger, page-level step.run, flow control |
| Polling loop waits for external async work |
Durable wait or durable poll |
step.waitForEvent, step.sleep, step.run |
| Large fan-out exceeds request/serverless limits |
Split orchestration and item work |
step.sendEvent, per-item function, concurrency |
| External API hits 429s |
Move limits to function config |
throttle, rateLimit, concurrency |
| Human review can take days |
Persist the wait in Inngest |
step.waitForEvent, timeout, realtime |
| AI agent/tool loop needs retry-safe progress |
One step per tool/model boundary |
step.ai, step.run, step.sleep, realtime |
| AI workflow needs production evals |
Attach outcome signals to durable runs |
meta.sessions, step.score, createScorer, defer, group.experiment |
| Existing queue only hides fragile work |
Replace queue boundary gradually |
Event trigger, idempotency, function-level retries |
Integration Plan Format
Before editing, summarize findings in this compact shape:
Inngest audit:
- Existing Inngest: none / partial / healthy / risky
- Framework: <framework and evidence>
- Best first slice: <file + workflow>
- Why: <loss/timeout/retry/idempotency failure>
- Proposed primitives: <event, steps, flow control, waits, realtime>
- Idempotency key: <source of truth>
- Files likely touched: <short list>
- Tests/checks: <commands or focused cases>
Then implement unless the user asked for audit-only.
Existing Inngest Checklist
If Inngest is already present, verify:
- A single shared client is exported from a stable module.
- The app
id is a stable slug and is not derived from deploy-specific data.
- v4 local development uses
INNGEST_DEV=1; production uses
INNGEST_SIGNING_KEY.
- Serve endpoint path is discoverable, usually
/api/inngest.
- The serve handler registers all functions that should sync.
- Side effects and non-deterministic work are inside steps.
- Step IDs are stable and descriptive.
- Event names follow
domain/noun.verb.
- Events that may be replayed use deterministic IDs.
- Webhook handlers verify signatures before emitting events.
- Flow control is configured where external APIs have limits.
- Realtime uses v4 native
inngest/realtime, not the v3
@inngest/realtime package.
Durable Agent Patterns
Use Inngest when an AI or agent workflow needs durable progress across model
calls, tool calls, waits, approvals, or streaming UI updates.
Good candidates:
- Multi-step agent that calls tools or external APIs.
- LLM workflow that may exceed one HTTP request lifetime.
- Human-in-the-loop review, approval, correction, or escalation.
- Agent that must pause for an external event or scheduled follow-up.
- Bulk AI work that needs provider-level rate limits and cost protection.
- User-visible agent progress that should stream from durable execution.
Recommended shape:
- HTTP/UI request stores the user intent and emits an event with a stable
id.
- Inngest function loads state inside
step.run.
- Each model call, tool call, vector search, and external side effect lives in
its own
step.ai or step.run boundary.
- Human pauses use
step.waitForEvent or step.waitForSignal with a timeout.
- Progress updates use
step.realtime.publish between steps, or
inngest.realtime.publish inside an existing step.run.
- Provider rate limits use
concurrency, throttle, or rateLimit, not
ad hoc in-process throttlers.
Avoid:
- Keeping agent state only in memory.
- Retrying whole agent loops after a single tool failure.
- Charging for repeated successful model calls because the result was not
memoized.
- Using
setTimeout or a cron poller for follow-ups and approvals.
- Streaming progress from a process-local WebSocket server when the workflow
itself is durable elsewhere.
Implementation Guardrails
- Do not replace working queues, crons, or webhooks blindly. First preserve
behavior with a thin Inngest slice.
- Do not create duplicate clients or serve endpoints if the repo already has
them.
- Do not put database writes, API calls, random IDs, timestamps, or LLM calls
outside steps in the new function.
- Do not hide missing idempotency behind retries. Retries require idempotent
side effects.
- Do not hardcode secrets or dev-mode flags in source.
- Do not leave the app unable to sync: register new functions with the serve
endpoint and run available type/tests.
Verification
Pick checks that prove the integration path:
- Typecheck/build/lint the touched app.
- Run existing tests around the migrated handler or workflow.
- Add focused tests for "handler emits event and returns fast" and "function
calls the same domain operations in step boundaries" where the repo supports
it.
- If local runtime is available, start the app and Inngest dev server, confirm
the function syncs, then send a sample event.
- If only static checks are available, explicitly state that runtime sync was
not verified.
1---2name: inngest-brownfield-audit3description: Use when analyzing an existing TypeScript or JavaScript codebase to decide where and how to introduce Inngest. Covers repository discovery, framework and package detection, finding durability gaps in HTTP handlers, webhooks, cron jobs, queues, long-running jobs, AI agents, Agent Evals, polling loops, eval loops, and side-effect-heavy code, then producing and implementing an incremental integration plan.4---5
6# Inngest Brownfield Audit
7
8Use this skill when asked to inspect an existing codebase, add
9Inngest "where it makes sense", migrate fragile background work, or find
10durability gaps before making changes.
11
12This is an agent-first workflow. Do the audit from evidence in the repo, name
13the specific files and call sites that drove each conclusion, and make small
14integration moves that preserve current behavior.
15
16## When to Trigger
17
18Use this skill for requests like:
19
20- "Audit this repo for Inngest opportunities"
21- "Add Inngest to this codebase"
22- "Make our webhooks / cron jobs / background tasks reliable"
23- "Find places where work can be lost on deploy or process crash"
24- "Replace fragile polling, delayed jobs, or fire-and-forget promises"
25- "Make this AI workflow / agent durable"
26
27If the user is starting from scratch instead of a brownfield repo, use
28`inngest-setup`, `inngest-durable-functions`, `inngest-events`,
29`inngest-steps`, and, for AI workflows, the agent patterns in this skill. Use
30`inngest-agent-evals` when the request includes scoring, sessions, experiments,
31deferred scorers, Insights, or outcome-based evaluation.
32
33## Audit Loop
34
351. **Map the project shape.**
36 - Read `package.json`, workspace files, app/router structure, server entry
37 points, deployment config, and test scripts.
38 - Identify framework: Next.js App Router, Next.js Pages Router, Express,
39 Hono, Fastify, Remix, SvelteKit, Astro, NestJS, worker-only service, or
40 other.
41 - Detect package manager and TypeScript conventions before adding files.
42
432. **Find existing Inngest usage.**
44 - Search for `inngest`, `createFunction`, `serve(`, `/api/inngest`,
45 `INNGEST_`, `step.run`, `step.sleep`, `step.waitForEvent`,
46 `step.sendEvent`, `step.invoke`, `step.ai`, `inngest.send`, and
47 `@inngest/realtime`.
48 - If Inngest exists, inspect version, client config, serve endpoint,
49 registered functions, event naming, env vars, and v3/v4 API shape before
50 changing anything.
51
523. **Find durability gaps.**
53 - Search for fire-and-forget work: `void someAsync()`, un-awaited promises,
54 `.then(` chains, `setTimeout`, `setInterval`, detached jobs after HTTP
55 response, and background work in route handlers.
56 - Search for cron and schedulers: `cron`, `node-cron`, `agenda`, `bull`,
57 `bullmq`, `bee-queue`, `qstash`, `sqs`, `temporal`, `trigger.dev`,
58 deployment cron config, and scheduled API routes.
59 - Search for webhooks and at-least-once producers: Stripe, Clerk, GitHub,
60 Slack, Shopify, HubSpot, Linear, Svix, and generic `webhook`.
61 - Search for long-running work: PDF generation, exports, video/image
62 processing, embeddings, bulk email, imports, ETL, sync jobs, polling loops,
63 retries, and external API calls.
64 - Search for AI agent shapes: tool loops, LLM calls, streaming tokens,
65 human approval, multi-step reasoning, vector search, eval loops, scoring,
66 experiment assignment, user-feedback signals, and provider calls that need
67 rate limits or retry-safe state.
68
694. **Classify each candidate.**
70 - **P0:** user-visible loss, duplicate charge/email/action, timeout, missed
71 webhook, or crash-prone workflow.
72 - **P1:** fragile but recoverable background work, manual retry burden,
73 noisy 429s, or poor observability.
74 - **P2:** cleanup, ergonomics, or future migration opportunity.
75 - For each candidate, record: file, current trigger, side effects,
76 idempotency key, failure mode, recommended Inngest primitive, migration
77 size, and confidence.
78
795. **Choose the smallest safe integration.**
80 - Prefer one vertical slice over a wide rewrite.
81 - Keep existing domain functions and data models where possible.
82 - Add an Inngest client and serve endpoint only once.
83 - Move side effects into `step.run` one boundary at a time.
84 - Make event IDs and database writes idempotent before adding retries.
85 - Add tests around existing behavior and the new event/function boundary.
86
87## Useful Discovery Commands
88
89Run commands that fit the repo. Prefer `rg`; keep output focused.
90
91```bash
92rg -n "inngest|createFunction|step\\.|serve\\(|/api/inngest|INNGEST_" .
93rg -n "setTimeout|setInterval|Promise\\.all|void [a-zA-Z0-9_]+\\(|\\.then\\(" .
94rg -n "cron|node-cron|schedule|bull|bullmq|bee-queue|agenda|qstash|sqs" .
95rg -n "webhook|stripe|svix|clerk|github|shopify|slack|hubspot|linear" .
96rg -n "retry|backoff|poll|status|timeout|429|rate limit|rate-limit" .
97rg -n "openai|anthropic|ai\\.|generateText|streamText|tool|agent|embedding" .
98```
99
100When the repo is large, narrow searches to app source directories and exclude
101generated/vendor folders.
102
103## Brownfield Decision Matrix
104
105| Existing shape | Inngest fit | Primary primitives |
106|---|---|---|
107| HTTP handler does slow side effects before responding | Emit event, return fast | `inngest.send`, event trigger, `step.run` |
108| Webhook must acknowledge quickly but process reliably | Verify signature, emit idempotent event | Event ID, `step.run`, retries |
109| Cron job loses progress midway | Cron-triggered durable function | Cron trigger, page-level `step.run`, flow control |
110| Polling loop waits for external async work | Durable wait or durable poll | `step.waitForEvent`, `step.sleep`, `step.run` |
111| Large fan-out exceeds request/serverless limits | Split orchestration and item work | `step.sendEvent`, per-item function, concurrency |
112| External API hits 429s | Move limits to function config | `throttle`, `rateLimit`, `concurrency` |
113| Human review can take days | Persist the wait in Inngest | `step.waitForEvent`, timeout, realtime |
114| AI agent/tool loop needs retry-safe progress | One step per tool/model boundary | `step.ai`, `step.run`, `step.sleep`, realtime |
115| AI workflow needs production evals | Attach outcome signals to durable runs | `meta.sessions`, `step.score`, `createScorer`, `defer`, `group.experiment` |
116| Existing queue only hides fragile work | Replace queue boundary gradually | Event trigger, idempotency, function-level retries |
117
118## Integration Plan Format
119
120Before editing, summarize findings in this compact shape:
121
122```text
123Inngest audit:
124- Existing Inngest: none / partial / healthy / risky
125- Framework: <framework and evidence>
126- Best first slice: <file + workflow>
127- Why: <loss/timeout/retry/idempotency failure>
128- Proposed primitives: <event, steps, flow control, waits, realtime>
129- Idempotency key: <source of truth>
130- Files likely touched: <short list>
131- Tests/checks: <commands or focused cases>
132```
133
134Then implement unless the user asked for audit-only.
135
136## Existing Inngest Checklist
137
138If Inngest is already present, verify:
139
140- A single shared client is exported from a stable module.
141- The app `id` is a stable slug and is not derived from deploy-specific data.
142- v4 local development uses `INNGEST_DEV=1`; production uses
143 `INNGEST_SIGNING_KEY`.
144- Serve endpoint path is discoverable, usually `/api/inngest`.
145- The serve handler registers all functions that should sync.
146- Side effects and non-deterministic work are inside steps.
147- Step IDs are stable and descriptive.
148- Event names follow `domain/noun.verb`.
149- Events that may be replayed use deterministic IDs.
150- Webhook handlers verify signatures before emitting events.
151- Flow control is configured where external APIs have limits.
152- Realtime uses v4 native `inngest/realtime`, not the v3
153 `@inngest/realtime` package.
154
155## Durable Agent Patterns
156
157Use Inngest when an AI or agent workflow needs durable progress across model
158calls, tool calls, waits, approvals, or streaming UI updates.
159
160Good candidates:
161
162- Multi-step agent that calls tools or external APIs.
163- LLM workflow that may exceed one HTTP request lifetime.
164- Human-in-the-loop review, approval, correction, or escalation.
165- Agent that must pause for an external event or scheduled follow-up.
166- Bulk AI work that needs provider-level rate limits and cost protection.
167- User-visible agent progress that should stream from durable execution.
168
169Recommended shape:
170
1711. HTTP/UI request stores the user intent and emits an event with a stable
172 `id`.
1732. Inngest function loads state inside `step.run`.
1743. Each model call, tool call, vector search, and external side effect lives in
175 its own `step.ai` or `step.run` boundary.
1764. Human pauses use `step.waitForEvent` or `step.waitForSignal` with a timeout.
1775. Progress updates use `step.realtime.publish` between steps, or
178 `inngest.realtime.publish` inside an existing `step.run`.
1796. Provider rate limits use `concurrency`, `throttle`, or `rateLimit`, not
180 ad hoc in-process throttlers.
181
182Avoid:
183
184- Keeping agent state only in memory.
185- Retrying whole agent loops after a single tool failure.
186- Charging for repeated successful model calls because the result was not
187 memoized.
188- Using `setTimeout` or a cron poller for follow-ups and approvals.
189- Streaming progress from a process-local WebSocket server when the workflow
190 itself is durable elsewhere.
191
192## Implementation Guardrails
193
194- Do not replace working queues, crons, or webhooks blindly. First preserve
195 behavior with a thin Inngest slice.
196- Do not create duplicate clients or serve endpoints if the repo already has
197 them.
198- Do not put database writes, API calls, random IDs, timestamps, or LLM calls
199 outside steps in the new function.
200- Do not hide missing idempotency behind retries. Retries require idempotent
201 side effects.
202- Do not hardcode secrets or dev-mode flags in source.
203- Do not leave the app unable to sync: register new functions with the serve
204 endpoint and run available type/tests.
205
206## Verification
207
208Pick checks that prove the integration path:
209
210- Typecheck/build/lint the touched app.
211- Run existing tests around the migrated handler or workflow.
212- Add focused tests for "handler emits event and returns fast" and "function
213 calls the same domain operations in step boundaries" where the repo supports
214 it.
215- If local runtime is available, start the app and Inngest dev server, confirm
216 the function syncs, then send a sample event.
217- If only static checks are available, explicitly state that runtime sync was
218 not verified.