Branches and Proposals
A branch is an isolated working copy of a Brain. Give an agent a branch-scoped key to let it write records there without granting access to main. Main’s state does not change when the branch changes.
Read that branch with the branch coordinate on /context, /search, or
/query. Documents submitted with branch and extract: false are pinned as staged evidence to
that branch and remain invisible on main. Compare includes document versions
alongside record changes. A Proposal stages the combined delta; approval and
publication through the ChangeSet lifecycle make its selected content visible
on main. Branch reads and their receipts stay scoped
to the branch head they served.
Reads never wait on a branch’s writer. Any number of /context, /search,
/query, document, receipt and compare reads run on one branch at the same
time, and beside a write to it. Each read is served from one committed branch
head and its receipt names that head.
A read-only branch
Section titled “A read-only branch”Create a branch with mode: "read_only" to pin it at its fork point:
{ "mode": "read_only" }POST /v1/brains/{handle}/branches with that body forks the current main
state exactly as a writable branch does, then refuses every write to it with
409: records, documents, and opening a Proposal (it has nothing to publish).
A key scoped to it can hold only read; minting or widening one with more is
a 409. It can still be read by any number of callers at once, compared with
main, archived, and destroyed. The mode is fixed at creation; retrying the
same Idempotency-Key with a different mode is a 409.
Use it as the pinned view for an evaluation: point the evaluation’s reads at the read-only branch and re-run them later against the knowledge as it was when the branch was created, while main keeps changing.
Archive, restore, destroy
Section titled “Archive, restore, destroy”POST /v1/brains/{handle}/branches/{branchId}/archive stops everything a
branch does. It refuses reads and writes, emits no date.reached events, and
runs no index rebuilds. Its data is kept.
POST /v1/brains/{handle}/branches/{branchId}/restore resumes it: date
crossings that came due while it was archived fire on the next sweep, and
parked rebuilds continue.
DELETE /v1/brains/{handle}/branches/{branchId} destroys an archived branch.
In the same transaction that records the destruction, it removes everything
the fork and the branch’s writes created: record and typed-field indexes,
pending and emitted date crossings, document pins, staged documents nothing
else references, read watermarks and cursors. It also closes the branch’s open
Proposals. Nothing about the branch fires or answers afterwards. The Brain’s
event history, read receipts, and the closed Proposals remain. Retrying the
delete is safe.
Compare and open
Section titled “Compare and open”GET /v1/brains/{handle}/branches/{branchId}/compare returns the branch’s
changes since it forked, applied onto main as main is now. It stages nothing,
records no admission decision, and waits on no writer. Run it whenever you
need a fresh preview.
Compare is three-way. Changes main made after the fork are not reported as branch changes, so they are never reverted. Each record and document is compared at three points: main at the fork, main now, and the branch.
- A record the branch changed and main did not is a change.
- If the branch and main changed different fields of the same record, they merge: the proposal keeps main’s new values and adds the branch’s.
- If both changed the same field to different values, the record is a conflict.
- If main deleted a record the branch changed, or both created a record with the same id, the record is also a conflict.
- A document follows the same rules for its served version. If the branch pins a new version and main now serves a different one, the document is a conflict.
{ "data": { "summary": { "nodes": { "upserts": 1, "deletions": 0 }, "edges": { "upserts": 0, "deletions": 0 }, "conflicts": 1 }, "diff": { "records": [ { "id": "gamma", "kind": "company", "operationId": "3121a944-e1a0-81d3-aadb-e2d489e867bd", "main": "changed", "conflict": true, "changes": [ { "part": "field", "field": "name", "fork": "Gamma Co", "main": "Gamma Co (main)", "branch": "Gamma Co (branch)", "conflict": true } ] } ], "documents": [] } }}main says what main did to the record since the fork: unchanged,
changed, deleted, or created. Each entry in changes is a field (or
part: "body") the branch changed. It lists main’s value at the fork, main’s
value now, and the branch’s value. A value is omitted when that side does not
have the field. A document entry lists the version the branch pins
(versionId), the version main served at the fork (forkServed), and the
version main serves now (expectedServed). Publication requires main to
still serve expectedServed.
POST /v1/brains/{handle}/proposals with a branch source pins that delta as
a Proposal and stages a review ChangeSet:
{ "source": { "kind": "branch", "branchId": "branch-id" }, "intent": "Update the company catalog" }The response gives the Proposal and its first immutable revision. The
revision includes changeIds and the pinned diff, which has the same
records and documents that compare returns. You can open a Proposal that
has conflicts. Each operation waits in review until someone approves it.
A conflicting operation carries the branch’s value, and its review item
names main’s value at the fork, main’s value now, and the branch’s value.
To publish only the clean changes, select their operationIds and leave
the conflicts as the remainder. The conflicts stay pending until a reviewer
approves or rejects them. Approving one publishes the branch’s value over
main’s. GET /proposals lists them; filter with
sourceKind or status. GET /proposals/{id} returns every revision and
the ChangeSets inside it. A branch-scoped key sees only Proposals from its own
branch.
When the branch advances, POST /proposals/{id}/revise pins its new delta in
a new revision. POST /proposals/{id}/close closes an open Proposal without
applying it. Applying still uses the existing ChangeSet select, approve,
and publish operations under main’s policy. A Proposal creates no shortcut
to accepted state. Direct agent writes staged by admission and source snapshot
reviews also appear as Proposals with sources direct and snapshot.
proposal.opened, proposal.revised, proposal.applied, and
proposal.closed appear on the event stream and can
be delivered to a webhook. The Brain MCP endpoint offers read-only Proposal
listing and inspection.