Skip to content

Instantly share code, notes, and snippets.

@zakelfassi
Last active February 22, 2026 05:08
Show Gist options
  • Select an option

  • Save zakelfassi/94f7a172ca323acee41dfdab9f7c528d to your computer and use it in GitHub Desktop.

Select an option

Save zakelfassi/94f7a172ca323acee41dfdab9f7c528d to your computer and use it in GitHub Desktop.
openclaw-update skill: gated upgrade workflow with pre-flight, backup, verify, and version pinning
#!/usr/bin/env bash
# pin-version.sh — record a known-good version as the intentional freeze point
# Usage: pin-version.sh [version] (defaults to current installed)
set -euo pipefail
PINNED_FILE=~/.openclaw/PINNED_VERSION
VERSION="${1:-$(openclaw --version 2>/dev/null || echo "unknown")}"
echo "$VERSION" > "$PINNED_FILE"
echo "Pinned: $VERSION$PINNED_FILE"
echo "Pre-flight checks will warn before upgrading past this."
echo "To unpin: rm $PINNED_FILE"
#!/usr/bin/env bash
# post-verify.sh — verify openclaw state after update
set -euo pipefail
PASS=0; FAIL=0
ok() { echo "$1"; PASS=$((PASS+1)); }
warn() { echo "⚠️ $1"; FAIL=$((FAIL+1)); }
echo "=== OpenClaw Post-Update Verify ==="
echo ""
# 1. Version
POST_VER=$(openclaw --version 2>/dev/null || echo "unknown")
LATEST_BACKUP=$(ls -1dt ~/.openclaw/backups/*/ 2>/dev/null | head -n1 || true)
PRE_VER=$([ -f "$LATEST_BACKUP/.pre-version" ] && cat "$LATEST_BACKUP/.pre-version" || echo "unknown")
echo "Version: $PRE_VER$POST_VER"
[ "$POST_VER" != "$PRE_VER" ] && ok "Version bumped" || warn "Version unchanged (update may not have applied)"
echo ""
# 2. Gateway health
echo "--- Gateway health ---"
openclaw health 2>/dev/null && ok "Gateway healthy" || warn "Gateway health check failed"
echo ""
# 3. Model chain
echo "--- Model chain ---"
python3 - <<'PY'
import json, pathlib, sys
try:
obj = json.loads((pathlib.Path.home()/'.openclaw/openclaw.json').read_text())
d = obj['agents']['defaults']['model']
primary = d.get('primary','?')
fallbacks = d.get('fallbacks',[])
print(f" primary: {primary}")
print(f" fallbacks: {fallbacks}")
# Warn if claude-sonnet-4-6 not first fallback (intentional config)
if fallbacks and 'claude-sonnet-4-6' not in fallbacks[0]:
print(" ⚠️ claude-sonnet-4-6 is not first fallback — check if migration reordered it")
else:
print(" ✅ Fallback chain intact (claude-sonnet-4-6 first)")
except Exception as e:
print(f" ⚠️ Could not read model chain: {e}", file=sys.stderr)
PY
echo ""
# 4. Device token scopes (known issue: 2026.2.19+ drops operator.write/read on rotate)
echo "--- Device token scopes ---"
openclaw devices list --json 2>/dev/null | python3 - <<'PY'
import json, sys, re
raw = sys.stdin.read()
m = re.search(r'\{.*\}', raw, re.DOTALL)
if not m:
print(" ⚠️ Could not parse devices list")
sys.exit(0)
data = json.loads(m.group())
for d in data.get('paired', []):
dev_scopes = set(d.get('scopes', []))
for t in d.get('tokens', []):
tok_scopes = set(t.get('scopes', []))
missing = dev_scopes - tok_scopes
if missing:
print(f" ⚠️ Token missing scopes: {missing}")
print(f" Fix: openclaw devices rotate --device {d['deviceId']} --role operator \\")
print(f" --scope operator.admin --scope operator.approvals --scope operator.pairing \\")
print(f" --scope operator.write --scope operator.read")
else:
print(f" ✅ Token scopes complete ({len(tok_scopes)} scopes)")
PY
echo ""
# 5. Custom daemon services (was #4)
echo "--- Daemon services ---"
for svc in dreamer.service forgeloop-paneforge.service forgeloop-gablus.service; do
STATE=$(systemctl --user is-active "$svc" 2>/dev/null || echo "unknown")
[ "$STATE" = "active" ] && ok "$svc active" || warn "$svc: $STATE"
done
echo ""
# 6. WORKFLOW_AUTO.md sentinels (missing = boot-md loop on every startup)
echo "--- WORKFLOW_AUTO.md sentinels ---"
declare -A _AWS=(
["noth"]="$HOME/clawd"
["tac"]="$HOME/tac-monorepo"
["tac-support"]="$HOME/clawd/workspaces/tac-support"
["fool"]="$HOME/.openclaw/workspace-fool"
["metamod"]="$HOME/.openclaw/workspace-metamod"
["codex"]="$HOME/.openclaw/workspace-codex"
["ip"]="$HOME/clawd/workspaces/ip"
["opus"]="$HOME/.openclaw/workspace-opus"
["flash"]="$HOME/.openclaw/workspace-flash"
["collab"]="$HOME/clawd/workspaces/collab"
)
_sentinel_ok=true
for _agent in "${!_AWS[@]}"; do
_ws="${_AWS[$_agent]}"
[ -d "$_ws" ] || continue
if [ ! -f "$_ws/WORKFLOW_AUTO.md" ]; then
warn "WORKFLOW_AUTO.md missing: $_agent ($_ws)"
_sentinel_ok=false
fi
done
$_sentinel_ok && ok "All WORKFLOW_AUTO.md sentinels present"
echo ""
# 7. Memory health (no readonly errors in today's log)
echo "--- Memory ---"
LOG=$(ls -1t /tmp/openclaw-*/openclaw-"$(date -u +%Y-%m-%d)".log 2>/dev/null | head -n1 || true)
if [ -n "$LOG" ]; then
COUNT=$(grep -c "readonly database" "$LOG" || true)
[ "$COUNT" -eq 0 ] && ok "No memory readonly errors" || warn "$COUNT readonly database errors in log"
else
echo " (no log for today yet)"
fi
echo ""
echo "=== Result: $PASS passed, $FAIL warnings ==="
[ "$FAIL" -gt 0 ] && echo "Review warnings above before confirming update success."
#!/usr/bin/env bash
# pre-flight.sh — evaluate whether updating OpenClaw is worth it RIGHT NOW
# Outputs: version delta, release notes summary, open critical bugs, recommendation
set -euo pipefail
CURRENT=$(openclaw --version 2>/dev/null || echo "unknown")
LATEST=$(npm view openclaw version 2>/dev/null || echo "unknown")
PINNED_FILE=~/.openclaw/PINNED_VERSION
echo "╔══════════════════════════════════════════════╗"
echo "║ OpenClaw Upgrade Pre-Flight Check ║"
echo "╚══════════════════════════════════════════════╝"
echo ""
echo "Current: $CURRENT"
echo "Latest: $LATEST"
if [ -f "$PINNED_FILE" ]; then
echo "Pinned: $(cat "$PINNED_FILE") (intentional freeze)"
fi
echo ""
# --- Already current? ---
if [ "$CURRENT" = "$LATEST" ]; then
echo "✅ Already on latest. No update available."
echo ""
echo "RECOMMENDATION: SKIP — nothing to update."
exit 0
fi
echo "Delta: $CURRENT$LATEST"
echo ""
# --- Release notes ---
echo "══ Recent Releases ══════════════════════════════"
gh release list --repo openclaw/openclaw --limit 5 2>/dev/null \
|| echo " (gh unavailable — check: https://github.com/openclaw/openclaw/releases)"
echo ""
# --- Release body for latest ---
echo "══ Latest Release Notes ═════════════════════════"
gh release view "$LATEST" --repo openclaw/openclaw --json body -q .body 2>/dev/null \
| head -n 40 \
|| echo " (unavailable via gh)"
echo ""
# --- Open critical bugs ---
echo "══ Open Bug Reports (recent) ════════════════════"
gh issue list \
--repo openclaw/openclaw \
--label bug \
--state open \
--limit 15 \
--json number,title,createdAt,comments \
--jq '.[] | "#\(.number) [\(.createdAt[:10])] \(.title) (\(.comments) comments)"' \
2>/dev/null \
|| echo " (gh unavailable — check: https://github.com/openclaw/openclaw/issues?q=is:open+label:bug)"
echo ""
# --- Known issues in current release window ---
echo "══ Issues mentioning this version ═══════════════"
gh issue list \
--repo openclaw/openclaw \
--state open \
--search "$LATEST" \
--limit 5 \
--json number,title \
--jq '.[] | "#\(.number) \(.title)"' \
2>/dev/null \
|| echo " (unavailable)"
echo ""
# --- Recommendation scaffold (agent fills in reasoning) ---
echo "══ Decision Framework ═══════════════════════════"
echo ""
echo " 1. Does latest fix a bug you're currently hitting? → if YES: lean toward updating"
echo " 2. Are there critical open bugs on latest? → if YES: lean toward waiting"
echo " 3. Is there a feature you actually need? → if YES: lean toward updating"
echo " 4. Are you stable right now? → if YES: default is STAY PUT"
echo ""
echo "Stable version pinning is a feature, not a failure."
echo "If you update: run pre-update.sh → gateway update → post-verify.sh"
echo "If you pin: echo '$LATEST' > $PINNED_FILE (or keep current pin)"
echo ""
echo "══ AGENT: read above, summarize findings, and ask the user before proceeding ══"
#!/usr/bin/env bash
# pre-update.sh — backup config + capture pre-version before openclaw update
set -euo pipefail
TS=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=~/.openclaw/backups/$TS
mkdir -p "$BACKUP_DIR"
echo "=== OpenClaw Pre-Update ==="
PRE_VER=$(openclaw --version 2>/dev/null || echo "unknown")
echo "Version: $PRE_VER"
echo "$PRE_VER" > "$BACKUP_DIR/.pre-version"
echo ""
echo "Backing up to: $BACKUP_DIR"
# Core config
cp ~/.openclaw/openclaw.json "$BACKUP_DIR/openclaw.json" && echo "✓ openclaw.json"
[ -f ~/.clawdbot/clawdbot.json ] && cp ~/.clawdbot/clawdbot.json "$BACKUP_DIR/clawdbot.json" && echo "✓ clawdbot.json"
# Auth profiles (scan known locations)
for f in \
~/.openclaw/auth-profiles.json \
~/.openclaw/agents/noth/agent/auth-profiles.json \
~/.openclaw/agents/main/agent/auth-profiles.json; do
if [ -f "$f" ]; then
LABEL=$(basename "$(dirname "$f")")
cp "$f" "$BACKUP_DIR/auth-profiles--$LABEL.json" && echo "$f"
fi
done
# Custom systemd unit files
mkdir -p "$BACKUP_DIR/systemd"
for f in ~/.config/systemd/user/dreamer.service \
~/.config/systemd/user/forgeloop-paneforge.service \
~/.config/systemd/user/forgeloop-gablus.service; do
[ -f "$f" ] && cp "$f" "$BACKUP_DIR/systemd/" && echo "$(basename "$f")"
done
echo ""
echo "Backup complete: $BACKUP_DIR"
ls -lh "$BACKUP_DIR"
# --- WORKFLOW_AUTO.md sentinel check (bug: hooks/boot-md loops if missing) ---
# Workspace paths keyed by agent — update if agents/workspaces change
echo ""
echo "=== Ensuring WORKFLOW_AUTO.md sentinels ==="
TODAY=$(date +%Y-%m-%d)
declare -A AGENT_WORKSPACES=(
["noth"]="$HOME/clawd"
["tac"]="$HOME/tac-monorepo"
["tac-support"]="$HOME/clawd/workspaces/tac-support"
["fool"]="$HOME/.openclaw/workspace-fool"
["metamod"]="$HOME/.openclaw/workspace-metamod"
["codex"]="$HOME/.openclaw/workspace-codex"
["ip"]="$HOME/clawd/workspaces/ip"
["opus"]="$HOME/.openclaw/workspace-opus"
["flash"]="$HOME/.openclaw/workspace-flash"
["collab"]="$HOME/clawd/workspaces/collab"
)
for agent in "${!AGENT_WORKSPACES[@]}"; do
WS="${AGENT_WORKSPACES[$agent]}"
[ -d "$WS" ] || continue
WFMD="$WS/WORKFLOW_AUTO.md"
if [ ! -f "$WFMD" ]; then
cat > "$WFMD" <<EOF
# WORKFLOW_AUTO.md
Purpose: post-compaction audit sentinel + minimal continuity pointer.
## After Compaction
- Read today's memory/${TODAY}.md (and yesterday if needed).
- Read memory/attention.md if it exists.
- Continue current work without re-introducing yourself.
EOF
echo " created: $agent ($WS)"
fi
# Also ensure today's memory file exists if the workspace has a memory dir
[ -d "$WS/memory" ] && [ ! -f "$WS/memory/$TODAY.md" ] && \
echo "# Memory — $TODAY" > "$WS/memory/$TODAY.md" && \
echo " created: $agent memory/$TODAY.md"
done
echo " sentinel check complete"
# --- end sentinel check ---
echo ""
echo "Ready to update. Run gateway update or: openclaw update"
echo "Note: gateway restart will drop exec session — that is expected."
name description
openclaw-update
Safe, deliberate OpenClaw upgrade workflow with pre-flight evaluation, backup, update, and post-verification. Use when the user asks to update or upgrade OpenClaw, says a new release dropped, wants to know if they should update, or asks about version pinning. This skill is the canonical upgrade path: it evaluates whether updating is worth it before touching anything.

OpenClaw Update

This skill is the full upgrade path — from "should I?" to "done, verified." Never run openclaw update raw. Always go through this workflow.

The 4-step protocol

Step 0 — Pre-flight: should we even update?

bash ~/clawd/skills/openclaw-update/scripts/pre-flight.sh

Read the output carefully. Summarize for the user:

  • Current version → latest version
  • What changed (release notes)
  • Open critical bugs on the new version
  • Your recommendation: UPDATE / WAIT / PIN

Wait for explicit human approval before proceeding.

Decision framework:

  • Bug you're hitting is fixed → lean UPDATE
  • Critical open bugs on latest → lean WAIT
  • Stable right now, no needed features → default STAY PUT
  • Version pinned intentionally → require override

If the answer is WAIT or STAY PUT: run pin-version.sh to record the freeze and stop here.

bash ~/clawd/skills/openclaw-update/scripts/pin-version.sh

Step 1 — Pre-update: backup + sentinel check

Only run this after explicit human approval to proceed.

bash ~/clawd/skills/openclaw-update/scripts/pre-update.sh

Creates a timestamped backup of: openclaw.json, auth profiles, systemd unit files. Ensures WORKFLOW_AUTO.md sentinels exist in all agent workspaces.

Step 2 — Run the update

Use the gateway tool — do NOT use exec for this step:

gateway.action = update.run
note = "OpenClaw updated. Running post-verify now."

The gateway will restart. The session will drop. That is expected.

Step 3 — Post-verify: confirm everything survived

After the gateway pings back:

bash ~/clawd/skills/openclaw-update/scripts/post-verify.sh

Checks (in order):

  1. Version actually bumped
  2. Gateway health (Telegram, WhatsApp)
  3. Model fallback chain (claude-sonnet-4-6 should be first fallback — rate limit reason)
  4. Device token scopes (known bug: 2026.2.19+ drops operator.write/read — auto-detects + prints fix)
  5. Daemon services (dreamer, forgeloop-paneforge, forgeloop-gablus)
  6. WORKFLOW_AUTO.md sentinels (missing = boot-md loop on every startup)
  7. Memory readonly errors

Known post-update manual fixes

If post-verify flags device token scope issues:

openclaw devices rotate \
  --device <id> --role operator \
  --scope operator.admin --scope operator.approvals --scope operator.pairing \
  --scope operator.write --scope operator.read

Rollback

LATEST=$(ls -1dt ~/.openclaw/backups/*/ | head -n1)
cp "$LATEST/openclaw.json" ~/.openclaw/openclaw.json
# Restore prior version via npm if needed:
# npm install -g openclaw@<prior-version>
systemctl --user restart openclaw-gateway.service

Version pinning

To freeze on a known-good version:

bash ~/clawd/skills/openclaw-update/scripts/pin-version.sh
# Stores current version in ~/.openclaw/PINNED_VERSION
# Pre-flight will warn before upgrading past this point

To unpin: rm ~/.openclaw/PINNED_VERSION

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment