Skip to content

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> off main, 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, and main is untouched until the branch validates.
  1. 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-status until it is current.

    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" });
  2. Write facts to the branch. Commit exactly as you would to main, with the client (or ?branch=) scoped to exp-1. These writes are invisible on main until 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" },
    );
  3. Validate-then-merge back onto the fork parent. Scope to the TARGET (main, the parent), name the child in from_branch, and pass an idempotency key. With validate: 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);

    The child’s post-fork commits replay onto main as one new commit, with event ids preserved, so the provenance of each fact is retained.

  4. Inspect the result. The response reports exactly what happened:

    • merged: true if the merge applied, false if 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 (with validation, a SchemaAuditReport) when SHACL refused the merge.
    • source_deleted: true if delete_source ran.
    • idempotent_replay: true when the same idempotency key replayed instead of re-applying.
  5. Delete the branch (optional). Pass delete_source: true to 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 with DELETE /v1/graph/branch?branch=<branch>&confirm=<branch>.

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.

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.

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.

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