Worlds
Merge policies and reviews
Approve rules once. Merges that keep to them land on their own; the rest wait for a person.
SoonThese pages describe Chronos Cloud, which opens from the waitlist. Running Chronos yourself? Self-host docs
Reviewing every agent’s diff works for ten changes a day and breaks at ten thousand. A merge policy changes what a person approves: you approve the rules once, and review only the merges that break them.
How it works
MERGE WORLD w ├─ may the agent merge w at all? (merge_own or merge; write_main for main) ── no ──> refused └─ inside the merge, holding its locks, on the exact rows it is about to apply: does the agent keep to a merge policy? no ─────────────────────────────────────────> merged yes: every rule kept? ── yes ────────────────> merged └─ no ──> refused (42501), every broken rule named; the world stays, queued in Reviews a person: read it, then Merge (at the version read) or Discard- What’s checked is what merges. The rules run inside the merge, on the rows it applies, under its locks: nothing written a moment before or after can slip past.
- Stale work doesn’t win. A merge that would overwrite rows
mainchanged since the fork needs a person, unless the policy allowsoverwrite. - The queue says when it’s out of date. Each held world records its version when the agent asked;
changed_sincesays the agent wrote more after. - A policy only takes rights away. An agent needs
merge_ownto merge at all; the policy decides which merges need a person. An agent keeping to a policy changesmainonly by merging: a direct write,RESTOREorUNDOthere is refused, since it would skip the rules.
Start here: no delete lands on its own
Whatever else a policy says, start every agent with max_deletes = 0. A wrong update leaves a row you can see and fix; a wrong delete leaves nothing to look at. Most agents rarely delete, so the queue stays short, and each delete that comes through shows you whether to trust the agent with more.
create merge policy agents with (max_deletes = 0);alter agent "claude-ada-shop" set (policy = 'agents');The rules
Every rule is optional. A rule left out limits nothing, except schema, overwrite and critical, which start on the safe side.
| Rule | A merge needs a person when | Default |
|---|---|---|
max_rows | it changes more rows than this | no limit |
max_deletes | it deletes more rows than this; 0: any delete | no limit |
rows_per_hour | with the agent’s other merges into main in the last hour, it changes more rows than this | no limit |
deletes_per_hour | the same, for deleted rows: an agent can’t get past max_deletes by splitting a job | no limit |
confirm_deletes_over | it deletes more rows than this, and the person approving must type the count | no count |
max_age | its world was forked longer ago than this ('1h', '30 minutes') | no limit |
tables | it changes a table not in this list ('orders,items') | any table |
review_tables | it changes any table in this list | none |
review_columns | it changes any column in this list ('users.email,accounts.owner'), even in one row | none |
schema | false, and it changes a table itself, a view, function, sequence, schema or type | false |
overwrite | false, and it overwrites rows its parent changed since the fork | false |
critical | false, and it changes a column a reader marked critical reads | false |
check_reads | true: its agents’ worlds record what they read, and a merge that read rows changed since waits | false |
require_scope | true: its agents must declare may_change or tenant when forking | false |
max_effects, effects_per_hour, review_channels | it queues more effects (emails, webhooks, payments sent once it lands) than allowed, or any on a listed channel | no limit |
reviewer | never itself: names an agent that decides held merges before a person | none |
- Counting: one per row the merge inserts, updates or deletes in the parent, after
ONLY TABLES/ONLY KEYS. A row settledUSING THEIRSchanges nothing and doesn’t count; indexes, constraints and counters never do. - Names: read as SQL reads them:
ordersfolds to lowercase,"Orders"quoted is another table. - Hourly rules: a sliding hour of the agent’s own merges into
main; merges between its worlds count when they reachmain, and a person’s approval doesn’t count. - Merge checks: a broken merge check is one more broken rule for an agent with a policy: its world is queued with the rows the check found.
Give a policy to an agent
On Chronos Cloud each person’s coding agent is claude-<login>-<project> (SHOW AGENTS lists them; the Policies page shows who keeps to what). Make the policy, then give it:
create merge policy careful with (max_rows = 500, max_deletes = 0, review_tables = 'payments');alter agent "claude-ada-shop" set (policy = 'careful');show merge policies;| Statement | Does |
|---|---|
CREATE MERGE POLICY name [WITH (…)] | a new policy |
ALTER MERGE POLICY name SET (…) | the rules named change; the rest stay. null removes a limit |
DROP MERGE POLICY name [CASCADE] | refused while an agent keeps to it, unless CASCADE releases them in the same step |
ALTER AGENT name SET (policy = 'name') | the agent keeps to it from its next merge; policy = null for none |
SHOW MERGE POLICIES | every policy and its rules |
The review queue
A held merge tells the agent why, and the agent stops and reports it (on a production project the answer carries the review’s link):
merging bot_bulk needs a person's review (merge policy careful: it changes 6 rows (at most 3)); it's queued (SHOW REVIEWS)The dashboard’s Reviews lists held worlds, oldest first. In SQL, SHOW REVIEWS:
| Column | Meaning |
|---|---|
world, owner, policy | the world, the agent that forked it, the policy that held it |
reasons | every rule it broke: changes 6 rows (at most 3); deletes 2 rows (at most 0) |
asked, version | when the agent asked, and the world’s version then |
changed_since | the agent wrote more, or part was merged, after it asked: read the diff again |
changes | per table: deleted, updated, inserted, the tables that delete most first |
scope | what the world declared it would change, if anything |
- Approve: Merge in the dashboard, or
MERGE WORLD w AT VERSION n. It lands only if the world is still at the version you read. - Approve part:
MERGE WORLD w ONLY TABLES (orders)orONLY KEYS (…); the rest stays queued. - Reject: Discard, or
DROP WORLD w. - Email: owners and members are emailed when a world starts waiting (one email per version), unless they turn it off under Team → Notifications.
The agent can’t skip the queue: the hold is kept where only the database writes it, so the agent can’t clear it or merge the world until a person does, or until it fits the rules.
Confirming big deletes
With confirm_deletes_over = 100, a merge deleting more than 100 rows waits, and the person approving must give the count, counted from the merge’s own plan:
merge world cleanup confirm deletes 4812;-- a missing or wrong count is refused (55000) with the real one, before anything is writtenA dry run’s notice gives the CONFIRM DELETES n that approves it. An agent can’t give the count for its own merge.
A reviewer before a person
Rules first, a reviewer model next, a person for what’s left. Name an agent as the policy’s reviewer; it reads each held merge and decides, with a reason:
alter merge policy careful set (reviewer = 'critic');-- as critic (or the MCP tools reviews, diff and review):review world bot_bulk approve reason 'a duplicate of lead 4'; -- merged, as it was heldreview world bot_refund reject reason 'lead 2 has an open deal'; -- not merged; a person sees whyreview world bot_odd pass reason 'not sure: check the deal'; -- left to a person- Approve merges at the held version; a world written to since is refused (
40001). A big delete underconfirm_deletes_overstill needs a person’s count. - Reject and pass leave the world queued with the decision in its
reviewercolumn. A person can overrule any decision. - Decisions are in the audit trail:
SHOW AUDIT FOR AGENT critic.
Tuning and measuring
alter merge policy careful set (max_rows = 100); -- the named rules change; the rest stayalter merge policy careful set (max_deletes = null); -- null: no limitalter agent "claude-ada-shop" set (policy = null); -- its rights alone decide nowshow merge outcomes; -- how each agent's worlds endedshow merge outcomes by policy;SHOW MERGE OUTCOMES counts, within the history window, how each agent’s worlds ended (merged on the rules alone, approved, rejected, discarded, still open) and how often merges were held, conflicted or refused. rules_rate is the share of decided worlds that merged on the rules alone: low means the rules hold too much; people undoing what passed means they hold too little.
Try rules before you set them
The MCP tool check_merge_policy runs rules against a world’s real changes and says whether its merge would land or wait, and why. Nothing is saved, so “what if max_rows were 1000?” is one call.
Recipes
| Agent | Policy | Why |
|---|---|---|
| Any new agent | max_deletes = 0 | nothing it deletes lands without you |
| Support bot fixing ticket typos | tables = 'tickets', max_rows = 20, max_deletes = 0 | it can’t reach anything else, or delete |
| Pricing agent | tables = 'prices', max_rows = 200, max_deletes = 0, review_tables = 'prices_history' | routine repricing flows; history rewrites wait |
| Data-cleaning job | max_rows = 5000, max_deletes = 50, deletes_per_hour = 200, confirm_deletes_over = 500 | deleting is its job: bounded, not banned |
| Anything near money | max_deletes = 0, review_tables = 'payments,refunds,payouts' | every change to money has a person on it |
| Migration assistant | schema = true, max_deletes = 0, review_tables = 'payments' | may change tables; deletes and money wait |
| Many agents, one table | leave overwrite = false | an agent that forked earlier can’t clobber a newer write |
| Identity and billing fields | review_columns = 'users.email,accounts.owner' | a quiet wrong value there does more harm than a loud delete |
From an agent
| Tool | Does |
|---|---|
merge_policies | the policies, and which agent keeps to each |
check_merge_policy | tries rules on a world’s real changes |
reviews | the queue |
set_merge_policy, drop_merge_policy | not listed for coding agents: only an admin sets rules (you, in the SQL editor or on the Policies page). An agent can propose rules and test them with check_merge_policy, never apply them |