Docs · IBM Bob
Run Cleave in Bob
Cleave's planner is a custom mode in IBM Bob. Bob groups and orders the hunks Cleave gives it, and it has no tool that can write code. These are the files the mode ships with, exactly as cleave init installs them.
01
Install the engine and the mode
The engine is a Python package with a cleave command. cleave init adds the mode, the MCP server entry, the hooks and the mode rules to your repository's .bob/ folder. Modes, MCP servers and hooks you already have stay as they are, and running it again changes nothing.
uv tool install ./packages/engine
cleave init
02
The ✂ Cleave mode
The mode's tool groups are the guarantee. It can read, call MCP tools, spawn read-only explore subagents and keep a todo list. It has no edit and no execute group, so it cannot change a file or run a command, however it is prompted.
customModes:
- slug: cleave
name: ✂ Cleave
description: Split a large diff into a proven stack. Never writes code.
roleDefinition: >-
You split an existing diff into an ordered stack of layers. You only group
and order the atoms Cleave gives you. You never write or edit code.
whenToUse: A branch or PR is too large to review as one change.
customInstructions: Follow .bob/rules-cleave/. Change the plan only with cleave_* tools.
groups: [read, mcp, subagent, todo]
allowedSubagents: [explore] # read-only subagentsreadmcpsubagent (explore)todoeditexecute
The mode follows a fixed procedure, shipped as its rules:
# 01 — Procedure You split an existing change into a stack of layers. You never write or edit code, never run commands, and never switch modes. Every change to the plan goes through `cleave_*` tools. 1. **Status.** Call `cleave_status`. If a run is already active, continue it. 2. **Start.** Otherwise read the request, `Cleave <head> onto <base>`, and call `cleave_start(base, head)` with those branch names. The result lists the slices. 3. **Explore.** Start one read-only `explore` subagent per slice file. Each reads its slice and returns, for every atom id in it, a concern label (≤ 60 characters) and an intent (≤ 200 characters). If subagents aren't available, read the slices yourself. 4. **Propose.** Call `cleave_propose_plan` with ordered layers and the labels (`02-plan-schema.md`): foundations first (models, schemas, configuration), then logic, then interfaces; tests in the same layer as the code they test; each layer under the repository's line limit where the dependencies allow. Fix any returned violation with another proposal or `cleave_move_atoms` before verifying. 5. **Verify.** Call `cleave_verify`. If it returns `pending`, call `cleave_verify_status(round)` until the status is `pass` or `fail`. 6. **Repair.** For each failing layer follow `03-repair.md`: `cleave_read_log`, then `cleave_move_atoms`, then verify again. At most 3 repair rounds. 7. **Describe.** Call `cleave_describe_layer(n, title, body)` for every layer: a pull request title and a short body saying what the layer adds and why it stands alone. 8. **Finish.** Call `cleave_finish`, then end the task with a summary: the layers, the five checks, and the report path it returned.
03
Cleave tools: Bob's only write surface
Bob changes the plan only through the Cleave MCP server. None of its eleven tools can write source code; the ones that write touch the plan, .cleave/ or cleave/* branches built from existing hunks.
{
"mcpServers": {
"cleave": {
"command": "cleave",
"args": ["mcp"],
"alwaysAllow": [
"cleave_start",
"cleave_status",
"cleave_atoms",
"cleave_graph",
"cleave_propose_plan",
"cleave_move_atoms",
"cleave_verify",
"cleave_verify_status",
"cleave_read_log",
"cleave_describe_layer",
"cleave_finish"
]
}
}
}In your repository, cleave init writes the full path of cleave and mcp --repo <repository> into this entry, because Bob may start MCP servers outside the repository and without your shell's PATH.
| Tool | What it does | Writes |
|---|---|---|
cleave_start(base, head)Atomize, build the graph, write slices and open a run | Atomize, build the graph, write slices and open a run | .cleave/ only |
cleave_status()Atom count, current plan version, last verify result | Atom count, current plan version, last verify result | — |
cleave_atoms(slice?)Atoms with file, line counts and symbols | Atoms with file, line counts and symbols | — |
cleave_graph(atom_id?)Dependency edges and forced groups | Dependency edges and forced groups | — |
cleave_propose_plan(layers)Check coverage and order, save a plan version, return violations | Check coverage and order, save a plan version, return violations | Plan only |
cleave_move_atoms(ids, to_layer, reason)New plan version plus violations | New plan version plus violations | Plan only |
cleave_verify()Build every prefix and run the check command in parallel | Build every prefix and run the check command in parallel | cleave/* branches |
cleave_verify_status(round)Poll a long verification round | Poll a long verification round | — |
cleave_read_log(layer, round)Excerpt of a failing layer's log | Excerpt of a failing layer's log | — |
cleave_describe_layer(n, title, body)Store the pull request text for a layer | Store the pull request text for a layer | Text only |
cleave_finish()Write the report and close the run | Write the report and close the run | .cleave/ only |
04
Hooks: a second lock and an audit trail
A PreToolUse hook guards every tool call while a run is active, and a PostToolUse hook appends each call to the run's event log. That log is where “0 lines written” on every proof page comes from. Hooks are shell commands, so they cost no Bobcoins.
{
"hooks": {
"PreToolUse": [
{ "hooks": [{ "type": "command", "command": "python3 .bob/hooks/guard.py", "timeout": 10 }] }
],
"PostToolUse": [
{ "hooks": [{ "type": "command", "command": "python3 .bob/hooks/audit.py", "timeout": 10 }] }
]
}
}- Read tools are allowed.
- Read-only explore subagents are allowed.
- Todo updates, questions to you and finishing the task are allowed.
- Calls to the cleave MCP server are allowed.
- Everything else is blocked with exit code 2 while a run is active: file writes, commands, mode switches and other MCP servers.
05
Run a split in Bob IDE
Switch Bob to ✂ Cleave and name the branch to split. Bob reads the diff in parallel slices with explore subagents, proposes layers, verifies them, and moves atoms when a layer fails. After three rounds it merges the two layers and says so instead of writing glue code.
Cleave feat/loyalty-and-seat-upgrades onto main
When the run finishes, send it to the web app to get the stack pages and a shareable proof.
cleave push --title "<pull request title>" --pr <number> --head-branch <branch> --base-branch main
06
Headless runs with bob run
The same mode runs without the IDE through Bob Shell. The runner uses this command, and the cost cap comes from the repository's Bobcoin setting.
bob run --mode cleave --format stream-json --max-cost 3 "Cleave feat/loyalty-and-seat-upgrades onto main"
To start splits from the web app instead, keep a runner going on your machine. It asks for runs you queue on New split, runs each one with the command above, streams its events to the run's page and sends the finished bundle, exactly like cleave push.
export CLEAVE_URL=<your deployment> CLEAVE_TOKEN=clv_… cleave runner
See what a finished run looks like.
Bob configuration references: custom modes, MCP, lifecycle hooks and non-interactive runs in the IBM Bob documentation.