# How BackWave Works

## The life of a job

Every job follows the same path: enqueue → durable row → claimed under a Lease →
runs once with retries → terminal.

- **Enqueue.** You enqueue a typed job with a due time. "Enqueue now" is just a
  due time of now, so there is no separate immediate path. Every job is a
  Scheduled Job, and a due time is the only shape of work the Core understands.
- **Durable in your database.** The job becomes a row in the database you already
  run, and that row is the single source of truth. When BackWave's tables share
  that database, you can enqueue inside your own transaction and the job commits
  or rolls back atomically with your business write, with no outbox and no
  dual-write race.
- **Claimed by a Worker.** Workers poll the database and claim due jobs, taking a
  Lease on each. A Lease is a time-bounded, heartbeat-renewed claim, not a lock,
  and claiming one starts an Attempt.
- **Runs once, with retries.** The handler runs for one Attempt. Return is
  success; throw is failure. A failure with retries left reschedules; a failure
  that exhausts the retry budget is Dead-Lettered.
- **Terminal.** The job settles in a terminal state and stops moving on its own.
  Only an explicit Operator Action, such as a requeue, moves it again.

Delivery is **at-least-once**. A Lease can expire on a crash or a network cut, so
a job can run again, which means handlers must be idempotent. The full state
machine and retry loop live in
[Job Lifecycle](/docs/core-concepts/job-lifecycle), and how at-least-once pairs
with the Effect-Once fence to keep repeated runs safe lives in
[Execution Guarantee](/docs/core-concepts/execution-guarantee).

## The Core decides, the Shell acts

BackWave splits its runtime into two halves. The **Core** is pure decision logic:
given a snapshot of state, an event, and the current time, it returns the
decisions that follow, such as when a job is due, whether a failure retries or
dead-letters, and when a Lease has expired. It does no I/O and never reads the
wall clock. The **Shell** is the thin imperative loop around it. It polls the
database, hands the current state to the Core, and carries out the resulting
instructions, owning all the concurrency, the sockets, and the real clock. The
database row is the single source of truth, and every node is an interchangeable
stateless peer, so you scale by adding processes.

That split is what makes your job logic testable. Because the Core does no I/O
and never reads the wall clock, the same inputs always produce the same
decisions. You can drive scheduling and retry logic from a test on a clock you
control, and years of schedule activity run in milliseconds. That same
determinism is why BackWave can be simulation-tested for correctness against
seeded fault injection. See [Execution Model](/docs/core-concepts/execution-model)
for the split and stateless-peer scaling, and
[Virtual-Time Testing](/docs/testing/behavior-over-time) to drive the runtime
deterministically yourself.

## Where to go next

- [Job Lifecycle](/docs/core-concepts/job-lifecycle): the seven states, the retry
  loop, leases, and terminal transitions.
- [Execution Model](/docs/core-concepts/execution-model): the Core/Shell split,
  the database as single source of truth, and stateless-peer scaling.
- [Execution Guarantee](/docs/core-concepts/execution-guarantee): at-least-once
  execution, the Effect-Once fence, and how to write idempotent handlers.
- [Quickstart](/docs/introduction/quickstart): wire BackWave up and run a real
  job.
