Sections & addressing
Every markdown file in Source is a section tree. A heading of rank n opens a section that owns everything after it — paragraphs, code, lower-ranked headings — until the next heading of rank ≤ n. Agents read and edit at section granularity, so updating one runbook step never rewrites the file around it.
Take this document at /runbooks/deploy.md:
Deploy notes for the api service.
# Deploy
## Prepare
Check the dashboard before starting.
## Ship
Run the release script.
### Rollback
Revert the release commit.
Deploy owns everything below it; Ship owns Rollback; the sentence before the first heading is the preamble — a section of its own.
Addresses#
Every section has two addresses, and every operation that takes a ref accepts either:
- Slug path — GitHub-style slugs of the heading trail:
deploy/ship/rollback. Stable across reorderings; this is the address to store. - Ordinal — 1-based document position, nesting by rank:
1.2.1here isdeploy/ship/rollback. Convenient for "read section 4" style access.
The preamble is "" (or ordinal 0). Duplicate headings get -2, -3 suffixes, deduplicated per sibling group, not per document — so two ## Notes under different parents keep their clean slugs, and an exact slug path always wins over an ambiguous bare slug.
list_sections returns the whole address book of a file:
[
{ "ordinal": "0", "slugPath": "", "title": "", "rank": 0 },
{ "ordinal": "1", "slugPath": "deploy", "title": "Deploy", "rank": 1 },
{ "ordinal": "1.1", "slugPath": "deploy/prepare", "title": "Prepare", "rank": 2 },
{ "ordinal": "1.2", "slugPath": "deploy/ship", "title": "Ship", "rank": 2 },
{ "ordinal": "1.2.1", "slugPath": "deploy/ship/rollback", "title": "Rollback", "rank": 3 }
]
These same addresses are the heading anchors in the web viewer — #deploy/ship on the page and ref: "deploy/ship" in the API are one namespace.
read_section#
read_section returns a section's raw markdown, subsections included:
{ "libraryId": "LIB_ID", "path": "/runbooks/deploy.md", "ref": "deploy/ship" }
## Ship
Run the release script.
### Rollback
Revert the release commit.
replace_section#
replace_section replaces a section's body — everything after its heading line, subsections included — and leaves every other byte of the document identical. The heading line itself stays, so the section keeps its address:
{
"libraryId": "LIB_ID",
"path": "/runbooks/deploy.md",
"ref": "deploy/ship",
"newContent": "Run the release script with --canary first.\n\n### Rollback\n\nRevert the release commit.\n"
}
After the call, only the Ship subtree changed:
## Ship
Run the release script with --canary first.
### Rollback
Revert the release commit.
replace_in_section#
replace_in_section runs a regex find-and-replace scoped to one section — the smallest possible edit:
{
"libraryId": "LIB_ID",
"path": "/runbooks/deploy.md",
"ref": "deploy/prepare",
"pattern": "the dashboard",
"replacement": "the release dashboard"
}
The result reports the match count; zero matches leaves the file untouched. A match elsewhere in the document — the same phrase under Ship, say — is out of scope and unharmed.
Why sections matter#
Whole-file writes force an agent to hold the entire document in context and re-emit it perfectly. Section operations don't: an agent reads list_sections, pulls the one section it needs, and replaces exactly that. Smaller reads, smaller writes, no accidental rewrites of the 900 lines it didn't touch — and a cleaner diff in the revision history.