Sweeps
A sweep runs a static scope over every record its over matches, a page at a time, in the
background. It is evaluate for a whole entity: dry by default,
so you can see what the rules would do to every record before you let them, and applied when you
ask.
Sweeps are off until an operator turns them on (rules.sweeps.enabled), and need a system user
(Configuration).
Routes
Section titled “Routes”| Route | Does | Needs |
|---|---|---|
POST /rules/sweeps | starts a sweep: 202 with the sweep | evaluate; apply too with apply: true |
GET /rules/sweeps | the sweeps you started; every sweep of the tenant with manage | read |
GET /rules/sweeps/{id} | one sweep: its state and counts | read for your own, manage for anyone’s |
GET /rules/sweeps/{id}/items?kind=…&limit=… | what it recorded | as above |
POST /rules/sweeps/{id}/cancel | stops it between pages | as above |
Starting one
Section titled “Starting one”{ "scope": "invoice-routing", "where": "total > 100", "apply": false }| Key | Meaning |
|---|---|
scope | a static scope with data.over |
where | narrows the scope’s own over.where (the two are combined with and), in the where language |
apply | write what the rules propose |
{ "id": "swp_e6220dcab707874e61914c1e", "scope": "invoice-routing", "stamp": "sha256:84853064e9fe118a5c7c4882e0dc5fa321305e2b941d6304cbf1642b3db8efb8", "apply": false, "where": "total > 100", "status": "queued", "startedby": "u-shop-owner", "startedbytype": "user", "counts": { "seen": 0, "matched": 0, "changed": 0, "partial": 0, "conflicts": 0, "errors": 0 }, "createdon": "2026-10-17T11:15:26.269112Z"}Following one
Section titled “Following one”{ "id": "swp_e6220dcab707874e61914c1e", "scope": "invoice-routing", "stamp": "sha256:84853064e9fe118a5c7c4882e0dc5fa321305e2b941d6304cbf1642b3db8efb8", "apply": false, "where": "total > 100", "status": "done", "startedby": "u-shop-owner", "startedbytype": "user", "principal": "u-rules-system", "counts": { "seen": 2, "matched": 2, "changed": 0, "partial": 0, "conflicts": 0, "errors": 0 }, "createdon": "2026-10-17T11:15:26.269112Z", "finishedon": "2026-10-17T11:15:26.285998Z"}| Field | What it is |
|---|---|
status | queued, running, done, failed or cancelled |
stamp | the rule text the sweep runs, fixed when it started |
startedby, startedbytype | who started it |
principal | who it runs as: the system user |
counts.seen | records read |
counts.matched | records whose rules proposed something |
counts.changed | records written (with apply) |
counts.partial, counts.conflicts, counts.errors | records that ended so, as in evaluate |
error | why a failed sweep stopped |
finishedon | when it ended |
What it recorded
Section titled “What it recorded”GET /rules/sweeps/{id}/items lists what the sweep kept about each record, filtered by kind:
kind | Kept for |
|---|---|
action | every proposal in a dry sweep (the preview), every applied write in an applied one |
finding | every finding a rule raised |
error | every record that failed |
conflict | every record that conflicted twice |
partial | every record applied in part |
{ "items": [ { "id": "swi_1bd44e848c0884544ce5dab3", "kind": "action", "record": "inv-big", "rule": "Senior", "op": "set", "detail": { "applied": false, "field": "review_tier", "value": "senior" }, "createdon": "2026-10-17T11:15:26.278888Z" }, { "id": "swi_7b51332f35ecaca7c3ec8c71", "kind": "action", "record": "inv-big", "rule": "Senior", "source": "shop", "entity": "task", "op": "create", "detail": { "applied": false, "fields": { "id": "t-inv-big", "subject": "review inv-big for gold" } }, "createdon": "2026-10-17T11:15:26.278888Z" } ]}Finished sweeps and their items are kept for rules.sweeps.retention (30 days by default).
Who it runs as
Section titled “Who it runs as”Starting a sweep is the caller’s: it needs evaluate, and apply for an applied one. Once it
starts, the sweep acts as the system user, whose rights are checked again before every page:
revoke them and the sweep stops, failed, saying why. Every record it reads and every write it makes
goes through the source as the system user, so data.svc records the system user as the writer.
Resuming, and the rule text it runs
Section titled “Resuming, and the rule text it runs”- The rule text is pinned. A sweep runs the rule text it started with (its
stamp). An edit to the scope mid-sweep does not change it. A replica that takes over a sweep and holds other rule text for the scope fails it, asking you to start it again. - Each page is kept whole. After every page, the sweep keeps that page’s items, its cursor and its counts together. A replica that dies mid-page leaves the sweep at the last page it kept; when its lease lapses, a worker on any replica resumes it after that page.
- Re-applying is safe. A page applied twice writes nothing the second time: a field set to what it holds is dropped, and each update carries its version.
- A source that cannot be reached pauses it. The sweep waits and resumes from its last page; it does not fail.
Limits
Section titled “Limits”| Setting | Default | Effect |
|---|---|---|
rules.sweeps.pertenant | 1 | sweeps a tenant may have queued or running; one more answers 429 rules-sweep-limit |
rules.sweeps.pagesize | 500 | records per page |
rules.sweeps.maxerrors | 100 | a sweep stops, failed, after this many records failed |
rules.sweeps.retention | 720h | how long finished sweeps are kept |
A cancel is honoured between pages: the page in hand finishes and is kept.
What refuses a start
Section titled “What refuses a start”| Status | Code | Why |
|---|---|---|
409 | rules-sweeps-disabled | sweeps are off, or no system user is configured |
429 | rules-sweep-limit | the tenant already has pertenant sweeps queued or running |
400 | rules-facts-invalid | a scope without data.over, or a where that does not parse |
403 | rules-access-denied | the caller may not evaluate, or not apply |
404 | rules-scope-not-found | no static scope of that name |
An unknown sweep, or one you may not see, answers 404 rules-sweep-not-found.
Continue with
Section titled “Continue with”- Evaluating records: the same decision for named records.
- Events: deciding a record when it changes.
- Access.