Inngest Durable Functions
Asymmetric-al/core
A skill your agent uses when building functions that must survive process crashes, retry automatically on failure, run on a schedule, react to events, or maintain state across infrastructure…
Guidance for building and debugging durable background jobs in Elixir with Oban and Oban Pro, covering serialization, retries, uniqueness, chaining, chunking and workflows.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools oban --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .claude/skills/oban && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .claude/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/obanType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools oban --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .agents/skills/oban && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .agents/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools oban --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .cursor/skills/oban && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .cursor/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/georgeguimaraes/elixir-agent-tools.git --path plugins/elixir-dev/skills/oban--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools oban --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .gemini/skills/oban && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .gemini/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install georgeguimaraes/elixir-agent-tools obanInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .github/skills/oban && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .github/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install georgeguimaraes/elixir-agent-tools oban --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/georgeguimaraes/elixir-agent-tools.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/elixir-dev/skills/oban .opencode/skills/oban && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "oban" agent skill from https://github.com/georgeguimaraes/elixir-agent-tools/tree/main/plugins/elixir-dev/skills/oban into .opencode/skills/oban/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "oban", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
obanGuidance for building and debugging durable background jobs in Elixir with Oban and Oban Pro, covering serialization, retries, uniqueness, chaining, chunking and workflows.
Part one covers plain Oban and opens with its main rule: job arguments are stored as JSON, so atom keys come back as strings, which the skill says causes most debugging trouble. For errors it recommends letting a job crash and bubble up to Oban, which then logs the stacktrace, retries with exponential backoff, shows the failure in the Oban Web dashboard and records the state in the database. Catching is reserved for custom retry logic or marking a job permanently failed, and a snooze return is the way to poll external state.
Other sections cover chaining simple sequences by having each job enqueue the next, without reaching for Oban Pro Workflows, and the `unique` option, which is checked at insert time, so identical jobs inserted outside the period, such as 61 seconds apart with a 60-second window, both run. For millions of records it advises chunking work into batches rather than one job per item to spare the database. The description also names Oban Pro batches and workflows, and sends in-memory tasks and process supervision to a separate `otp` skill. The excerpt ends in the chunking section.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit ac3a5a1. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are elixir).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Oban Background Jobs loads about 2.2k tokens when it runs. Until then it costs about 60 tokens; SKILL.md has 528 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from georgeguimaraes/elixir-agent-tools at commit ac3a5a1, republished under its Apache-2.0 licence (© georgeguimaraes). 528 words, ~2,172 tokens.
.claude/skills/oban/SKILL.md (or your agent's skills folder).Handle durable jobs, serialization, failures, and workflow composition with Oban.
JOB ARGS ARE JSON. ATOMS BECOME STRINGS.This single fact causes most Oban debugging headaches.
# Creating - atom keys are fine
MyWorker.new(%{user_id: 123})
# Processing - must use string keys (JSON converted atoms to strings)
def perform(%Oban.Job{args: %{"user_id" => user_id}}) do
# ...
endDon't catch errors in Oban jobs. Let them bubble up to Oban for proper handling.
# Bad: Swallowing errors
def perform(%Oban.Job{} = job) do
case do_work(job.args) do
{:ok, result} -> {:ok, result}
{:error, reason} ->
Logger.error("Failed: #{reason}")
{:ok, :failed} # Silently marks as complete!
end
end# Good: Let errors propagate
def perform(%Oban.Job{} = job) do
result = do_work!(job.args) # Raises on failure
{:ok, result}
end
# Or return error tuple - Oban treats as failure
def perform(%Oban.Job{} = job) do
case do_work(job.args) do
{:ok, result} -> {:ok, result}
{:error, reason} -> {:error, reason} # Oban will retry
end
endOnly catch errors when you need custom retry logic or want to mark a job as permanently failed:
def perform(%Oban.Job{} = job) do
case external_api_call(job.args) do
{:ok, result} -> {:ok, result}
{:error, :not_found} -> {:cancel, :resource_not_found} # Don't retry
{:error, :rate_limited} -> {:snooze, 60} # Retry in 60 seconds
{:error, _} -> {:error, :will_retry} # Normal retry
end
endUse {:snooze, seconds} for polling external state instead of manual retry logic:
def perform(%Oban.Job{} = job) do
if external_thing_finished?(job.args) do
{:ok, :done}
else
{:snooze, 5} # Check again in 5 seconds
end
endFor simple sequential chains (JobA → JobB → JobC), have each job enqueue the next:
def perform(%Oban.Job{} = job) do
result = do_work(job.args)
# Enqueue next job on success
NextWorker.new(%{data: result}) |> Oban.insert()
{:ok, result}
endDon't reach for Oban Pro Workflows for linear chains.
Prevent duplicate jobs with the unique option:
use Oban.Worker,
queue: :default,
unique: [period: 60] # Only one job with same args per 60 seconds
# Or scope uniqueness to specific fields
unique: [period: 300, keys: [:user_id]]Gotcha: Uniqueness is checked on insert, not execution. Two identical jobs inserted 61 seconds apart will both run.
For millions of records, chunk work into batches rather than one job per item:
# Bad: One job per contact (millions of jobs = database strain)
Enum.each(contacts, &ContactWorker.new(%{id: &1.id}) |> Oban.insert())
# Good: Chunk into batches
contacts
|> Enum.chunk_every(100)
|> Enum.each(&BatchWorker.new(%{contact_ids: Enum.map(&1, fn c -> c.id end)}) |> Oban.insert())Use bulk inserts without uniqueness constraints for maximum throughput.
Unlike regular job args, cascade context preserves atoms:
# Creating - atom keys
Workflow.put_context(%{score_run_id: id})
# Processing - atom keys still work!
def my_cascade(%{score_run_id: id}) do
# ...
end
# Dot notation works too
def later_step(context) do
context.score_run_id
context.previous_result
end| Creating | Processing | |
|---|---|---|
| Regular jobs | atoms ok | strings only |
| Cascade context | atoms ok | atoms ok |
Reserve Workflows for:
Don't use Workflows for simple A → B → C chains.
When you need a parent workflow to wait for a sub-workflow to complete before continuing, use add_graft instead of add_workflow.
| Method | Sub-workflow completes before deps run? | Output accessible? |
|---|---|---|
add_workflow | No - just inserts jobs | No |
add_graft | Yes - waits for all jobs | Yes, via recorded values |
Don't couple unrelated concerns (e.g., notifications) to domain-specific workflows (e.g., scoring). Instead, create a higher-level orchestrator:
# Bad: Notification logic buried in AggregateScores
defmodule AggregateScores do
def workflow(score_run_id) do
Workflow.new()
|> Workflow.add(:aggregate, AggregateJob.new(...))
|> Workflow.add(:send_notification, SendEmail.new(...), deps: :aggregate) # Wrong place!
end
end
# Good: Higher-level workflow composes scoring + notification
defmodule FullRunWithNotifications do
def workflow(site_url, opts) do
notification_opts = build_notification_opts(opts)
Workflow.new()
|> Workflow.put_context(%{notification_opts: notification_opts})
|> Workflow.add_graft(:scoring, &graft_full_run/1)
|> Workflow.add_cascade(:send_notification, &send_notification/1, deps: :scoring)
end
defp graft_full_run(context) do
# Sub-workflow doesn't know about notifications
FullRun.workflow(context.site_url, context.opts)
|> Workflow.apply_graft()
|> Oban.insert_all()
end
endFor a grafted workflow's output to be available to dependent steps, the final job must use recorded: true:
defmodule FinalJob do
use Oban.Pro.Worker, queue: :default, recorded: true
def perform(%Oban.Job{} = job) do
# Return value becomes available in context
{:ok, %{score_run_id: score_run_id, composite_score: score}}
end
endAdd jobs to a running workflow with Workflow.append/2:
def perform(%Oban.Job{} = job) do
if needs_extra_step?(job.args) do
job
|> Workflow.append()
|> Workflow.add(:extra, ExtraWorker.new(%{}), deps: [:current_step])
|> Oban.insert_all()
end
{:ok, :done}
endCaveat: Cannot override context or add dependencies to already-running jobs. For complex dynamic scenarios, check external state in the job itself.
To run a final job after multiple paginated workflows complete, use Batch callbacks:
# Wrap workflows in a shared batch
batch_id = "import-#{import_id}"
pages
|> Enum.each(fn page ->
PageWorkflow.workflow(page)
|> Batch.from_workflow(batch_id: batch_id)
|> Oban.insert_all()
end)
# Add completion callback
Batch.new(batch_id: batch_id)
|> Batch.add_callback(:completed, CompletionWorker)
|> Oban.insert()Tip: Include pagination workers in the batch to prevent premature completion.
Don't use inline testing mode - workflows need database interaction.
# Use run_workflow/1 for integration tests
assert %{completed: 3} =
Workflow.new()
|> Workflow.add(:a, WorkerA.new(%{}))
|> Workflow.add(:b, WorkerB.new(%{}), deps: [:a])
|> Workflow.add(:c, WorkerC.new(%{}), deps: [:b])
|> run_workflow()For testing recorded values between workers, insert predecessor jobs with pre-filled metadata.
Non-Pro:
perform/1{:ok, _}Pro:
add_workflow when you need to wait for completionrecorded: true when you need output from grafted workflowsAny of these? Re-read the serialization rules.
© georgeguimaraes, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/elixir-dev/skills/oban of georgeguimaraes/elixir-agent-tools.
Open the folder on GitHubat commit ac3a5a1
Oban Background Jobs next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Oban Background Jobs this skillgeorgeguimaraes/elixir-agent-tools | 184 | — | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Inngest Durable FunctionsAsymmetric-al/core | 381 | — | ~4k | Automated safety check: Pass | AGPL-3.0 | |
| Cloudflare Queuessecondsky/claude-skills | 227 | — | ~4k | Automated safety check: Pass | MIT | |
| Robust Error Handling In Scriptsaiming-lab/MetaClaw | 3.5k | — | ~225 | Automated safety check: Pass | MIT | |
| Elixir AntipatternsGentleman-Programming/Gentleman-Skills | 658 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Node Backend Development Guidelinesdiet103/claude-code-infrastructure-showcase | 10k | 2 repos | ~2k | Automated safety check: Pass | MIT |
Asymmetric-al/core
A skill your agent uses when building functions that must survive process crashes, retry automatically on failure, run on a schedule, react to events, or maintain state across infrastructure…
secondsky/claude-skills
This skill should be used when the user asks to "set up Cloudflare Queues", "create a message queue", "implement queue consumer", "process background jobs", "configure queue retry logic", "publish…
aiming-lab/MetaClaw
A skill your agent uses when writing shell scripts, Python automation, or any unattended batch job.
Gentleman-Programming/Gentleman-Skills
Core catalog of 8 critical Elixir/Phoenix anti-patterns covering error handling, separation of concerns, Ecto queries, and testing.
diet103/claude-code-infrastructure-showcase
Sets layered architecture and coding rules for Node.js, Express and TypeScript microservices, covering routes, controllers, services, repositories, Prisma, Sentry and Zod.
diet103/claude-code-infrastructure-showcase
Enforces that every error in a service is captured to Sentry v8, with patterns for controllers, routes, cron jobs and database performance spans instead of console logging alone.
georgeguimaraes/elixir-agent-tools
Designs and debugs Elixir persistence with Ecto: schemas, changesets, queries, preloads, migrations and multi-tenancy, kept within application contexts.
georgeguimaraes/elixir-agent-tools
Writes and refactors idiomatic Elixir modules and functions, with rules for pattern matching, error handling, protocols and when a process is really needed.
georgeguimaraes/elixir-agent-tools
Guides choices among GenServer, Supervisor, Task, Registry, ETS and Broadway when designing or debugging Elixir concurrency, state and fault recovery.
georgeguimaraes/elixir-agent-tools
Guidance for building and debugging Phoenix web interfaces: where LiveView loads data, scopes, PubSub topics, external polling and component state.
Works with
Categories
Guidance for building and debugging durable background jobs in Elixir with Oban and Oban Pro, covering serialization, retries, uniqueness, chaining, chunking and workflows. Part one covers plain Oban and opens with its main rule: job arguments are stored as JSON, so atom keys come back as strings, which the skill says causes most debugging trouble. For errors it recommends letting a job crash and bubble up to Oban, which then logs the stacktrace, retries with exponential backoff, shows the failure in the Oban Web dashboard and records the state in the database.
Oban Background Jobs fits situations like: writing an Oban worker with retries and sensible error handling; debugging job arguments that arrive as strings instead of atoms; preventing duplicate jobs with the unique option; processing a very large set of records without flooding the jobs table.
Run `npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a claude-code`. Or copy the skill folder (plugins/elixir-dev/skills/oban in georgeguimaraes/elixir-agent-tools) into .claude/skills/oban in your project. Claude Code loads it when a task matches its description.
Run `npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a codex`. Or copy the skill folder (plugins/elixir-dev/skills/oban in georgeguimaraes/elixir-agent-tools) into .agents/skills/oban in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add georgeguimaraes/elixir-agent-tools --skill oban -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/oban, .gemini/skills/oban, .github/skills/oban and .opencode/skills/oban in your project.
SKILL.md names no scripts, command-line tools or credentials: Oban Background Jobs is instructions for the agent only. Our summary lists: An Elixir project that uses Oban.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Oban Background Jobs is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.2k tokens (SKILL.md is roughly 8.7k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Oban Background Jobs: Inngest Durable Functions (Asymmetric-al/core, 381 stars), Cloudflare Queues (secondsky/claude-skills, 227 stars), Robust Error Handling In Scripts (aiming-lab/MetaClaw, 3.5k stars) and Elixir Antipatterns (Gentleman-Programming/Gentleman-Skills, 658 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
georgeguimaraes (a GitHub user) maintains it in georgeguimaraes/elixir-agent-tools, which has 184 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on September 20, 2026.
Source: georgeguimaraes/elixir-agent-tools on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.