Skip to content

fix(update): recover from OpenClaw size-drop rejection#170

Merged
1bcMax merged 6 commits into
BlockRunAI:mainfrom
0xCheetah1:fix/update-size-drop-recovery
May 24, 2026
Merged

fix(update): recover from OpenClaw size-drop rejection#170
1bcMax merged 6 commits into
BlockRunAI:mainfrom
0xCheetah1:fix/update-size-drop-recovery

Conversation

@0xCheetah1
Copy link
Copy Markdown
Contributor

@0xCheetah1 0xCheetah1 commented May 23, 2026

Summary

  • treat OpenClaw config size-drop rejections during plugin install as recoverable
  • when the rejected payload is validated as the expected BlockRun model-list trim, apply only that scoped model-list update to the active config
  • continue with the direct npm install fallback and keep rollback active until the fallback path has successfully installed ClawRouter

Context

Older ClawRouter versions wrote the full BlockRun model catalog into openclaw.json. Newer versions trim that provider model list to the curated visible picker set, so upgrades from older installs can legitimately shrink the config by a large amount. OpenClaw correctly rejects that as a potential data-loss size drop, but the updater should not roll back the whole ClawRouter update in that recoverable case.

The scoped config update is only applied after validating that required top-level sections are still present and the size reduction is primarily from models.providers.blockrun.models. If validation fails, the updater preserves the existing config and still uses the npm fallback instead of forcing the rejected payload.

Testing

  • bash -n scripts/update.sh
  • tested patched updater on a VPS where OpenClaw rejected a 90728 -> 25514 config write; the update continued and installed ClawRouter v0.12.195 without rolling back
  • manually verified the rejected payload's shrink came from models.providers.blockrun.models changing from 175 models to 38 models

Summary by CodeRabbit

  • Bug Fixes

    • Improved plugin installation reliability with enhanced error detection, recoverable failure handling, and timeout resilience.
    • Added automatic recovery that trims and accepts safe plugin payloads to avoid unnecessary rollbacks and allow installs to proceed.
  • Chores

    • Refined configuration validation, conditional rollback clearing, and a fallback install/verification flow for partial failures.
  • Documentation

    • Reflowed and realigned README and multiple skill docs for clearer tables, examples, and endpoint listings.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 85dc7358-d851-4811-bbfa-7a98fcc290b4

📥 Commits

Reviewing files that changed from the base of the PR and between 9ffc569 and 4b693ae.

📒 Files selected for processing (1)
  • scripts/update.sh

📝 Walkthrough

Walkthrough

scripts/update.sh captures OpenClaw install output to a temp log, detects timeouts and "Config write rejected: ... size-drop:" failures, applies a Node-based scoped-model trim from the rejected payload when appropriate, sets OPENCLAW_INSTALL_RECOVERABLE to defer rollback, and—if needed—force-installs the plugin from npm and clears the trap afterward.

Changes

Install Resilience Improvements

Layer / File(s) Summary
Scoped model trim helper
scripts/update.sh
Adds apply_scoped_model_trim that parses active and rejected OpenClaw configs, validates invariants and byte-size/shrink constraints, overwrites only models.providers.blockrun.models with the rejected model array, and saves atomically (range_c43ea99f8125).
Install failure detection and handling
scripts/update.sh
Adds handle_openclaw_install_failure to treat timeout (exit 124) as recoverable in some cases, detect size-drop rejections in the install log, extract rejected payload path, invoke scoped trim, and set OPENCLAW_INSTALL_RECOVERABLE=1 (range_cc29971a72db).
Resilient install with log capture
scripts/update.sh
Runs openclaw plugins install with output captured to OPENCLAW_INSTALL_LOG (via tee/timeout), funnels failures through the handler, removes temp log, and conditionally clears rollback trap based on recoverable flag (range_5dc962e76e36).
Recoverable direct npm install and verification
scripts/update.sh
When OPENCLAW_INSTALL_RECOVERABLE=1 and plugin package.json is missing, resolves @blockrun/clawrouter@latest, force-installs from npm, reads installed version, and clears rollback trap (range_2023f5d63e07).

Documentation formatting (non-functional)

Layer / File(s) Summary
CHANGELOG / README edits
CHANGELOG.md, README.md
Small single-line and table spacing/formatting adjustments; content unchanged (range_561cb2b7bc3b, range_af1ba13d6814, range_7d65d9659e84, range_d94fc61e8a1a).
ClawRouter / Phone / Release SKILL docs
skills/clawrouter/SKILL.md, skills/phone/SKILL.md, skills/release/SKILL.md
Table and example formatting adjustments; semantics unchanged (range_f611a3485ce3, range_f89d5bac4038, range_76ce97ba9c31, range_185af0615382, range_af738bc583a6).
Surf SKILL endpoint catalog
skills/surf/SKILL.md
Reformatted numerous endpoint listings into consistent aligned tables; paths and required params unchanged (range_a1ac97a63eec … range_bd3a85effa1d).

Minor code formatting and tests (non-functional)

Layer / File(s) Summary
CLI phone output formatting
src/cli.ts
Reformatted multiline template literals for cmdPhone error/list/buy output; no semantic change (range_9a4ce39f9e3b, range_667ad36b6353, range_cef66d0a4ac2).
Index command / cr-call formatting
src/index.ts
Condensed token extraction and small formatting changes in partners and cr-call handlers; behavior preserved (range_49cec0340cdc, range_6d14a02b0ee5, range_917193993492, range_a718e59e6044).
Proxy and tests
src/proxy.surf-routing.test.ts, src/proxy.ts, test/integration/security-scanner.test.ts
Small assertion/assignment formatting and simplified test fallback typing; no behavioral changes (range_b80aef528856, range_4616d31b39ef, range_b7a10dde56b5, range_7c4a3ff7a1db).

Sequence Diagram(s)

sequenceDiagram
  participant UpdateScript as UpdateScript
  participant OpenClaw as openclaw
  participant LogFile as OPENCLAW_INSTALL_LOG
  participant NodeHelper as apply_scoped_model_trim
  participant Npm as npm
  participant Disk as Filesystem

  UpdateScript->>OpenClaw: run "openclaw plugins install --force `@blockrun/clawrouter`" (captured via timeout|tee -> OPENCLAW_INSTALL_LOG)
  OpenClaw->>LogFile: emit install output (may include "Config write rejected: ... size-drop:")
  UpdateScript->>NodeHelper: on size-drop, extract rejected payload path and invoke apply_scoped_model_trim
  NodeHelper->>Disk: read active openclaw.json and rejected payload
  NodeHelper->>NodeHelper: validate keys, sections, ordering, and byte/delta thresholds
  NodeHelper->>Disk: write updated openclaw.json (models.providers.blockrun.models)
  UpdateScript->>Npm: if recoverable and plugin missing, resolve version and force-install from npm
  Npm->>Disk: install package (package.json present)
  UpdateScript->>UpdateScript: clear rollback trap after recovery path
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • BlockRunAI/ClawRouter#112 — Also modifies scripts/update.sh behavior around openclaw.json blockrun model allowlist handling and reconciliation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding recovery logic for OpenClaw size-drop rejections during plugin installation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/update.sh`:
- Around line 456-468: The recoverable-install branch uses an elif so a
size-drop path can skip the npm fallback while still clearing the rollback trap;
change the logic so when OPENCLAW_INSTALL_RECOVERABLE=1 you always attempt the
npm fallback via force_install_from_npm and then only run the trap - EXIT INT
TERM after confirming INSTALLED_VER (from the node check of
$PLUGIN_DIR/package.json) indicates a successful install (not "?" or empty);
locate the block referencing OPENCLAW_INSTALL_RECOVERABLE,
force_install_from_npm, the node-based INSTALLED_VER check and the trap - EXIT
INT TERM and refactor them into an unconditional-if for the fallback plus a
guarded trap-clear that runs only on successful INSTALLED_VER.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c89363a-eeee-443b-87f8-1291fcb55a44

📥 Commits

Reviewing files that changed from the base of the PR and between 0cfadbf and 4fb07a5.

📒 Files selected for processing (1)
  • scripts/update.sh

Comment thread scripts/update.sh Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/update.sh`:
- Around line 421-425: The timeout handler that checks exit_code==124 should not
unconditionally treat the install as successful; change the block in
scripts/update.sh to detect whether the plugin actually completed (for example
by checking for the expected package.json or the installed plugin artifact)
before returning 0, and only clear/avoid setting OPENCLAW_INSTALL_RECOVERABLE
when that verification passes; if the verification fails, propagate failure (do
not return 0) so the npm fallback runs and rollback remains possible. Ensure you
reference exit_code, OPENCLAW_INSTALL_RECOVERABLE, and the presence of
package.json (or the specific install success indicator used elsewhere) when
implementing the check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ae61885-6151-416a-b326-2cedd4bf92da

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb07a5 and 8169f98.

📒 Files selected for processing (1)
  • scripts/update.sh

Comment thread scripts/update.sh
0xCheetah1 and others added 3 commits May 23, 2026 09:26
prettier reinterpreted the bare `*` chars in `/v1/phone/*, /v1/voice/*`
as italic markers and rewrote them to `_`. Escape with `\*` so the
literal asterisks survive future prettier passes.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/update.sh (1)

373-405: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Verify the rejected BlockRun list is a true shrink before applying it.

Lines 379-405 only check counts and byte deltas. They never prove that rejected.models.providers.blockrun.models is actually a subset of the active list, so a payload that swaps or edits model entries can still pass and overwrite the live config. Please require every rejected model to match an existing active model entry, and ideally preserve relative order, before Line 408 copies it over.

🔧 Minimal guard
 const activeModels = getBlockrunModels(active);
 const rejectedModels = getBlockrunModels(rejected);
 if (!Array.isArray(activeModels) || !Array.isArray(rejectedModels)) {
   fail('blockrun model list is missing or invalid');
 }
+
+const canonical = (model) => JSON.stringify(model);
+const activeSet = new Set(activeModels.map(canonical));
+for (const model of rejectedModels) {
+  if (!activeSet.has(canonical(model))) {
+    fail('rejected model list is not a pure shrink of the active list');
+  }
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/update.sh` around lines 373 - 405, Ensure the rejected BlockRun list
is a true shrink by verifying each entry in rejectedModels actually exists in
activeModels (and optionally in the same relative order) before accepting the
change: iterate rejectedModels (from rejected.models.providers.blockrun.models /
rejectedModels as returned by getBlockrunModels) and check each item matches an
entry in activeModels (or that the sequence of indexes in activeModels is
strictly increasing to preserve order); if any rejected model is not found or
ordering is violated, call fail with a clear message and abort instead of
proceeding to copy active.models -> rejected.models.
♻️ Duplicate comments (1)
scripts/update.sh (1)

557-578: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep rollback armed until the recoverable npm install is actually confirmed.

Because the generic package.json branch at Line 557 runs before the recoverable branch at Line 566, a size-drop recovery can skip force_install_from_npm entirely and still clear the rollback trap at Lines 576-578. That puts the script back in the state this PR is trying to avoid: rollback is disabled without a confirmed successful fallback install.

🔧 Safer control flow
-if [ -d "$PLUGIN_DIR" ] && [ -f "$PLUGIN_DIR/package.json" ]; then
+if [ "$OPENCLAW_INSTALL_RECOVERABLE" = "1" ]; then
+  LATEST_VER=$(npm view `@blockrun/clawrouter`@latest version 2>/dev/null || echo "")
+  if [ -z "$LATEST_VER" ]; then
+    echo "  ✗ Could not resolve latest ClawRouter version from npm"
+    exit 1
+  fi
+  force_install_from_npm "$LATEST_VER"
+  INSTALLED_VER=$(node -e "try{const p=require('$PLUGIN_DIR/package.json');console.log(p.version);}catch{console.log('');}" 2>/dev/null || echo "")
+  if [ -z "$INSTALLED_VER" ]; then
+    echo "  ✗ Recoverable install did not produce a valid package.json"
+    exit 1
+  fi
+  echo "  ✓ ClawRouter v${INSTALLED_VER} installed"
+  trap - EXIT INT TERM
+elif [ -d "$PLUGIN_DIR" ] && [ -f "$PLUGIN_DIR/package.json" ]; then
   INSTALLED_VER=$(node -e "try{const p=require('$PLUGIN_DIR/package.json');console.log(p.version);}catch{console.log('');}" 2>/dev/null || echo "")
   LATEST_VER=$(npm view `@blockrun/clawrouter`@latest version 2>/dev/null || echo "")
   if [ -n "$LATEST_VER" ] && [ -n "$INSTALLED_VER" ] && [ "$INSTALLED_VER" != "$LATEST_VER" ]; then
     echo "  ⚠️  openclaw installed v${INSTALLED_VER} (cached) but latest is v${LATEST_VER}"
     force_install_from_npm "$LATEST_VER" || true
   fi
   INSTALLED_VER=$(node -e "try{const p=require('$PLUGIN_DIR/package.json');console.log(p.version);}catch{console.log('?');}" 2>/dev/null || echo "?")
   echo "  ✓ ClawRouter v${INSTALLED_VER} installed"
-fi
-if [ "$OPENCLAW_INSTALL_RECOVERABLE" = "1" ]; then
-  trap - EXIT INT TERM
 fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/update.sh` around lines 557 - 578, The current flow clears the
rollback trap unconditionally at the end, which can disable rollback when the
generic package.json branch ran but the recoverable npm fallback hasn't
completed; modify the control flow so that the trap (trap - EXIT INT TERM) is
only cleared after a confirmed successful recoverable install: keep
OPENCLAW_INSTALL_RECOVERABLE set while running force_install_from_npm and only
run trap - EXIT INT TERM after force_install_from_npm succeeds (and after
verifying INSTALLED_VER via node require), or clear the trap inside the branch
that runs when OPENCLAW_INSTALL_RECOVERABLE=1 but only after a successful
force_install_from_npm and verification; reference PLUGIN_DIR,
OPENCLAW_INSTALL_RECOVERABLE, force_install_from_npm, INSTALLED_VER and the trap
command to locate where to change the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/update.sh`:
- Around line 373-405: Ensure the rejected BlockRun list is a true shrink by
verifying each entry in rejectedModels actually exists in activeModels (and
optionally in the same relative order) before accepting the change: iterate
rejectedModels (from rejected.models.providers.blockrun.models / rejectedModels
as returned by getBlockrunModels) and check each item matches an entry in
activeModels (or that the sequence of indexes in activeModels is strictly
increasing to preserve order); if any rejected model is not found or ordering is
violated, call fail with a clear message and abort instead of proceeding to copy
active.models -> rejected.models.

---

Duplicate comments:
In `@scripts/update.sh`:
- Around line 557-578: The current flow clears the rollback trap unconditionally
at the end, which can disable rollback when the generic package.json branch ran
but the recoverable npm fallback hasn't completed; modify the control flow so
that the trap (trap - EXIT INT TERM) is only cleared after a confirmed
successful recoverable install: keep OPENCLAW_INSTALL_RECOVERABLE set while
running force_install_from_npm and only run trap - EXIT INT TERM after
force_install_from_npm succeeds (and after verifying INSTALLED_VER via node
require), or clear the trap inside the branch that runs when
OPENCLAW_INSTALL_RECOVERABLE=1 but only after a successful
force_install_from_npm and verification; reference PLUGIN_DIR,
OPENCLAW_INSTALL_RECOVERABLE, force_install_from_npm, INSTALLED_VER and the trap
command to locate where to change the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 645664cd-1967-43bc-8689-e154e0a9d432

📥 Commits

Reviewing files that changed from the base of the PR and between 85f23bd and 9ffc569.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • scripts/update.sh
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

@1bcMax 1bcMax merged commit 3cc7323 into BlockRunAI:main May 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant