# Jobs

A job is work that is too big to sit inside a conversation: analyze ten thousand documents, move forty thousand files, write to a hundred records. You hand over a plan, it runs for as long as it takes, and you read receipts.

The alternative, calling one action in a loop, breaks in ways that are hard to recover from: a crash halfway leaves you unsure what already happened, and the rows themselves pile up in the model's context. A job avoids both. Rows never come back to the thing that asked for the work; steps hand collections to each other by reference, and what comes back is a count and a receipt.

## What a plan is made of

Five kinds of step, each doing one thing, each naming the earlier steps it reads from.

| Step | What it does |
|---|---|
| Collect | Pages through a listing tool in a connected app and stores what it found. |
| Map | Runs one model call per row, with a schema the answer has to fit. |
| Reduce | Runs a deterministic program over a whole collection: sorting, ranking, joining, top of the list. |
| Act | Applies one tool per row, with a key derived from the row so a crash cannot send twice. |
| Files | Pulls the files a provider only handed you references to, into storage you control. |

A step can only read from steps declared before it, so a plan is always a plan and never a loop.

A step can also declare what it expects: how few rows would be surprising, how many would be, what it should cost at most. When reality falls outside that, the job asks rather than carrying on, which is how a plan against the wrong filter becomes a question instead of a mess.

A step can also declare a judgment: a point where it stops and asks, either a model or a person, before continuing. See [Approvals and questions](./approvals.md).

## Run it as a dry run first

Two modes let you see what a plan would do before it does it.

**Simulate** runs the reading steps for real and projects every acting step against what is already known, so the answer is a projection over real data: how many of these hundred invitations would land on something that already exists, and how many would be new.

**Shadow** answers everything, reads included, from recorded state, so nothing outside is touched at all. A call that nothing recorded is reported as unrecorded rather than guessed at.

Receipts from both are marked as simulated and never fold into a real job's numbers.

You can also ask for both at once: a simulation that runs immediately, and the real plan parked for approval with the simulation named on it. That is the shape to use when a person is going to sign off on a large run.

## Watching one

**Jobs** in the console lists them, filterable by state. Open one and you get the plan, the event log, the receipt for each step, where each collection came from, the reason it parked if it did, and its simulation beside its run if it has one.

The folded receipt at the top is the summary: rows in, rows out, how many were dropped and why, whether the count is complete, and what it cost.

That completeness flag is worth reading. When it is false, a step declared that it could not see everything it was asked to, so the drop reasons are the truth and the total is not. A receipt never claims more than the steps proved.

## Reading one from code

`submit_job` returns the job with the status it reached: running, or parked for approval with a cost estimate and the projected effects for a person to release once for the whole plan.

`get_job` reads it back: the status, the folded receipt, each step's receipt, and what it is waiting on.

Do not poll in a tight loop. Subscribe to the platform events instead, `job_finished`, `job_parked`, and `job_judgment_requested`, and read the job when you are woken. See [Standing work](./standing-work.md) for subscriptions.

Report what the receipt says rather than what the plan hoped for. The [JobsService reference](../reference/api/jobs.md) has every call.

## Cost

A job spends money in two places: the model calls inside map steps, and whatever the apps you are acting on charge. Both are metered per job and shown on the job.

**Model** in the console is where the model a map step runs on is configured, along with its prices, so the estimate on a plan is calculated against your own numbers rather than a guess. Ceilings on what a job may spend are written in the policy document; see [Policies](./policies.md).
