Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 5 Nov 2026. What's built · Install 0.3.0
jadpo
ReferenceBatches

Background workBatches

What a run reaches under policy

On this page

A batch's actions run as the system, but what they do still answers to the policy of the people who started the run or read its status. This topic says what Jadpo checks so that a batch never leaks a record to someone who may not read it.

What the summary carries

An ImportSummary holds counts and problems, never a record's fields. items shows nothing of the items the run walked, as a count of a query's records doesn't. items_with_problems and problems count the items whose action failed, and say which and why. So they carry what decides whether each:'s action fails: what decided each reject and attempt it runs, and whether an action it calls fails, read from the item and from what the run saved. What decided that the run starts goes with all three.

The item is checked like any call's argument

Each item is one of the records from: finds, and it is the same record all through each:'s action. So what that action stores from it, given the item or a field of it, is checked where each: calls it, as a call's arguments are.

A saved value has left its record

What a run saves has left its record, as a value an event carries has (Subscribers): the record may change while the run goes on. So it is checked as a value that could have come from any record of its entity. The one exception is a record a saved tenant id was read from, which is known to be of the tenant the run is limited to. A value the starting action was passed is checked where that action is called, in the same way.

The run's tenant proves what it reaches

Every record a run reads is of the tenant it is limited to. So the item, and what decided its action's failures, may be stored wherever that tenant's roles may read them, as a record of an event's tenant may. A record every tenant shares (Tenancy.shared) isn't the tenant's.

Every action a run reaches is checked

What a schedule, a subscriber or a batch reaches is checked against the line under actions: of every action it reaches, not only the one it runs (Permissions on actions). A listed action's line decides its own body's writes wherever it is called from. Routes are checked the same way.

What the run's actions show

Nothing reaches whoever started the run. A failure's fields, and a record found by a value, are shown to nobody, and a problem keeps only the failure's code.

What batches.status shows

Where batches.status is read, nothing says which batch a BatchRun is, since a run is found by its id alone. So what it gives is held to what decides the failures of every batch the app starts: whether each one's each: action fails, and whether its finished: action fails, which ends the run failed. Each is checked as though it came from any record of its entity, never one of a run's tenant, and with nothing of what the starting action was passed.

The reader is checked only by what the reading action's line under actions: lets in as it starts. A status read inside a batch's own actions goes round in a circle, and is worked out as calls that go round are.

So in an app with a tenant, a run's status shows only what anyone at all, or a role held across the whole app, may read. Letting a reader in through the record that keeps the run would need the BatchRun to say which batch and tenant it is.

No request carries a batch type

A request can't name a run. An Input, read from a body or, for a GET or a DELETE, from the query string, and a route's placeholder may not hold a BatchRun, a BatchProgress, a RowProblem or an ImportSummary. That holds whether the type is there directly, in a list, a map or an option, in an object, through a type built on one, or through an entity's field holding one (Sweep.run); each is type.batch_input. A BatchState is a plain enum a request may name.

A named query's parameter isn't refused, because what is passed to it comes from a route's placeholder or input, which are checked, or from an action. A batch type's fields can be read (Other built-in types), but one named as a type, such as BatchRun.id, is type.unknown_field, as is a field a standard object doesn't have. A run is reached from batches.each, or from a field the caller may read.

Here each:'s action stores part of the item where more people can read it: post_item puts a diary's confession, which only its owner may read, on a board anyone signed in may read. Jadpo follows the item into the action and refuses the call:

example
action post_item(diary: Diary) -> Unit {    var board_post: Board = Board.create {        set: {            text: diary.confession        }    }    assert board_post.text == board_post.text}action post_all(page: DiaryPage) -> BatchRun {    return batches.each(from: diaries(page), each: diary -> post_item(diary))}
jadpo checkcheck failed: 1 error, 2 files

error post_all passes Diary.confession to post_item, which stores it in Board.text, though not everyone who may read text may read it

--> main.jadpo:11:71

   |11 |     return batches.each(from: diaries(page), each: diary -> post_item(diary))   |                                                                       ^^^^^ from `Diary.confession`
reason
post_item stores what it is given here in Board.text, and everyone who may read Board.text, anyone signed in among them, reads what is stored there, from any request, and only the user owner_id points at and schedules and subscribers may read confession. A value stored in a field keeps its own field's rules, as one sent in an output does, so it is stored only where everyone who may read it may read the value.
next
Pass post_item what everyone who may read Board.text may read, or give text's read: only those who may read confession (agent)
rule
policy.output_field_unproved
help
https://jadpo.dev/docs/errors/policy.output_field_unproved

Here a run's status leaks what decided its failures. check_diary fails on a diary whose confession says "draft", so the number of problems tells the reader something about confessions. count_drafts shows that number to anyone signed in, which its line under actions: lets in:

example
type Draft = Failure {    kind: FailureKind.rejected    message: "This diary is a draft."}type DraftCount = Output {    drafts: Int}action check_diary(diary: Diary) fails Draft -> Unit {    if (diary.confession == "draft") {        reject Draft {}    }}action check_all(page: DiaryPage) -> BatchRun {    return batches.each(from: diaries(page), each: diary -> check_diary(diary))}action count_drafts(draft_walk: BatchRun) -> DraftCount {    var at: BatchProgress = batches.status(draft_walk)    return DraftCount {        drafts: at.problems    }}
jadpo checkcheck failed: 1 error, 2 files

error DraftCount.drafts carries Diary.confession, which not every caller of count_drafts may read

--> main.jadpo:23:17

   |23 |         drafts: at.problems   |                 ^^^^^^^^^^^ decided by `Diary.confession`
reason
batches.status finds a run by its id alone, so what it gives shows what decides the failures of every batch the application starts, Diary.confession among them, to anyone who may call count_drafts, and only the user owner_id points at and schedules and subscribers may read confession.
next
Let only those who may read confession call count_drafts, through its line under actions:, or decide which items fail by what its callers may read (agent)
rule
policy.output_field_unproved
help
https://jadpo.dev/docs/errors/policy.output_field_unproved

A route can't take a run in its path:

example
routes Sweeps {    prefix: /sweeps    DELETE /{shelf_run: BatchRun} {        authentication: RouteAuthentication.none        status: SuccessStatus.no_content        run: stop_sweep(shelf_run)    }}
jadpo checkcheck failed: 1 error, 2 files

error Placeholder shelf_run holds a BatchRun, which no request carries

--> main.jadpo:4:25

  |4 |     DELETE /{shelf_run: BatchRun} {  |                         ^^^^^^^^ a `BatchRun`
reason
Only the runtime builds a BatchRun: a run comes from batches.each, or from a field the caller may read where the application kept it, and batches.status and batches.cancel find it by its id alone. A request that named one could read or stop any run.
next
Take the id of the record that keeps the run, and read the BatchRun from its field (agent)
rule
type.batch_input
help
https://jadpo.dev/docs/errors/type.batch_input

Markdown for agents: /docs/what-a-run-reaches-under-policy.md.Built from Jadpo main at aee2e8f.