Chronos/DBWaitlist

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.

SQL
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: 40001 for anyone; for an agent keeping to a merge policy, a broken rule, and the world waits in Reviews. A dry run shows it in its blocked row.
  • For every world of an agent: a merge policy with check_reads = true turns 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 SELECT in 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 by AND) 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', an OR of columns) reads the whole table; so do full-text and vector searches.
  • Only the columns read: a SELECT keeps, per table, the columns it used. select sum(amount) from orders where status = 'open' isn’t held when an open order’s notes changes, but is when its amount or status does. 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.

SQL
check reads of world payout;--  checked_against | changed-- -----------------+--------------------------------------------------  main            | read rows that changed since it forked: accounts/1

Declared 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.

SQL
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

KeyMeans
may_changea 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_deletesrows 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, runshown 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 COLUMNS combines 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 = true refuses its agents’ forks that declare neither may_change nor tenant.

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.

SQL
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';
Stale reads and scope · Chronos