Skip to content

The ledger

shipped 1.0.0-rc.2

Every environment cwp manages has one file: cwp/refs/<env>.yml, committed with the tree. It holds three things a crossing cannot work without, and they are worth telling apart.

base and seen are not the same field

base is what cwp last transferred. It moves when a crossing completes, in either direction, and at no other time. This is origin/main.

seen is how the environment looked the last time cwp read it.

The refusal does not depend on it. An upward crossing compares base against what its own survey has read from the environment. A remembered reading can go stale. The survey cannot, and it needs no recent cwp status --remote.

Conflating them is the mistake the git analogy invites. git fetch moves origin/main because the remote’s commits are the remote’s authored state. Here they are not: an editor’s change noticed in wp-admin is not something you have accepted, so it may not move the baseline cwp measures you against.

The consequence is concrete. If a read moved base, changed-here would be unreachable. Every read would agree with itself, every conflict would quietly vanish, and a push would overwrite somebody’s afternoon while reporting success.

base records what the environment holds, item by item, with a hash over what each item is. A role hashes by what it grants and a plugin by its version and status. A widget area hashes by its contents in order and a settings key by its value. A later comparison means something for that reason: a count would not see a role renamed or a plugin deactivated at an unchanged version.

For content it also records which post each item is on that environment. A post id is local to one install. The tree carries none of them: a committed item has to mean the same thing on two sites. The ref is about one site, so it can hold the pairing. The pairing is the one thing that survives a site losing its identities to a restore. cwp adopt reads it before it falls back to the slug, and never reads another environment’s.

The log

An append-only record of what happened, rendered by cwp log.

2026-08-22 09:14  deploy   12 content    c3f8a2 main
  restore point: 2026-08-22-091355-a3f
  previous tree: 7f2c9ab   (git checkout 7f2c9ab -- .)

A refusal is an event. A drift table can never give you that one. A prevented accident is a fact, and without the log it vanishes with the terminal scrollback. The log records why, not only that.

The events it writes are adopt, release, pull, push, deploy, refused and observed. Together they cover every run that crossed, every run the guards stopped from crossing, and every read that found the environment had moved.

A survey writes an entry only when it found something. A read that agrees with the baseline is not news, and the log has a bound. Spending it on the cheapest and most frequent command there is would fill it with runs that changed nothing.

A read that disagrees lands in the log as observed, naming the artifacts that had moved. It answers when did this start, the one question a drift table cannot answer: the table says what differs now.

The entry carries both routes back where they exist: the remote restore point taken before an upward write and the dangling snapshot taken before the pull overwrote the tree. A backup nobody can find is not a backup, and a terminal is not where one is findable.

The log holds two hundred entries at most. The bound is about the file rather than about usefulness. The file goes into git, into diffs and occasionally into a merge by hand. A file nobody can read a conflict in is worse than a shorter history.

It is committed

The baseline travels with the tree, so a colleague’s checkout knows what the last transfer agreed on without anything shared out of band.

A merge conflict in the ref happens exactly when two people synced the same object. One of them has to find out. The conflict is information rather than friction.

The site’s copy is a witness

_cwp_sync stays on the post, and cwp writes it only on an upward write. Authority is local: the ref is what cwp agreed to. The marker on the site is evidence that survives a lost repository and tells a second checkout what happened.

Where the two disagree about adoption, cwp reports both and chooses neither. Choosing would mean guessing which one is in front of you, and guessing wrong mints a second identity over the first.