Runtime path moved cleanly. The documentation never followed, because only the runtime path had a script.

On 2026-02-07 I renamed the House of Gizmo ops tree from openclaw to hog. That’s commit d96c81e. 109 files changed, 574 insertions, 8,623 deletions. The runtime root went from /opt/openclaw to /opt/hog.

Today 22 files in that repo reference /opt/hog and 6 still reference /opt/openclaw. One of the six is the runbook I’d actually open during an incident.

What the script did, and what it refused to do

All of the cutover lives in deploy/migrate-opt-openclaw-to-opt-hog.sh, 65 lines. It runs once, as root, on the runtime host:

mkdir -p "$DST_ROOT"/{workspace,services,data,logs,run}
rsync -a "$SRC_ROOT/data/"      "$DST_ROOT/data/"
rsync -a "$SRC_ROOT/services/"  "$DST_ROOT/services/"
rsync -a "$SRC_ROOT/workspace/" "$DST_ROOT/workspace/"

Four decisions in there are why nothing broke on the day.

No symlinks. Line 9 of the script says it outright: “You want a clean cutover (no symlinks).” A compatibility symlink from /opt/openclaw to /opt/hog would have hidden the migration. Every stale reference would keep working and I’d never find any of them. I want the old name to stop answering.

rsync -a, not mv. Copy first, delete later, by hand. The closing message tells me rm -rf $SRC_ROOT is mine to run. A migration I can’t re-run is a migration I get one attempt at.

Subtree by subtree, each guarded. data, services, workspace, and logs copy in four separate if [[ -d ... ]] blocks instead of one recursive sweep. Nothing under run/ gets copied. It’s created empty, because runtime scratch state shouldn’t survive a move.

Stop the container first, best-effort. docker stop hog-email-automation is wrapped in || true. It’s a convenience, not a gate. The header already assumes nothing depends on the old root.

Ownership is chown -R openclaw:staff, also best-effort. The user account keeps the old name. First hint that “rename” was a bigger word than I was using it as.

Three weeks later I undid half of it

Commit 2f7b276 on 2026-03-01 dissolved the hog/ tree I’d just made. hog/deploy/ became deploy/. hog/workspace/email-automation/ became email-automation/. The top-level hog/ directory stopped existing. The repo layout went openclaw/ to hog/ to flat in twenty-two days.

/opt/hog survived that round. The repo directory name didn’t. There’s still an openclaw/ directory in the repo today, holding the deployable workspace.

That gives me my favorite line in the project, deploy/deploy-hog.sh:10:

SRC_WORKSPACE="/Users/dustinholden/Projects/repos/house-of-gizmo/openclaw/workspace/"

A script named hog deployed the directory named openclaw to the runtime root named /opt/hog, through a sudo wrapper named hog-deploy-hog. Every one of those names is correct in its own layer. Together they’re a puzzle.

What I got wrong

I treated this as one rename. It was three, and they have nothing in common except the word.

LayerChanged byVerified byStatus
Runtime path /opt/openclaw → /opt/hogA 65-line scriptThe service startingDone, 2026-02-07
Repo tree openclaw/ → hog/git mv in a 109-file commitThe buildDone, then reverted 2026-03-01
DocumentationHand editsNothingStill wrong

Only the first layer had a script. Only the first layer had a test. The service either came up against the new root or it didn’t. The third layer had neither, so it drifted, and nothing fails when it does.

A grep today finds /opt/openclaw in README.md:10 as the stated runtime location. Ten more times in docs/runbook.md. That includes the log paths I’d tail at 2am:

docs/runbook.md:302:| OpenClaw gateway stdout | `/opt/openclaw/logs/gateway.out.log` |

That file doesn’t exist. Neither does /opt/hog/. I checked the runtime host while editing this post, and /opt holds exactly one directory: homebrew.

The runtime went away and the docs didn’t

Checked on the host, 2026-09-27. No /opt/hog, no /opt/openclaw, nothing named hog in launchctl list, no hog-email-automation container. The repo is not dead. It took 11 commits in the last 30 days, most recently on 2026-09-16, all of them in other subprojects. The runtime payload is what stopped.

So 22 files now tell me where a runtime lives that doesn’t, and 6 more tell me the older wrong answer. The documentation layer drifted for seven months and then outlived the thing it was describing.

That four-line CI grep would have caught the February drift. A second check is just as cheap and I don’t have that one either: assert the documented path exists on the host. test -d /opt/hog is one line. It has been failing since I tore the runtime down, and I can’t tell you what day that was, because nothing recorded it.

What I’d do differently

Make the documentation layer fail loudly. The cheapest version is one grep in CI. Any occurrence of the retired path outside the migration script is an error. That’s about four lines. It’s the only part of this that would have kept working while I wasn’t looking. It’s also the part I skipped.

Second change is ordering. I renamed the repo tree and the runtime path in the same commit. That coupled a cosmetic change to an operational one. It made the 2026-03-01 reversal look riskier than it was. The runtime path was a decision. The directory name was a preference, and preferences should move on their own commits.

One more thing. deploy/deploy-hog.sh hard-codes an absolute path to my laptop’s checkout. It only runs from one machine. That’s fine for a one-operator system. It’s the kind of fine that stops without warning.