Skip to content

The build loop

shipped 1.0.0

cwp has no build command and is not going to get one. What it has is a cycle, and the cycle is the product. Every command below exists because a step in it needed one.

This page is that cycle, end to end. It holds for any project cwp manages. A site with a page builder runs one more command than a site without, and nothing else about it changes.

The loop

cwp db pull              # 1. production's database, into the local container
                         # 2. build — in the browser, or through an MCP client
cwp pull                 # 3. the tree, out of the database into files
git diff                 # 4. read what you actually changed
git commit               # 5.
cwp deploy dev           # 6. the tree and the files, upward

Six steps, and the two halves are not symmetrical: down is a copy, up is a diff. cwp db pull replaces the local database wholesale. That makes it safe to run whenever you are unsure what state you are in. A push sends named things and refuses the ones it cannot place.

Step by step

1. cwp db pull: start from what is live

cwp db pull imports the environment’s database into the local container, scrubbed and rewritten for the local URL. The mail guard goes in whether or not scrubbing ran. Uploads come down as a proxy rather than a copy. The first run against an image-heavy site therefore takes about two minutes rather than an hour. cwp media pull copies them when you want them local.

The database and the tree are different commands, and step 3 is the other one. This step gives you a site to work in; that one gives you files to review.

Take a snapshot before you begin if you want a point to return to: cwp db snapshot.

2. Build

In the Bricks builder, in wp-admin, or through an MCP client talking to the site’s abilities. cwp is not involved and does not want to be. The work happens in this step, and the tool’s job is to be absent during it.

3. Pull what you built: the step people skip

Everything you built lives in the database: the design system in options, the pages in postmeta. cwp deploy moves files. A deploy after a build session therefore ships nothing you did, reports success, and is the single most common way to lose an afternoon with this tool.

cwp pull                 # the design system, the pages, terms, menus, settings,
                         # widget areas, roles and the inventory → files

One command, and it writes a tree of files rather than touching the site. It is safe to re-run: a pull that changes nothing writes nothing. It carries every artifact at once. A partial read leaves a baseline that means nothing in particular.

Narrow it with --only when you know what you touched:

cwp pull --only bricks   # colours, typography, classes, templates → bricks/

Check what came out before committing:

cwp status --drift

That compares the local site against the committed tree and says which is ahead. It costs a container. A bare cwp status does not, and still runs offline in half a second.

4–5. Read the diff, then commit

git diff bricks/ content/ is the review step, and it is readable on purpose. The trees are canonical JSON and YAML with names in place of environment-local ids, so a change to a colour is one line rather than a re-serialised blob.

Here the tree becomes the record. Everything after it moves what you committed, never what sits in a database.

6. cwp deploy: everything, upward

cwp deploy is cwp push plus the remote post-steps and the post-deploy hook. The push carries both halves of what you built: the six artifacts out of the tree and the tracked files. One confirmation lists every page, every image it would upload and every attachment whose text it would overwrite. Terms first, then pages parent-first, then the navigation.

One command for both is the reason this list is six steps: the pages and the files go up together, and neither can be the one you forget.

Where the loop is guarded

  • Nothing moves up by default. Database and uploads flow downward; the upward writes are individually named commands, and each refuses a protected environment without --force.
  • cwp refuses a conflict, never merges it. If the target changed since cwp last saw it, the push stops and names the page.
  • cwp takes a remote backup before any upward write, unless you disable it deliberately.

The safety model is the full account.

Making the loop enforce itself

Two optional habits, neither of them cwp’s policy to impose:

A pre-push gate. cwp init scaffolds cwp/hooks/pre-push.sh with the line commented out:

cwp status --fail-on-drift

Uncomment it and a deploy aborts while the local site is ahead of the tree: step 3 forgotten. It fails on a column it could not read rather than passing, so a stopped container is a stop and not a green light.

A snapshot before each pull, and you get it for free: every pull leaves pre-pull-<env>-<timestamp>. cwp status reads “last pull” from there too.

What this loop is not

It is not continuous deployment. There is no watcher, no daemon and no auto-push. Every upward step is a command somebody ran on purpose, after reading what it said it would do. The safety model says why.