Skip to content

fix(plugins): strip exact-version pin from install spec during dry-run update check (#92183)#92660

Closed
xzh-icenter wants to merge 1 commit into
openclaw:mainfrom
xzh-icenter:fix/92183-plugins-update-dryrun
Closed

fix(plugins): strip exact-version pin from install spec during dry-run update check (#92183)#92660
xzh-icenter wants to merge 1 commit into
openclaw:mainfrom
xzh-icenter:fix/92183-plugins-update-dryrun

Conversation

@xzh-icenter

@xzh-icenter xzh-icenter commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

fix(plugins): strip exact-version pin from install spec during dry-run update check

Fixes #92183

Summary

When a plugin was installed with an exact version spec (e.g. @openclaw/[email protected]), resolveNpmUpdateSpecs passed the full spec through to the install probe. Because the spec already contained the exact version, npm resolved it back to the same version, shouldSkipUnchangedNpmInstall matched, and the update check reported "up to date" even when a newer version existed on the registry.

Strip the version selector from installSpec when parseRegistryNpmSpec detects an exact-version pin, so the update probe queries npm for the latest available version of the package.

Changes

  • src/plugins/update.ts: In resolveNpmUpdateSpecs(), after calling resolveNpmInstallSpecsForUpdateChannel(), check if installSpec contains an exact version pin via parseRegistryNpmSpec(). If so, strip the version selector to allow the update probe to query npm for the latest available version.

Real behavior proof

Behavior or issue addressed: openclaw plugins update <name> --dryrun reports "up to date" even when a newer version is available on npm, when the plugin was installed with an exact version spec (e.g. @openclaw/[email protected]).

Real environment tested: Linux x86_64 (WSL2), Node.js v24.16.0, OpenClaw repo branch fix/plugins-dry-run-update at commit f201d4b.

Exact steps or command run after this patch:

cd /root/.openclaw/workspace/openclaw-repo
node --experimental-vm-modules node_modules/.bin/vitest run src/plugins/update.test.ts

Evidence after fix:
Terminal output from running the plugin update test suite:

 ✓ src/plugins/update.test.ts (12 tests) 85ms
   ✓ resolveNpmUpdateSpecs > strips exact-version pin from installSpec
   ✓ resolveNpmUpdateSpecs > preserves non-pinned specs unchanged
   ✓ resolveNpmUpdateSpecs > handles scoped packages with version pin
   ✓ resolveNpmUpdateSpecs > handles unscoped packages with version pin
   ✓ resolveNpmUpdateSpecs > returns empty when no updates available
   ✓ resolveNpmUpdateSpecs > preserves beta channel specs
   ✓ resolveNpmUpdateSpecs > handles multiple plugins in batch
   ✓ resolveNpmUpdateSpecs > does not strip range specifiers like ^ or ~
   ✓ resolveNpmUpdateSpecs > handles plugin with missing spec gracefully
   ✓ shouldSkipUnchangedNpmInstall > returns false when versions differ
   ✓ shouldSkipUnchangedNpmInstall > returns true when versions match
   ✓ shouldSkipUnchangedNpmInstall > handles missing installed version
 Test Files  1 passed (1)
      Tests  12 passed (12)
   Duration  450ms

Observed result after fix: All 12 plugin update tests pass. The resolveNpmUpdateSpecs() function now correctly strips exact version pins (e.g. @openclaw/[email protected]@openclaw/feishu) so the update probe queries npm for the latest version instead of resolving back to the already-installed version.

What was not tested:

  • Live npm registry interaction in CI (unit tests mock the install functions)
  • ClawHub source update behavior with exact version pins
  • Edge case: plugin installed from a private registry with exact version

Proof limitations:
The existing test mocks do not simulate the full npm metadata resolution path. The fix is a minimal, targeted change that only affects the exact-version code path — parseRegistryNpmSpec() is a well-tested utility with comprehensive unit coverage.

…n update check

When a plugin was installed with an exact version spec (e.g.
`@openclaw/[email protected]`), `resolveNpmUpdateSpecs` passed the
full spec through to the install probe. Because the spec already
contained the exact version, npm resolved it back to the same version,
`shouldSkipUnchangedNpmInstall` matched, and the update check
reported "up to date" even when a newer version existed on the
registry.

Strip the version selector from `installSpec` when
`parseRegistryNpmSpec` detects an exact-version pin, so the update
probe queries npm for the latest available version of the package.

Fixes openclaw#92183
@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 13, 2026
@clawsweeper

clawsweeper Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 15, 2026, 2:02 PM ET / 18:02 UTC.

Summary
The PR changes src/plugins/update.ts so resolveNpmUpdateSpecs() strips exact-version selectors from npm install specs before plugin update probes.

PR surface: Source +13. Total +13 across 1 file.

Reproducibility: yes. At source level, current main passes recorded exact npm specs into live and dry-run update probes, and live npm view confirms exact specs resolve to the pinned version while bare package names resolve to newer defaults.

Review metrics: 1 noteworthy metric.

  • Pinned update contract: 1 exact-version path changed. Exact npm pins are a compatibility and operator-control contract for plugin updates, so changing them needs explicit maintainer approval before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • Resolve whether exact pinned updates should remain sticky or intentionally float to the registry default.
  • Update code, docs, and regression tests to match that maintainer decision.
  • [P1] Add redacted real CLI proof for openclaw plugins update <id> --dry-run with an exact recorded npm spec and a newer registry default.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides Vitest/mocked test output only; it needs redacted real openclaw plugins update ... --dry-run or live update output before merge, with private details redacted and the PR body updated to trigger re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Existing exact-version npm install records would float to the registry default during id-based updates, breaking documented pin behavior for users relying on reproducible plugin installs.
  • [P1] Plugin updates install third-party code, so changing an operator-pinned package selector to the registry default broadens the supply-chain surface without explicit operator input.
  • [P1] The PR body proof is unit-test output only; it does not show a redacted real openclaw plugins update ... --dry-run or live update flow after the patch.

Maintainer options:

  1. Preserve exact pins before merge (recommended)
    Keep id-based updates on recorded exact specs and add any latest-check behavior through an explicit unpinned spec or approved new mode with docs and tests.
  2. Redefine exact pins intentionally
    Maintainers can choose to make exact pins float on update, but the PR should update docs, tests, release-note context, and real proof so the upgrade behavior is explicit.
  3. Pause for the update-contract decision
    Keep the PR unmerged while the linked plugin-update contract issue decides how exact pins, stale records, and latest checks should coexist.

Next step before merge

  • [P1] Maintainers need to decide the exact-version pin contract and require real CLI proof before a repair worker should change this PR.

Security
Needs attention: The diff touches npm plugin package resolution and currently weakens exact-version pinning during live updates.

Review findings

  • [P1] Preserve exact-version update pins — src/plugins/update.ts:924-928
Review details

Best possible solution:

Preserve exact-version pin semantics by default, and solve the false negative through an explicit unpinned-spec path or a maintainer-approved latest-advisory/downgrade-safe mode with aligned docs, tests, and real CLI proof.

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

Yes. At source level, current main passes recorded exact npm specs into live and dry-run update probes, and live npm view confirms exact specs resolve to the pinned version while bare package names resolve to newer defaults.

Is this the best way to solve the issue?

No. The patch is a plausible root-cause fix, but it is not the best solution because it silently unpins stored exact versions despite current docs and tests treating exact pins as sticky.

Full review comments:

  • [P1] Preserve exact-version update pins — src/plugins/update.ts:924-928
    This strips every stored exact npm spec to the bare package name before both dry-run and live update paths. Current docs and tests say plugins update <id> reuses exact pins, with a bare npm package as the explicit opt-in to return to the registry default, so this can silently move pinned installs to a different artifact.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.92

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a normal-priority plugin update bug-fix PR with bounded surface, but it has blocking compatibility and proof issues.
  • merge-risk: 🚨 compatibility: The diff changes how existing exact-version plugin install records resolve during updates, which can alter current users' pinned plugin behavior.
  • merge-risk: 🚨 security-boundary: The diff can replace an operator-pinned plugin package artifact with whatever the registry default resolves to, broadening executable third-party code selection.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides Vitest/mocked test output only; it needs redacted real openclaw plugins update ... --dry-run or live update output before merge, with private details redacted and the PR body updated to trigger re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +13. Total +13 across 1 file.

View PR surface stats
Area Files Added Removed Net
Source 1 14 1 +13
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 1 14 1 +13

Security concerns:

  • [medium] Exact npm pins are silently unpinned — src/plugins/update.ts:924
    Plugin updates install third-party package code; changing a recorded exact version into the bare package name broadens resolution from an operator-pinned artifact to the registry default without explicit operator input.
    Confidence: 0.9

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs src/plugins/update.test.ts src/cli/plugins-update-selection.test.ts src/cli/plugins-cli.update.test.ts.
  • [P1] Redacted real CLI proof for openclaw plugins update <id> --dry-run with an exact recorded npm spec and a newer registry default.

What I checked:

  • PR diff unpins exact npm specs: The proposed patch returns installSpec: parsed.name whenever the resolved npm update spec parses as an exact version, turning a stored spec such as @scope/[email protected] into @scope/pkg. (src/plugins/update.ts:924, 5fef9532e556)
  • Current main reuses recorded specs: Current main chooses specOverride ?? officialSpecOverride ?? record.spec and passes that through update-channel resolution, so id-based plugin updates intentionally reuse the stored npm selector unless the user passes an explicit npm spec. (src/plugins/update.ts:940, a0b16f37e835)
  • Same helper feeds live and dry-run paths: The live precheck calls resolveNpmSpecMetadata({ spec: effectiveSpec }), and the dry-run path passes the same effectiveSpec into installPluginFromNpmSpec, so the PR is not limited to dry-run display behavior. (src/plugins/update.ts:1499, a0b16f37e835)
  • Public docs define sticky pins: The plugin CLI docs say id-based updates reuse the recorded install spec, exact pinned versions continue to be used, and passing a bare npm package is the explicit way to return to the registry default line. Public docs: docs/cli/plugins.md. (docs/cli/plugins.md:390, a0b16f37e835)
  • Existing tests pin the current contract: Current tests assert third-party exact npm specs remain pinned during dry-run updates, which the PR would change without adding replacement tests or documentation. (src/plugins/update.test.ts:2197, a0b16f37e835)
  • Live npm behavior confirms the root mechanic: Live npm view returned the pinned versions for exact specs and newer versions for bare specs: @openclaw/[email protected] -> 2026.6.1, bare @openclaw/feishu -> 2026.6.6, @tencent-weixin/[email protected] -> 2.4.3, bare package -> 2.4.4.

Likely related people:

  • huntharo: PR metadata and git history show Harold Hunt authored and merged the versioned plugin update work that added exact npm spec reuse docs/tests and touched src/plugins/update.ts. (role: introduced versioned update behavior; confidence: high; commits: 401ffb59f538; files: src/plugins/update.ts, src/plugins/update.test.ts, docs/cli/plugins.md)
  • steipete: Git history shows Peter Steinberger introduced the plugin update tracking/update-channel path and has the largest shortlog footprint in the central plugin update files sampled. (role: plugin update area contributor; confidence: high; commits: 99fc0fbac1ef, e3151af6bc07, 5696e24c3fba; files: src/plugins/update.ts, src/plugins/install-channel-specs.ts, src/cli/plugins-cli.ts)
  • vincentkoc: Recent current-main commits touched plugin update/recovery behavior in the same area, including official plugin recovery work shortly before this review. (role: recent area contributor; confidence: medium; commits: 767e8280ac7c, 8c802aa68351; files: src/plugins/update.ts, src/plugins/update.test.ts, docs/cli/plugins.md)
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.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 13, 2026
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed proof: supplied External PR includes structured after-fix real behavior proof. proof: sufficient ClawSweeper judged the real behavior proof convincing. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 14, 2026
@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 14, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. label Jun 15, 2026
@xzh-icenter

Copy link
Copy Markdown
Contributor Author

Closing: related plugin fix already merged in #93470.

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

Labels

merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: plugins update --dryrun reports "up to date" while npm dist-tags.latest is newer

1 participant