Skip to content

Anthropic built Scion for itself. Carbon Forge is yours →

Carbon Forge
Menu

Guide

Land a code change and its documentation in one changeset

A code change and the page that documents it land in the same changeset, together or not at all. This guide walks one such change from an open lane to a single landing.

Also in: Español, Português, 日本語

Disclosure: we build Carbon Forge at 100monkeys.ai, so read this as a maker's account. This guide was drafted by one of our AI agents from the public docs, chiefly Working in lanes and Code and files on the substrate, and reviewed by the company. Every call below is the docs' own example. Where the docs leave a question open, the last section says so.

Why one changeset

The docs home defines the unit: "A changeset is many writes landed as one: every page, atom, code tree and library entry it names is written in one transaction under one intent, the changeset's intent, or nothing is written" (Welcome to Carbon). Code and prose are entities of one record, so a reader never meets the new code beside the old page, and a revert takes both back together.

You need a token that can write lanes and trees; the SCM agent preset is the one the docs name for that work.

Step 1. Learn whether the trunk is protected

workspaces.get_settings {}
→ { "workspaceId": "…", "trunkProtected": true, "trunkProtectedEntities": false }

"With trunkProtected, code trees, library entries and changesets go through a lane; with trunkProtectedEntities as well, page and atom edits do too." Either way, a lane is the right shape here: the docs reach for one when "a change should be seen whole before it lands."

Step 2. Open a lane for the change

lanes.create { "label": "agent/7/fix-login" }

A label is slash-separated lowercase segments, unique among open lanes. "A lane costs nothing to open and holds no copy, so one per task, or one per agent, is the expected use." Pass owner to make the lane yours alone to write, resolve and land.

Step 3. Change the code on the lane

trees.apply_patch { "path": "app", "operations": [ { "op": "patch", "entry_path": "src/login.ts", "old_string": "password", "new_string": "magicLink" } ], "intent": "fix: accept magic links", "lane": "agent/7/fix-login" }

Pass the label as lane and "the write is held on the lane as a patch against the trunk": "the trunk is not written." trees.apply_patch sends only its operations (a substitution inside a file, a write, a removal or a move); trees.put sends the tree's whole list of entries. Either way "the lane keeps only what differs."

Step 4. Change the page that documents it, on the same lane

pages.apply_patch { "pathOrId": "guides/login", "old_string": "Sign in with a password.", "new_string": "Sign in with a magic link.", "lane": "agent/7/fix-login" }

Same label, so the page edit waits beside the code. If the change also renames a page, pages.move takes lane too, and "a rename belongs to the change that needs it." A screenshot goes in the same way with library.put_at_path and the label.

Step 5. Read what the lane would make

Pass the same lane to a read and "the answer is the trunk as it stands now with the lane's patches applied, composed on the call": pages.read for the page, trees.get, trees.diff or trees.grep for the code. Then review the whole:

lanes.diff { "label": "agent/7/fix-login" }
lanes.get { "label": "agent/7/fix-login" }

lanes.diff answers "entity by entity, the trunk's section and the lane's"; lanes.get answers the patches, the conflicts and landable: "open, at least one live patch, no conflict." In the app, /<workspace>/lanes/<label> shows the same, with each changed text file as a unified diff. A reviewer who reads lanes.export and approves at its composition hash "has approved exactly that change."

Step 6. Answer any conflict

If someone edited the same lines of the page or the file on the trunk meanwhile, the patch is in conflict, and "nothing merges by itself." lanes.conflicts gives the patch, the section as it was, the trunk's section now and the changesets that moved it. Answer with lanes.resolve or drop the patch with lanes.drop_patch. A substitution inside a file "replays on the trunk's current file, so two agents changing different parts of one file both land."

Step 7. Land once, with an intent

lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links", "dry_run": true }
lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links" }

Run the dry run first, then the landing. "Every live patch is replayed as one changeset whose intent is lane agent/7/fix-login: fix: accept magic links", checked again inside its own transaction and "refused whole, with every conflict named, if anything moved." Start the intent with feat, fix, docs, chore or revert and the release notes group it under that word. Send an idempotency_key and a retry after a timeout "answers the first landing's result instead of landing twice."

Step 8. Read the landing back

Open /<workspace>/changes/<id>: it shows the whole intent, the actor, the lane it landed from, its seal and its entries, the code file and the page together. Blame on the changed line now answers with the intent you wrote: trees.blame gives "each line's revision, changeset, intent, actor and landing lane" (Code and files). To undo both at once, changesets.revert writes one new changeset that restores every entity the landing wrote.

What the docs leave unanswered

Related guides

Land the code and its docs together.