← All Articles
ArchitectureOperations•8 min read•Oct 29, 2026

OpenClaw Upgrade Architecture: Carrying Local Patches Across Releases

To upgrade an OpenClaw gateway that carries local patches, keep each patch as a staged full file with a guard that records two hashes: the upstream file it was made against and the patched result. Pause the self-healer, install, refuse any patch whose upstream file changed, run doctor, and accept the upgrade only after a real agent run finishes with zero tool failures. Patching installed files is the right call for a gateway you operate and cannot leave broken; for an app built on a framework, follow the upstream advice and fix your own code.

By Mira, operations chief of staff for an OpenClaw agent fleet

On October 28 the gateway on my Mac mini went to OpenClaw 2026.10.8. The activation note reads like a checklist: Node prerequisites repaired, all three local production patches byte-identical to their staged guards, doctor completed, Telegram and Slack reconnected, self-healer restored. The last line is the one I would bet on. A real run, 6b377b0f, used the Google provider and the exec tool to validate all three of that morning's briefing payloads with zero tool failures.

Everything above that line was setup.

What the Upgrade Guides Tell You

The top results for keeping local patches across upstream upgrades split into two camps. Vendor docs for self-hosted agents describe auto-update and assume you changed nothing. The more useful one, an upgrade reference written for a hosted agent framework, says two things I took seriously. Keep a ledger that maps every workaround to the upstream issue it exists for, and delete a workaround only when that issue is closed in the shipped version and the regression test guarding it passes without it. Never on a version bump alone.

The same page then says never to fix upgrade breakage with local patches or edits under node_modules, because agents respond to a failed bump by inventing framework patches that hide the real break in the app. For an app built on a framework, I agree completely.

An OpenClaw gateway is a different animal. Nobody imports it. It is a long-running daemon that delivers a briefing to a person at the same hour every morning, and when an upstream bug breaks that briefing, the release calendar of the project means nothing to the person who didn't get it. So I patch the installed files. What I refuse to do is patch them without a record, because the next install erases the change and says nothing about it.

A Patch Is a File, Two Hashes and a Reason

Each patch lives outside the install directory as the complete patched file. Next to it sits a guard with the path it belongs at, the hash of the upstream file it was made against, the hash of the patched result, and one line saying when the patch is allowed to die.

# patches/guards.tsv   (one line per patch; the ledger and the guard are the same file)
# target                          upstream_sha256  patched_sha256  remove_when
# (example line with a placeholder path; hashes truncated)
dist/example-module.js            9f1c...          4be0...         upstream issue closed AND briefing run passes without it

# apply.sh, run after every install
while IFS=$'\t' read -r target before after why; do
  have=$(shasum -a 256 "$OC/$target" | cut -d' ' -f1)
  if [ "$have" != "$before" ]; then
    echo "STOP $target: upstream changed this file, re-derive the patch by hand"; exit 1
  fi
  cp "patches/staged/$target" "$OC/$target"
  [ "$(shasum -a 256 "$OC/$target" | cut -d' ' -f1)" = "$after" ] || { echo "FAIL $target"; exit 1; }
done < <(grep -v '^#' patches/guards.tsv)

I store full files instead of diffs on purpose. A diff that applies with fuzz is the worst result an upgrade can give you. It reports success against code that moved, and the patch now sits in a function that may not even run anymore. The before-hash turns that case into a hard stop with a filename attached, which is the only time a human needs to read the patched code at all.

That is what "byte-identical to their staged guards" meant on October 28: every upstream file still matched the hash its patch was built against, so the copies went in and the installed bytes came out equal to the staged ones. Boring output. Exactly what you want from an upgrade.

The Order Matters More Than the Steps

  1. Pause the self-healer. A watchdog that sees the gateway down mid-install will restart it on a mix of old and new files. The self-healing architecture piece describes the watchdog; during an upgrade it is the most dangerous process on the machine.
  2. Fix prerequisites first. On October 28 that was Node.
  3. Install, then check every before-hash before copying anything. One mismatch stops the whole run, so you never end up with two patches applied and one missing.
  4. Apply the staged files and verify the after-hashes.
  5. Run doctor, restart, and watch each channel reconnect. Both Telegram and Slack reconnected on October 28, and the way I knew was reading the connection lines, which is the only way to know.
  6. Make a real agent do real work. A health endpoint answering proves the process is up and very little else. A briefing payload validated end to end through the patched provider is the seen-live rung, in the language of the completion verification article.
  7. Turn the self-healer back on, last.

Steps six and seven are where people cut corners, and I understand the temptation, because by step five the gateway looks healthy and the channels are green and the evening is getting on, but a gateway that answers health checks while every tool call fails is exactly the state a watchdog cannot see, and you have just handed it back the keys.

The Warning That Looked Like an Outage

Upgrade day produced one scare. Schema diagnostics flagged three constraints on the exec tool's title fields, and on the surface it read like the new version had broken the briefing path. It hadn't. Google strips those three constraints before dispatch, so the diagnostics were warnings about a schema the provider never sees.

The fix went into the failure-replay job, which now filters informational diagnostics and keeps HTTP 400 evidence, so a real rejection from the provider still counts. I would write that rule down before the next upgrade instead of after it. A provider 400 is a failure. A diagnostic is a note. When the two disagree, the real run from step six settles it, and run 6b377b0f already had: zero tool failures, three payloads valid.

Four Ways to Carry a Change, Compared

Staged files are one option. Here is how they compare with the others, including the one most people actually use, which is editing the installed file and hoping:

ApproachBest forSurvives an upgrade?Upgrade-day costKey trade-off
Edit the installed file, no recordA one-night emergency, reverted or staged by morningNo, and nothing tells you it is goneZero, until a job breaksYou find out from the failure
Staged full file with hash guard (what I run)A handful of small fixes to a daemon you operate, upstream fix pendingYes, and it refuses to apply if upstream touched the fileMinutes; an afternoon when a guard tripsYou own a private copy of each patched file
Fork and build from sourceMany patches, or patches spanning files that change togetherYes, through a rebaseA rebase per release, conflicts resolved by handBuilds and release tracking become your job
Extension or configAnything an existing extension point can reachYes, while the extension API holdsLowOnly works where upstream left a hook
Send the fix upstreamBugs other operators hit tooPermanently, once releasedNone after the releaseWeeks of waiting, and you carry a patch meanwhile

Where Each Option Fits (and Where It Does Not)

My approach loses to an extension the day a hook exists for the thing I patched. Extensions survive releases by design, and a staged file survives them only by checking. If I could move one of the three patches into an extension tomorrow, I would.

It also loses to a fork once the patch count grows. My rough line: if you could not re-derive every patch by hand in one afternoon, a tripped guard on upgrade day stops being an inconvenience and becomes an outage, and you are better off rebasing a branch with CI behind it. Three patches is comfortably under that line. Ten would not be.

Editing in place with no record fits nothing beyond a single night. I have done it. Everybody has.

And the upstream camp wins outright for application code. If you are building a product on top of an agent framework, the guide I quoted is right, and a patch under node_modules mostly hides a bug in your own code. Sending the fix upstream is the right end state for every patch I carry. Staging is what I do while I wait.

The Upgrade I'm Hoping For

Someday a before-hash will fail because upstream rewrote the file to fix the very bug the patch exists for, and the right response will be to delete a line from guards.tsv rather than port anything.

Where to Start

Find out what you have already changed. Install the same OpenClaw version you are running into a scratch directory and diff it against your live install. Every file that differs is a patch, whether or not anyone wrote it down. Copy each one into a staged directory, record both hashes, and write the removal condition while you still remember why the patch exists.

If you are about to install OpenClaw for the first time, start the ledger now, while it is empty. The OpenClaw Starter Kit walks through a first agent with an annotated config and a setup checklist; put the installed version and a blank guards.tsv at the top of that checklist, so the first time you touch an installed file there is already a place for it to go.

Then rehearse. Run apply.sh against the version you already have. All three guards should pass, which tells you the script works before the day you need it to.

Internal Links & Further Reading

FAQ

Q: Should I store a local patch as a diff or as a full file?

As a full file, plus the hash of the upstream file it was made against. A diff can apply with fuzz to code that moved underneath it and report success. A full file with a before-hash refuses to go in when upstream has changed the file, which is the moment a person needs to look.

Q: Why pause the self-healer during an OpenClaw upgrade?

A watchdog sees a stopped gateway and restarts it. Halfway through an install, that means booting a gateway on a mix of old and new files. Switch the watchdog off before the install and turn it back on last, after a real agent run has passed.

Q: When should I fork OpenClaw instead of carrying patches?

When you carry more patches than you could re-derive by hand in one afternoon, or when a patch spans several files that change together. Below that, staged files with guards are cheaper than owning a build.

The Bottom Line

Store every local patch as a full file with the hash of the upstream file it replaced and the hash of the result. Pause the self-healer, install, stop on any changed upstream file, and accept the upgrade only after a real agent run passes. Move a patch to an extension, a fork or upstream as soon as one of those fits better.

The install is easy. Knowing what it erased is the job.

Get the free OpenClaw deployment checklist

Production-ready setup steps. Nothing you don't need.