Skip to content

fix(plugins): repair stale managed openclaw peers#78251

Closed
vincentkoc wants to merge 4 commits into
mainfrom
fix-plugin-peer-root-repair
Closed

fix(plugins): repair stale managed openclaw peers#78251
vincentkoc wants to merge 4 commits into
mainfrom
fix-plugin-peer-root-repair

Conversation

@vincentkoc

@vincentkoc vincentkoc commented May 6, 2026

Copy link
Copy Markdown
Member

Summary

  • repair stale root-level openclaw installs inside OpenClaw-managed npm plugin roots before updating npm-installed plugins
  • prevent new Opik-style peer installs from materializing root node_modules/openclaw by running managed npm-root installs with --omit=peer
  • trust legacy managed roots through the default npm root, the existing install record path, or an already-written managed-root marker; the marker is future proofing, not required for old installs
  • narrow beta fallback so @beta only falls back to stable when the beta package/tag is missing, not when npm reports a real install failure like ERESOLVE

RCA

Externalized official plugins can live in ~/.openclaw/npm or another OpenClaw-managed npm root. Some legacy roots ended up with openclaw installed at the root of that managed npm project, either in root manifest fields, the package lock, or node_modules/openclaw.

The likely origination path is an external plugin that declared openclaw as a peer dependency. With modern npm, a peer dependency can be materialized during install unless we explicitly omit peers. That can create root-level node_modules/openclaw in the managed npm project even though OpenClaw already has an explicit host-peer symlink path for plugins.

That stale root package is poison for plugin updates. When a user moves from stable to beta, npm can resolve the plugin candidate correctly, then fail because the managed npm root still appears to contain an older openclaw package. In the bad box, this showed up as plugin beta update failure and fallback to the stable package, leaving Discord/WhatsApp/Codex-style externalized plugins behind even though the core OpenClaw package updated.

The old updater made that worse by treating every failed beta npm install as "beta unavailable" and falling back to the untagged/stable package. That is okay for a missing beta dist-tag. It is wrong for peer-resolution failures, malformed packages, or other real install errors.

Fix

The installer now prevents the normal peer path from creating root poison: managed npm-root plugin installs include --omit=peer as well as --omit=dev. OpenClaw then owns the openclaw peer through its existing host symlink, so plugin packages can still declare peerDependencies.openclaw without npm installing a second OpenClaw copy into the managed root.

The install path is still intentionally non-blocking. We do not reject plugins that declare openclaw. If an installed package declares openclaw in peer, direct, or optional dependencies, the installed package gets linked to the host OpenClaw package before dependency-tree scanning when it came from the managed npm root. That avoids stranding users with a blocked install just because npm or a package manifest tried to materialize OpenClaw locally.

The installer also inspects the managed npm root before and after installing any non-openclaw npm plugin. If it finds stale root-level openclaw state, it first uses native npm cleanup commands from the managed root: npm uninstall openclaw and npm prune. Only if native cleanup leaves poison behind does it fall back to directly removing the root manifest/lock entries and quarantining stale filesystem payloads.

The repair is conservative. It refuses bare OpenClaw installs, roots overlapping the running OpenClaw package, and pnpm workspaces. It also refuses unknown custom roots unless the root is the default managed npm dir, came from an existing npm install record, or already has the managed-root marker. That keeps the repair from mutating a user's unrelated npm project while still fixing old OpenClaw-managed plugin roots retroactively.

Update now derives the original npm root from each npm install record and passes that root back into install/update. That means users who installed official externalized plugins into a non-default OpenClaw-managed root do not get silently redirected to the default root during update.

Beta fallback is now limited to missing-package/missing-tag cases (npm_package_not_found, ETARGET, notarget, missing dist-tag/tag text). Real beta install failures now surface as errors so we do not mask broken state by downgrading the plugin.

Coverage

Happy paths covered:

  • unchanged managed npm root when no stale root openclaw exists
  • normal plugin dependency upsert still preserves other managed plugin dependencies
  • managed npm-root installs use --omit=peer, so peer dependencies do not materialize root openclaw
  • native npm cleanup repairs stale root openclaw while preserving plugin packages
  • npm updates reuse the npm root derived from an existing install record
  • beta update falls back to stable only when the beta tag/package is unavailable

Non-happy paths covered:

  • stale root openclaw in dependencies, optionalDependencies, peerDependencies, root lock metadata, lock-only state, and filesystem-only state
  • Opik-style peer install where npm materializes root openclaw: post-install repair removes the root poison and links the plugin to the host OpenClaw package
  • required-peer npm e2e install where npm would otherwise create a root OpenClaw package in the managed plugin root
  • corrupt package lock gets quarantined during fallback repair
  • plugin-local nested node_modules/openclaw is not treated as root poison
  • bare openclaw npm roots and pnpm workspaces are skipped instead of mutated
  • unproven custom roots are skipped; legacy install-record roots are trusted and repaired
  • invalid beta package installs do not fall back to stable
  • beta ERESOLVE peer-resolution failures do not fall back to stable

Verification

  • pnpm exec oxfmt --check --threads=1 src/infra/safe-package-install.ts src/infra/safe-package-install.test.ts src/plugins/install.ts src/plugins/install.npm-spec.test.ts
  • pnpm exec oxlint --threads=1 src/infra/safe-package-install.ts src/infra/safe-package-install.test.ts src/plugins/install.ts src/plugins/install.npm-spec.test.ts
  • git diff --check
  • Testbox tbx_01kqxy0ntk8s446ynqa06a53re: pnpm test:serial src/infra/safe-package-install.test.ts src/infra/npm-managed-root.test.ts src/plugins/install.npm-spec.test.ts src/plugins/install.npm-spec.e2e.test.ts src/plugins/update.test.ts (117 tests)
  • Testbox tbx_01kqxy0ntk8s446ynqa06a53re: pnpm check:changed (lanes: core, coreTests, docs)

@openclaw-barnacle openclaw-barnacle Bot added size: M maintainer Maintainer-authored PR labels May 6, 2026
@vincentkoc vincentkoc self-assigned this May 6, 2026
@clawsweeper

clawsweeper Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge.

Summary
The PR adds stale managed npm-root openclaw peer cleanup around npm plugin installs/updates, runs managed npm installs with --omit=peer, reuses recorded npm roots, narrows beta fallback, and adds tests plus a changelog entry.

Reproducibility: no. not as a high-confidence live reproduction in this read-only review. Source inspection shows current main can install/update npm plugins in a managed root without stale root openclaw cleanup or --omit=peer, and can fall back from real beta npm install failures.

Real behavior proof
Not applicable: The external-proof gate is not applicable to this maintainer-authored PR; the Testbox commands in the body are useful validation, but live poisoned-root beta-update logs would still strengthen the merge case.

Next step before merge
A maintainer should choose whether to close this branch in favor of #78291 or fold the missing cleanup surfaces back here; this is not a safe automated repair lane while a superseding protected PR is open.

Security
Cleared: The diff does not add dependencies, CI changes, secret access, or new third-party code execution; its npm cleanup commands use --ignore-scripts on proven managed roots.

Review findings

  • [P2] Remove hidden npm lock state during root repair — src/infra/npm-managed-root.ts:380-386
Review details

Best possible solution:

Use #78291 as the canonical landing target, or fold its hidden-lock, bin-shim, and peer-link reassertion coverage back into this branch before merging.

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

No, not as a high-confidence live reproduction in this read-only review. Source inspection shows current main can install/update npm plugins in a managed root without stale root openclaw cleanup or --omit=peer, and can fall back from real beta npm install failures.

Is this the best way to solve the issue?

No for landing this exact branch as-is. The repair is in the right code path, but #78291 supersedes it and covers additional known cleanup and peer-link surfaces that this branch misses.

Full review comments:

  • [P2] Remove hidden npm lock state during root repair — src/infra/npm-managed-root.ts:380-386
    The fallback repair removes the visible package-lock.json entries and node_modules/openclaw, but npm also maintains node_modules/.package-lock.json. If stale root openclaw state remains only in that hidden lock, this helper reports a repaired root while leaving npm metadata behind for the next install, so the managed-root repair can still fail to clear the poisoned root completely.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.82

Acceptance criteria:

  • pnpm test src/infra/safe-package-install.test.ts src/plugins/uninstall.test.ts src/plugins/install.npm-spec.test.ts src/plugins/install.npm-spec.e2e.test.ts src/plugins/update.test.ts src/infra/npm-managed-root.test.ts
  • git diff --check origin/main...HEAD
  • Testbox pnpm check:changed before merge if this branch remains the landing target

What I checked:

  • Live PR state: GitHub API shows this PR is open, ready for review, authored by vincentkoc with author association MEMBER, labeled maintainer and size: XL, and currently points at head e536a8648acb3ef1c28c27e68dc01a98fb37fa01. (e536a8648acb)
  • Current main install path lacks the repair: Current main resolves the managed npm root, upserts the target plugin dependency, and runs npm install --omit=dev without pre/post stale root openclaw repair or --omit=peer. (src/plugins/install.ts:1337, 627b0073f2f7)
  • PR install hook: The PR calls repairManagedNpmRootOpenClawPeerForInstall before and after non-openclaw npm plugin installs and adds omitPeer: true to the safe npm install args. (src/plugins/install.ts:1394, e536a8648acb)
  • Current main beta fallback is broad: Current main falls back from a failed beta npm update whenever a fallback spec exists, without checking whether the beta failure is a missing tag/package versus a real npm install error. (src/plugins/update.ts:1331, 627b0073f2f7)
  • PR narrows beta fallback: The PR adds shouldFallbackBetaNpmUpdate and gates both dry-run and live beta fallback on missing-package/tag-style failures. (src/plugins/update.ts:260, e536a8648acb)
  • Superseding PR: GitHub API shows fix(plugins): repair managed npm openclaw peers #78291 is open and its body says it carries forward this PR and fix(plugins): clear hidden npm lock during peer repair #78265, removes hidden lock/bin-shim state, reasserts plugin-local peer links, and explicitly Supersedes this PR. (0668a238c369)

Likely related people:

  • vincentkoc: Recent merged history for src/plugins/install.ts and src/plugins/update.ts includes repeated official npm, prerelease, provenance, rollback, and update fixes in the exact install/update area this PR changes. (role: recent plugin install/update maintainer; confidence: high; commits: 5ca0aa1d158e, 2014c2327b20, b0f841ef37db; files: src/plugins/install.ts, src/plugins/update.ts)
  • ProspectOre: Current main has adjacent peer-link repair commits by ProspectOre in the npm update path, including repair after npm updates and isolating peer-link repair failures. (role: adjacent peer-link repair owner; confidence: high; commits: fb42c722f0f5, d221d7b6a989, 2e8761c5c154; files: src/plugins/update.ts, src/plugins/update.test.ts, src/infra/package-update-utils.ts)
  • steipete: Recent current-main and GitHub history show plugin install/update maintenance, filesystem safety refactors near the managed-root helper, and ownership of the open superseding PR fix(plugins): repair managed npm openclaw peers #78291. (role: recent adjacent maintainer and superseding PR owner; confidence: medium; commits: 538605ff44d2, b85b1c68d175, 4820b701a597; files: src/plugins/install.ts, src/plugins/update.ts, src/infra/npm-managed-root.ts)
  • Patrick-Erichsen: Patrick contributed the required-peer coverage commit on this branch, opened the related stacked hidden-lock repair fix(plugins): clear hidden npm lock during peer repair #78265, and has recent merged update robustness work in the same plugin update area. (role: related stacked repair and update-test contributor; confidence: medium; commits: 5468e9ba2ea0, 8aa7b7a4ca17; files: src/infra/npm-managed-root.test.ts, src/plugins/install.npm-spec.e2e.test.ts, src/plugins/update.ts)

Remaining risk / open question:

  • This read-only review did not run a live beta-channel update against a poisoned managed npm root; the root cause is source-reproducible but not live-reproduced here.
  • Merging this branch alone would leave known repair coverage behind the open superseding PR, especially npm hidden lock and peer-link reassertion surfaces.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 627b0073f2f7.

Re-review progress:

@vincentkoc vincentkoc force-pushed the fix-plugin-peer-root-repair branch from 97c6830 to a47973d Compare May 6, 2026 05:52
@Patrick-Erichsen Patrick-Erichsen force-pushed the fix-plugin-peer-root-repair branch from 3eeaf7b to 5468e9b Compare May 6, 2026 05:57
@Patrick-Erichsen Patrick-Erichsen marked this pull request as ready for review May 6, 2026 06:06
@steipete

steipete commented May 6, 2026

Copy link
Copy Markdown
Contributor

Superseded by #78291, which is now merged on main as 8e533490ab0a94fec8b3bdb3b184ebb5ce2edaba.

#78291 carries this stale managed-root peer repair forward and broadens it to remove stale managed-root openclaw manifest/lock/filesystem state, relink plugin-local peers after install/update/uninstall mutations, and cover the npm e2e/update/uninstall paths. Closing this PR so review stays on the merged canonical fix.

@steipete steipete closed this May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants