Use case: branch-per-session workspaces & safe merges
When several writers work on one graph at once, or when you want to try a
speculative set of facts before committing to them, their in-progress writes
should stay off main. little big brain gives every session, experiment, or
hypothesis its own branch forked from main. Each branch is a private
workspace: write to it freely, then validate-then-merge it back onto its fork
parent as a single commit. SHACL runs first, conflicts are enumerated in the
response, and a clean branch can be deleted as part of the merge.
Two concrete shapes:
- Multi-writer isolation. N writers each work on
agent-<id>offmain, so one writer’s half-finished facts never collide with another’s. Each merges back only when its task validates. - Hypothesis / experiment branch. Write speculative facts to
exp-1, validate them against your SHACL shapes, and merge only if clean. Otherwise discard the branch. The experiment is reversible, andmainis untouched until the branch validates.
-
Fork a branch off
main. The scoped?branch=names the NEW branch; the request body says which branch it forks from. Scope the client to the new branch first, then create it.Branch creation immediately queues the new branch’s own immutable published generation, including for an empty fork at commit 0. No marker write is needed. If the next step needs a strong read, wait on that branch’s
publication-statusuntil it iscurrent.import { LbbClient } from "@littlebigbrain/client";const lbb = new LbbClient({baseUrl: "https://0abc1def--production.db.eu.littlebigbrain.com",apiKey: process.env.LBB_API_KEY,});// Scope to the new branch, then fork it from main.const exp = lbb.graph("main", { branch: "exp-1" });await exp.createBranch({ from_branch: "main" });import osfrom lbb import LbbClientlbb = LbbClient("https://0abc1def--production.db.eu.littlebigbrain.com", api_key=os.environ["LBB_API_KEY"])# The Python SDK forks over HTTP: POST /v1/graph/branch scoped to the new branch.import httpxhttpx.post("https://0abc1def--production.db.eu.littlebigbrain.com/v1/graph/branch",params={"graph": "main", "branch": "exp-1"},headers={"Authorization": f"Bearer {os.environ['LBB_API_KEY']}"},json={"from_branch": "main"},).raise_for_status()Terminal window curl -X POST "https://0abc1def--production.db.eu.littlebigbrain.com/v1/graph/branch?graph=main&branch=exp-1" \-H "Authorization: Bearer $LBB_API_KEY" \-H "Content-Type: application/json" \-d '{"from_branch": "main"}' -
Write facts to the branch. Commit exactly as you would to
main, with the client (or?branch=) scoped toexp-1. These writes are invisible onmainuntil the merge. Loading RDF works the same way: scope the import to the branch.await exp.facts.create({triplets: [{ source: { type: "SERVICE", name: "auth-service" }, relation: "WRITES_TO",target: { type: "DATABASE", name: "sessions-db" },confidence: 0.88, evidence: "hypothesis: move sessions off user-db" },],},{ idempotencyKey: "exp-1-sessions-split" },);lbb.graph("main", branch="exp-1").facts.create({"triplets": [{"source": {"type": "SERVICE", "name": "auth-service"}, "relation": "WRITES_TO","target": {"type": "DATABASE", "name": "sessions-db"},"confidence": 0.88, "evidence": "hypothesis: move sessions off user-db"},],}, idempotency_key="exp-1-sessions-split") -
Validate-then-merge back onto the fork parent. Scope to the TARGET (
main, the parent), name the child infrom_branch, and pass an idempotency key. Withvalidate: true(the default) the active SHACL schema runs on the would-be merged state first and refuses the merge on any violation.const res = await lbb.graph("main").mergeBranch({ from_branch: "exp-1", validate: true, delete_source: false },{ idempotencyKey: "merge-exp-1-v1" },);console.log(res.merged, res.commits_applied, res.conflicts);res = lbb.merge_branch({"from_branch": "exp-1", "validate": True, "delete_source": False},idempotency_key="merge-exp-1-v1",)print(res["merged"], res["commits_applied"], res.get("conflicts", []))Terminal window curl -X POST "https://0abc1def--production.db.eu.littlebigbrain.com/v1/graph/branch/merge?graph=main&branch=main" \-H "Authorization: Bearer $LBB_API_KEY" \-H "Idempotency-Key: merge-exp-1-v1" \-H "Content-Type: application/json" \-d '{"from_branch": "exp-1", "validate": true, "delete_source": false}'The child’s post-fork commits replay onto
mainas one new commit, with event ids preserved, so the provenance of each fact is retained. -
Inspect the result. The response reports exactly what happened:
merged:trueif the merge applied,falseif it was refused or a no-op.commits_applied: how many branch-local commits were replayed (as one).conflicts: an array of{ kind: "supersedure_race", edge_event_id }. Each entry is a branch edge that a later supersedure on the target replaced. Every dropped edge is listed.refusal: set (withvalidation, aSchemaAuditReport) when SHACL refused the merge.source_deleted:trueifdelete_sourceran.idempotent_replay:truewhen the same idempotency key replayed instead of re-applying.
-
Delete the branch (optional). Pass
delete_source: trueto purge the child branch’s objects after a clean merge, so a merged experiment leaves no branch objects in storage. Discard a failed hypothesis the same way: skip the merge, and delete the branch withDELETE /v1/graph/branch?branch=<branch>&confirm=<branch>.
A refused merge (SHACL fails)
Section titled “A refused merge (SHACL fails)”If the merged state would violate an active shape, the merge is refused and
main is untouched:
{ "merged": false, "commits_applied": 0, "refusal": "SHACL validation failed on the would-be merged state", "validation": { "conforms": false, "violations": [ /* … */ ] }, "snapshot": { /* main's untouched head */ }}The experiment stays on exp-1, and you read validation to see exactly which
constraint failed. That is the same machine-readable report an agent can use to
self-correct. See
SPARQL & SHACL for shape authoring.
A merge with a reported conflict
Section titled “A merge with a reported conflict”When a fact was superseded on main after the fork point, the target’s newer
version takes precedence and the branch’s version is dropped. The response
reports it:
{ "merged": true, "commits_applied": 3, "conflicts": [ { "kind": "supersedure_race", "edge_event_id": "evt_7f3a…" } ], "snapshot": { /* main's new head */ }}Every dropped edge is enumerated by edge_event_id, so a session or a human can
re-assert it.
From an agent (MCP)
Section titled “From an agent (MCP)”Agents run the whole lifecycle through the one lbb_branch tool, part of the
MCP tool set. Fork with action: "create", merge with
action: "merge":
// Fork a private workspace for this session, off main.{ "action": "create", "from_branch": "main", "graph": "main", "branch": "agent-42" }
// …the agent writes facts to agent-42 via lbb_commit…
// Validate-then-merge back onto main, consuming the branch when clean.{ "action": "merge", "from_branch": "agent-42", "validate": true, "delete_source": true, "graph": "main", "branch": "main" }The tool refuses the merge with the SHACL report on violation and returns the
same conflicts array, so an extraction pipeline cannot write invalid facts to
main.
Why this shape
Section titled “Why this shape”- Isolation comes from branching. Object-storage CAS lets many writers fork and write concurrently with no single-writer bottleneck, and conflicts are resolved deterministically at merge time.
- Merge is one commit, provenance intact. The child’s commits collapse into a single new commit on the parent, and event ids are preserved, so the temporal record still reflects what happened.
- Validate before merging. SHACL validates the merged state, so a branch that conforms on its own can still be refused when the combined state violates a shape.
- Idempotent. A retried merge (same
Idempotency-Key) replays instead of double-applying, which is safe for at-least-once agent loops and network retries.
Related
Section titled “Related”- Load RDF data: the writes a branch isolates.
- SPARQL, structured query & SHACL: author the shapes the merge validates against.
- Time-travel audit: read a branch’s history after the merge.
- MCP server reference: the
lbb_branchtool and the full tool set. - HTTP API: the raw
/v1/graph/branchand/v1/graph/branch/mergeendpoints.