Skip to content
Back to skills

Elixir Recurrent Jobs

ASecurity

Build or review a recurrent background job / scheduled sweep in portal/ the emisar way - context-owned jobs/, supervised by the owning context, idempotent durable-row work, testable. Use when adding/changing a recurrent job or debugging job ticks.

  • 391 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
devopsbashtestingdebugging

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add AndrewDryga/emisar --skill elixir-recurrent-jobs --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Elixir Recurrent Jobs?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Elixir Recurrent Jobs
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/andrewdryga-elixir-recurrent-jobs/badge)](https://www.skillsdirectory.com/skills/andrewdryga-elixir-recurrent-jobs)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: elixir-recurrent-jobs
description: Build or review a recurrent background job / scheduled sweep in portal/ the emisar way - context-owned jobs/, supervised by the owning context, idempotent durable-row work, testable. Use when adding/changing a recurrent job or debugging job ticks.
effort: medium
argument-hint: "[job or context]"
allowed-tools: Read, Grep, Glob, Bash, Write, Edit
---

# Recurrent Jobs

Runtime jobs are first-party supervised processes, not Oban workers.

## Shape

Domain jobs live under the owning context's `jobs/`, and the context module
supervises them:

```elixir
defmodule Emisar.Runs.Jobs.DispatchTimeout do
  use Emisar.Jobs.Job,
    otp_app: :emisar,
    every: :timer.minutes(1)

  @impl Emisar.Jobs.Executors.GloballyUnique
  def execute(config) do
    # Poll durable rows and make idempotent transitions.
    :ok
  end
end
```

The owning context starts its jobs:

```elixir
defmodule Emisar.Runs do
  use Supervisor

  def start_link(opts), do: Supervisor.start_link(__MODULE__, opts, name: __MODULE__.Supervisor)

  @impl Supervisor
  def init(_opts) do
    Supervisor.init([Jobs.DispatchTimeout], strategy: :one_for_one)
  end
end
```

`Emisar.Jobs` is infrastructure only: declaration macro + executors. Do not put
domain sweep engines in top-level `Emisar.Jobs` and do not recreate
`lib/emisar/workers/`.

## Iron Law IL-13

1. **Idempotent.** A tick may run more than once after restarts, failover, or a
   crash. Check current row state before acting.
2. **Durable rows, not scheduler memory.** The scheduler has no persistent queue;
   each tick polls rows with durable state and bounded queries.
3. **No overlapping-work assumptions.** `GloballyUnique` elects one leader per
   job across the cluster, but the job logic must still be safe if a previous
   tick's side effects partially completed.

## Wiring

- Keep the job under the owning context's `jobs/`.
- Add that context to `Emisar.Application` if it is not already a supervised
  child.
- Disable job modules explicitly in `config/test.exs`; tests call `execute/1`
  directly so all DB work stays inside the test sandbox checkout.
- If a job needs another context's table shape, add a narrow internal function on
  the owning context instead of building a sibling context's Query pipeline.

## Testing

- Run the job directly: `JobModule.execute([])` or with keyword config.
- Cover the idempotency path: run `execute/1` twice, assert the second is a no-op.
- For a sweep, cover happy, nothing-to-do, cross-account scoping, and bounded
  pagination/batch behavior.

## Finish

Run `./run gate portal --changed` from the repository root. Run the complete
`./run gate portal` before pushing.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.