Skip to content

fix: avoid config rewrites for plugin record updates#100005

Closed
qingminglong wants to merge 6 commits into
openclaw:mainfrom
qingminglong:codex/98419-plugin-update-records
Closed

fix: avoid config rewrites for plugin record updates#100005
qingminglong wants to merge 6 commits into
openclaw:mainfrom
qingminglong:codex/98419-plugin-update-records

Conversation

@qingminglong

@qingminglong qingminglong commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #98419

What Problem This Solves

Fixes an issue where users following gateway status --deep plugin drift hints after a core upgrade could have openclaw plugins update <exact official npm spec> fail at the config write step even though the only durable change was an installed-plugin index record.

The reported Windows upgrade path downloaded @openclaw/brave-plugin@2026.6.11, then attempted to replace a larger user-authored openclaw.json with a smaller projected config, triggering the size-drop guard and leaving official plugin drift unresolved.

This branch carries the records-only update fix onto current upstream/main and keeps the scope to install-record-only plugin updates. It does not change legacy plugins.installs cleanup, hook updates, or plugin id migration paths that genuinely mutate authored config.

Why This Change Was Made

plugins update now compares the projected next source config with the mutation-start source config after plugin install records have been stripped. If the source config is unchanged, the command commits only the persisted plugin install index and skips replaceConfigFile(...) entirely. Existing config-writing behavior remains in place when legacy plugin install records need to be removed from config or when hook/config state actually changes.

The records-only path preserves the safety barriers from the config-writing path: before the records-only commit is allowed to complete, it rereads the writable config snapshot and rejects stale root config hashes, config path changes, included-file target changes, included-file hash changes, and plugin-aware config validation failures after the new install record is visible. The check runs through the existing install-record commit rollback path, so a stale config race or incompatible plugin metadata restores the previous install index.

Because records-only updates intentionally preserve the authored config bytes, the command touches the config file mtime after a successful records-only commit so external running gateways still receive a watcher event. The gateway reload path clears the install-record cache before reload reads and does not discard same-hash watcher events until it has checked whether persisted plugin install records changed.

User Impact

Operators can repair official plugin version drift after upgrading OpenClaw without rewriting or shrinking their existing openclaw.json when no config mutation is needed. Config size-drop protection still protects real risky writes, concurrent config edits fail closed, plugin updates that would make existing plugin config invalid roll back the install index, and running gateways see a restart-triggering plugin-index change instead of staying on the old plugin source.

Evidence

Current head proved: b1eedaa71a4a71647e903bd16de38c29f37f5c2a.

Claim proved:

  • A real local CLI run on this PR head installed the official beta Brave plugin record and then ran openclaw plugins update @openclaw/brave-plugin@2026.6.11.
  • The update succeeded and advanced the durable install record from 2026.6.11-beta.2 to 2026.6.11.
  • The authored config file hash and size stayed unchanged during the update, and the config audit log showed zero config-write events for the update command.

Before evidence from the linked issue shows the Windows upgrade failure:

$ openclaw --version
OpenClaw 2026.6.11 (e085fa1)

$ openclaw gateway status --deep
Plugin version drift: 4 active official plugins not on gateway 2026.6.11
- brave: 2026.6.11-beta.2 (npm) -> expected 2026.6.11
- discord: 2026.6.11-beta.2 (npm) -> expected 2026.6.11
- qqbot: 2026.6.11-beta.2 (npm) -> expected 2026.6.11
- whatsapp: 2026.6.11-beta.2 (npm) -> expected 2026.6.11

$ openclaw plugins update @openclaw/brave-plugin@2026.6.11
Installed @openclaw/brave-plugin@2026.6.11 into <managed npm project>
Config write rejected: C:\Users\Administrator\.openclaw\openclaw.json (size-drop:40696->14568).
[openclaw] Could not start the CLI.

After-fix real CLI proof on this PR head, with local paths redacted:

$env:OPENCLAW_HOME=<isolated proof home>
$env:OPENCLAW_STATE_DIR=<isolated proof state>
$env:OPENCLAW_CONFIG_PATH=<isolated proof state>/openclaw.json

$ node --import tsx src/entry.ts plugins install npm:@openclaw/brave-plugin@2026.6.11-beta.2 --pin
Installing @openclaw/brave-plugin@2026.6.11-beta.2 into <managed npm project>
Linked peerDependency "openclaw" -> <repo checkout>
Plugin manifest id "brave" differs from npm package name "@openclaw/brave-plugin"; using manifest id as the config key.
Pinned npm install record to @openclaw/brave-plugin@2026.6.11-beta.2.
Installed plugin: brave
Restart the gateway to load plugins.

$ node --import tsx src/entry.ts plugins update @openclaw/brave-plugin@2026.6.11
Installing @openclaw/brave-plugin@2026.6.11 into <managed npm project generation>
Linked peerDependency "openclaw" -> <repo checkout>
Plugin manifest id "brave" differs from npm package name "@openclaw/brave-plugin"; using manifest id as the config key.
Updated brave: 2026.6.11-beta.2 -> 2026.6.11.
Restart the gateway to load plugins and hooks.

Config preservation proof for that same update:

config after install / before update:
  sha256 = 1eae281d3922c64477a17aee6ac434c30422b93d2c789007cb698c892f1bcdf5
  bytes  = 448

config after update:
  sha256 = 1eae281d3922c64477a17aee6ac434c30422b93d2c789007cb698c892f1bcdf5
  bytes  = 448

config-audit update write events: 0

Persisted install-record proof after the update:

{
  "source": "npm",
  "spec": "@openclaw/brave-plugin@2026.6.11",
  "version": "2026.6.11",
  "resolvedName": "@openclaw/brave-plugin",
  "resolvedVersion": "2026.6.11",
  "resolvedSpec": "@openclaw/brave-plugin@2026.6.11",
  "integrity": "sha512-FclcOH2g4E4Lt9fEViJgnjDRG4JVnG9yODjalj0/+qtMdCSKDWxlkrpS5kakDGzwhqy9+CiioczZQW0AtoWdOA=="
}

Source-level proof added here:

  • Regression coverage for plugins update brave where only the persisted install record changes:
    • writes the updated install record,
    • does not call replaceConfigFile(...) / writeConfigFile(...),
    • refreshes the plugin registry with the unchanged source config.
  • Root-config freshness coverage rejects stale root config hashes and restores previous install records.
  • Included-config freshness coverage rejects changed included config hashes and restores previous install records.
  • Plugin-aware validation coverage rejects invalid current plugin config after the new install record is visible and restores previous install records.
  • Config watcher coverage touches the unchanged config path after successful records-only updates.
  • Gateway reload coverage handles same-hash watcher events by checking fresh persisted plugin install records before deduping.
  • Existing legacy-record cleanup tests still expect config writes when plugins.installs must be removed from authored config.

Validation on this branch after merging current upstream/main:

node scripts/run-vitest.mjs src/cli/plugins-cli.update.test.ts --testNamePattern="plugins cli update"
# 1 file passed, 37 tests passed

node scripts/run-vitest.mjs src/gateway/config-reload.test.ts
# 4 files passed, 308 tests passed

git diff --check upstream/main...HEAD
# passed

node_modules\.bin\oxlint.cmd src/cli/plugins-update-command.ts src/cli/plugins-install-record-commit.ts src/cli/plugins-cli.update.test.ts src/gateway/config-reload.ts src/gateway/config-reload.test.ts
# passed

node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-plugin-update-records-pr98419-after-main.tsbuildinfo
# passed

python .agents\skills\autoreview\scripts\autoreview --mode branch --base upstream/main
# autoreview clean: no accepted/actionable findings reported

Current PR checks at the same head include Real behavior proof, check-lint, and check-test-types passing.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime cli CLI command changes size: L labels Jul 4, 2026
@clawsweeper

clawsweeper Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 6, 2026, 2:28 PM ET / 18:28 UTC.

Summary
The PR adds a records-only plugin install-index commit path for plugins update, config freshness and validation rollback checks, and gateway reload handling for same-hash plugin-index changes.

PR surface: Source +125, Tests +367. Total +492 across 5 files.

Reproducibility: yes. at source level: current main routes changed plugin install records through commitPluginInstallRecordsWithConfig and then replaceConfigFile, while the linked issue and PR body provide the real Windows size-drop failure log. I did not run the Windows upgrade path in this read-only review.

Review metrics: 1 noteworthy metric.

  • Config persistence and reload boundary: 1 records-only commit path added; 1 same-hash reload dedupe path changed. Both changes affect how upgraded users persist plugin records and how running gateways notice plugin-index-only updates before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98419
Summary: The linked issue is the canonical report for install-record-only plugin updates rewriting config; this PR and the earlier candidate PR are both candidate fixes for that root cause.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

Risk before merge

  • [P1] This intentionally changes a compatibility-sensitive config persistence path: install-record-only updates can now skip config replacement when equality, freshness, validation, rollback, and watcher checks pass.
  • [P1] Two open candidate PRs target the same canonical issue, so maintainers should choose one landing path and close or supersede the other after preserving attribution and proof.
  • [P1] The running-gateway wake-up path is covered by focused gateway reload tests, but the PR body does not show a live packaged gateway process observing the plugin-index-only change.

Maintainer options:

  1. Accept the records-only persistence boundary (recommended)
    Proceed with this PR after maintainer review confirms the equality gate, freshness checks, validation rollback, and watcher behavior are the intended upgrade-safe contract.
  2. Consolidate duplicate candidate branches
    Choose either this current-main port or fix: plugins update rewrites config for install-record-only updates #98422 as the landing path, then close or supersede the other with attribution and proof preserved.
  3. Pause for packaged gateway proof
    Request one packaged upgrade or running-gateway proof before merge if maintainers want direct process-level evidence for the watcher path.

Next step before merge

  • [P2] Manual review is appropriate because the remaining action is maintainer acceptance of a compatibility-sensitive config boundary and consolidation with the earlier candidate PR, not an automated code repair.

Maintainer decision needed

  • Question: Should maintainers land this current-main port, land fix: plugins update rewrites config for install-record-only updates #98422 instead, or request stronger packaged gateway proof before choosing the landing path?
  • Rationale: The code path is compatibility-sensitive and there are two open proof-positive candidate PRs for the same canonical issue, so automation should not pick the final landing branch unilaterally.
  • Likely owner: vincentkoc — Recent adjacent plugin recovery and gateway reload work makes this the best available routing signal for the compatibility boundary decision.
  • Options:
    • Land this current-main port (recommended): Use this PR as the landing path if maintainers accept the equality gate, freshness checks, validation rollback, and watcher behavior as the intended upgrade-safe contract.
    • Land the earlier candidate PR: Use fix: plugins update rewrites config for install-record-only updates #98422 instead if maintainers prefer the original branch and then close this port as superseded with attribution preserved.
    • Request packaged gateway proof: Ask for one live packaged gateway proof only if the focused reload tests and CLI proof are not enough for the watcher-side confidence bar.

Security
Cleared: The diff changes TypeScript CLI/gateway config persistence code and tests, with no dependency, workflow, package metadata, secret handling, or external code execution changes.

Review details

Best possible solution:

Land this current-main-compatible fix or an equivalent consolidated branch after maintainer acceptance of the records-only persistence boundary, then close the linked issue and supersede the other candidate PR.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: current main routes changed plugin install records through commitPluginInstallRecordsWithConfig and then replaceConfigFile, while the linked issue and PR body provide the real Windows size-drop failure log. I did not run the Windows upgrade path in this read-only review.

Is this the best way to solve the issue?

Yes. The narrow maintainable fix is to skip openclaw.json replacement only when the projected source config is unchanged, while preserving rollback, stale-config rejection, plugin-aware validation, and gateway reload signaling.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f859d78b6126.

Label changes

Label justifications:

  • P2: The PR fixes a bounded CLI plugin update failure that blocks official plugin drift repair without making the core gateway unusable.
  • merge-risk: 🚨 compatibility: The diff changes config persistence and reload behavior on a plugin upgrade path that existing user configs and running gateways depend on.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body provides redacted terminal proof for the real CLI update path, including install-record advancement, unchanged config hash/size, and zero update config-write audit events.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides redacted terminal proof for the real CLI update path, including install-record advancement, unchanged config hash/size, and zero update config-write audit events.
Evidence reviewed

PR surface:

Source +125, Tests +367. Total +492 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 3 138 13 +125
Tests 2 374 7 +367
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 512 20 +492

What I checked:

  • Root policy applied: Root AGENTS.md was read fully; its compatibility guidance applies because this PR changes plugin install-record persistence, config loading, and gateway reload behavior. (AGENTS.md:28, f859d78b6126)
  • Scoped gateway and plugin policy applied: Scoped gateway and plugin AGENTS files were read; gateway hot reload and plugin metadata cache boundaries are relevant to the changed code. (src/gateway/AGENTS.md:1, f859d78b6126)
  • Current main still rewrites config for changed install records: Current main calls commitPluginInstallRecordsWithConfig whenever the plugin install index should persist, so install-record-only updates still enter the config replacement path. (src/cli/plugins-update-command.ts:349, f859d78b6126)
  • Current config-writing callee: commitPluginInstallRecordsWithConfig delegates to replaceConfigFile, matching the linked issue's size-drop failure path after a plugin install record update. (src/cli/plugins-install-record-commit.ts:337, f859d78b6126)
  • PR records-only boundary: The merge ref gates the records-only path on equality with the mutation-start source config, then verifies freshness and plugin-aware validation before completing the install-record commit. (src/cli/plugins-update-command.ts:127, 7b7060a2bf25)
  • PR rollback helper reuse: The records-only helper reuses the existing install-record writer and rollback path while replacing the config-write commit callback with freshness verification. (src/cli/plugins-install-record-commit.ts:349, 7b7060a2bf25)

Likely related people:

  • ooiuuii: Authored the substantive records-only update commits in this PR and authored recent merged managed npm plugin update lifecycle work. (role: candidate fix owner and recent plugin update contributor; confidence: high; commits: 288b83e94648, 4fb7ec626870, e007cb6f5c36; files: src/cli/plugins-update-command.ts, src/cli/plugins-install-record-commit.ts, src/cli/plugins-cli.update.test.ts)
  • vincentkoc: Recent history shows official plugin recovery hardening and adjacent gateway/plugin update maintenance in the same surfaces. (role: recent adjacent contributor; confidence: medium; commits: 767e8280ac7c, e879a67bf728; files: src/cli/plugins-update-command.ts, src/gateway/config-reload.ts)
  • steipete: History shows plugin management hot-reload and config migration hardening work in the affected CLI/gateway persistence area. (role: feature-history contributor; confidence: medium; commits: d678bcfcc7d0, de5971eedc2c, 381c5e0762d1; files: src/cli/plugins-update-command.ts, src/cli/plugins-install-record-commit.ts, src/gateway/config-reload.ts)
  • shakkernerd: Earlier commits introduced and consolidated plugin install-index rollback and pending-record persistence behavior reused by this PR. (role: install-record rollback history contributor; confidence: medium; commits: be1d656514aa, c19f8a522310, 921ffad7c7a6; files: src/cli/plugins-install-record-commit.ts, src/cli/plugins-update-command.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-04T15:24:16.928Z sha b1eedaa :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-04T15:29:37.044Z sha b1eedaa :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-04T16:21:02.753Z sha b1eedaa :: needs maintainer review before merge. :: none
  • reviewed 2026-07-04T16:27:03.906Z sha b1eedaa :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 4, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

I updated the PR body Evidence for current head b1eedaa71a4a71647e903bd16de38c29f37f5c2a with redacted real CLI output for openclaw plugins update @openclaw/brave-plugin@2026.6.11, config hash/size preservation, zero update config-write audit events, and the persisted brave install record at 2026.6.11.

@clawsweeper

clawsweeper Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 🔁 re-review loop A fresh ClawSweeper review was explicitly requested after the latest review. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. status: 🔁 re-review loop A fresh ClawSweeper review was explicitly requested after the latest review. labels Jul 4, 2026
@clawsweeper clawsweeper Bot added the status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. label Jul 4, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper automerge

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

Labels

cli CLI command changes gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugins update rewrites config for install-record-only updates

2 participants