Skip to main content
Version: Next

What cedit sees: blocks, hashes, keys

A document is a flat sequence of edit blocks in document order. There are two kinds, and both diff, overlay and merge identically:

KindNode typesWhat the text is
unitheading, paragraph, th, tdthe inline source of the block
opaquefence, code_block, html_block, front_matter, hrthe token's own content (plus the fence info string)

The unit set is exactly tree_diff's translation units. The opaque set is cedit's addition: to a translator a code fence is never translated, so it is just copied — here it is the motivating edit, so it is a first-class block with its own identity.

Nothing in this section has to be taken on trust: cedit md blocks <file> prints precisely what it describes — every block's kind, node type, hash, occurrence and heading context — for any Markdown file, tracked or not, and cedit md canonicalize prints the canonical form the hashes are taken over. The md verbs are the reference for both; running them against a document as you read this is the fastest way to make the rest concrete.

Identity is a hash. Each block carries the 16-hex-char Merkle hash the vendored tree_diff.hash_tree assigns over the canonicalized document. Everything downstream is keyed by it: the overlay, the conflicts, the resolve argument.

Duplicates are disambiguated by occurrence. Two byte-identical fences in one document have the same hash, so a block's full address is <hash>:<occurrence>, occurrence counted in document order from 0. That is what lets you adapt just one copy of a repeated command:

RUNBOOK.md: 2 local edit(s)
[edit opaque fence] #2b8e761f4dae633c:1 sim=0.67
ctx : Production
base : bash scripts/deploy.sh
local: bash scripts/deploy.sh --env production --confirm

[edit unit td] #286d36272b08407b:0 sim=0.83
ctx : Production
base : release manager
local: release manager + SRE

The :1 says: the second of the two identical bash scripts/deploy.sh fences. (That positional half of the address has a sharp edge when upstream reorders — see What alignment buys you.)

Canonicalization comes first. Before anything is hashed, the document goes through an mdformat round trip. This is why formatting churn is free — and why your working copy may be reformatted the moment you snapshot it. A vendored table written with | --- | separators comes back canonicalized:

| Step | Owner | Blocking |
| -- | -- | -- |
| preflight | release engineer | yes |

That is the canonical form, once, on the first snapshot. It does not keep changing.

Front matter is one block. Editing a single key overlays the whole front matter, and an upstream change to any other key in it is a conflict on the whole block:

$ cedit diff
SKILL.md: 1 local edit(s)
[edit opaque front_matter] #3989fd03ddc7beae:0 sim=0.89
base : …ame: deploy version: 1 model: sonnet
local: …ame: deploy version: 1 model: opus

$ cedit sync --from vendor
SKILL.md: 0 edit(s) reapplied, 0 block(s) updated from upstream, 1 conflict(s)
[CONFLICT opaque front_matter] #3989fd03ddc7beae:0
base : name: deploy version: 1 model: sonnet
upstream: name: deploy version: 2 model: sonnet
local : name: deploy version: 1 model: opus (kept in the working file)
resolve : cedit resolve SKILL.md 3989fd03ddc7beae:0 --take local|upstream

You changed model, upstream changed version, and the two never touched — but the block is the unit, so it conflicts. Splitting front matter per key is future work; until then, expect front-matter edits to need a hand-merge (--show, edit, --take local) whenever upstream touches that block. ctx is empty here because front matter sits above every heading.