Worlds
Stale reads and scope
Hold a merge whose decisions rest on data that changed, or that wrote outside what it said it would.
SoonThese pages describe Chronos Cloud, which opens from the waitlist. Running Chronos yourself? Self-host docs
Stale reads
A merge conflicts on rows both sides wrote. An agent that read a balance, decided on it, and wrote a payout conflicts with nothing if the balance changed meanwhile: the payout merges on a decision made on a number that’s gone. A world that checks its reads is held when rows it only read changed in its parent since it forked.
create world payout with (check_reads = true); -- only when forking: earlier reads aren't known later-- ... reads and writes in payout ...merge world payout;-- ERROR: merging payout read rows that changed since it forked: accounts/1; nothing was merged:-- redo the work in a new world forked from nowalter world payout set (check_reads = false); -- a person's way past it- Refused or queued:
40001for anyone; for an agent keeping to a merge policy, a broken rule, and the world waits in Reviews. A dry run shows it in itsblockedrow. - For every world of an agent: a merge policy with
check_reads = trueturns it on for every world its agents fork, and they can’t turn it off (42501); a person can. - Partial merges check every read of the world, not only those of the rows they merge.
- Anyone reading counts: a person running
SELECTin the world adds to what it read.
What counts as read
- Every row read through the world: a key looked up, a scan’s range up to where it stopped, and the index and constraint entries it looked in (so a row the parent added that a lookup would have found counts too).
- A scan with conditions like
column op constant(=,<,>=,IN,IS NULL,LIKE 'prefix%', joined byAND) reads only the rows those conditions match:where owner = 'ana'isn’t held by changes to other owners’ leads. - A scan with no such condition (
lower(name) = 'x', anORof columns) reads the whole table; so do full-text and vector searches. - Only the columns read: a
SELECTkeeps, per table, the columns it used.select sum(amount) from orders where status = 'open'isn’t held when an open order’snoteschanges, but is when itsamountorstatusdoes. A row added or deleted always counts. - Past 10,000 keys, 1,000 ranges or 1,000 distinct filtered scans of one table, reads of it count as the whole table: read many rows with one filtered scan rather than one at a time.
- Rows the world wrote itself aren’t checked here: they conflict or settle as always.
Checking between steps
CHECK READS [OF WORLD w] (MCP check_reads) lists what the world read that changed since it forked, and changes nothing. An agent on a long task runs it at the start of each step; if something changed, it runs REBASE WORLD, reads those rows again and decides again before writing, instead of finding out at its merge.
check reads of world payout;-- checked_against | changed-- -----------------+-------------------------------------------------- main | read rows that changed since it forked: accounts/1Declared scope
An agent allowed to write a table can still overwrite the wrong rows of it. A world can say, when it is forked, what it is meant to change; its merge is then held to that.
create world close_stale with ( may_change = 'leads.status, lists', -- whole tables, or single columns tenant = 'org_id = 42', -- every row changed has org_id = 42, before and after max_rows = 250, -- rows this run changes, across every table max_deletes = 0, -- and how many of them it may delete intent = 'close leads idle 90 days', run = 'planner/3' -- your own id for the run);merge world close_stale;-- ERROR: merging close_stale changes leads.owner: leads/7; it changes leads rows outside-- tenant org_id = 42: leads/9, outside the scope it was forked with ...Over MCP, put the same keys in fork’s meta: {"may_change": "leads.status, lists", "tenant": "org_id = 42", "max_rows": 250}.
How scope is judged
| Key | Means |
|---|---|
may_change | a table named whole may be inserted into, deleted from, updated and altered; a column (leads.status) may only be updated. Each must exist where the world forks from (42P01). Left out: every table |
tenant | 'column = value' (a number, true/false, or quoted text), or {"org_id": 42} in META. A row counts when the column holds the value before the change and after it, so moving a row out of the tenant is outside too |
max_rows, max_deletes | rows this run changes, and how many of those it deletes. Under a policy an agent may declare less than its policy allows, never more |
intent, run | shown in Reviews and SHOW WORLDS, not judged |
- Views, functions, sequences and schemas are always outside a declared scope.
- It’s checked on the world’s own rows as the merge applies them; columns its parent changed and
BY COLUMNScombines in don’t count. - Refused (
42501) or, for an agent with a policy, queued. Only people widen it:ALTER WORLD w SET (may_change = 'leads'); the agent that forked it can’t. - A merge policy with
require_scope = truerefuses its agents’ forks that declare neithermay_changenortenant.
Which run made it
Tag a run
run_id, prompt_version, model and tool_schema in a world’s metadata say which run of an agent made its changes. Reviews and SHOW AUDIT show them in a run column.
create world run_r42 with (run_id = 'r42', prompt_version = 'triage-v9', model = 'claude-opus-5-5', tool_schema = 'crm-tools-3');-- later, if that prompt version was wrong:undo agent "claude-ada-shop" since '-1 day' where run_id = 'r42';