Skip to content

fix(doctor): migrate legacy bundled plugin load paths#55054

Merged
obviyus merged 4 commits into
openclaw:mainfrom
SnowSky1:codex/54963-feishu-dist-path-migration
Mar 27, 2026
Merged

fix(doctor): migrate legacy bundled plugin load paths#55054
obviyus merged 4 commits into
openclaw:mainfrom
SnowSky1:codex/54963-feishu-dist-path-migration

Conversation

@SnowSky1

Copy link
Copy Markdown
Contributor

Summary

  • detect legacy bundled plugin load paths that still point at extensions/<id> after packaged builds moved to dist/extensions/<id>
  • teach openclaw doctor --fix to rewrite those stale bundled paths automatically
  • surface the migration in doctor preview warnings so users get an actionable fix hint before startup fails

Why

Users upgrading from older installs can be left with bundled plugin load paths like .../openclaw/extensions/feishu, which no longer exist in packaged builds. That currently blocks startup with plugin path not found, even though the matching bundled plugin now lives under dist/extensions/<id>.

Scope

  • adds a targeted doctor repair for legacy bundled plugin load paths
  • adds regression coverage for both the repair and preview warning paths
  • does not change startup-time auto-migration behavior in this PR

Verification

  • pnpm test -- src/commands/doctor/shared/bundled-plugin-load-paths.test.ts src/commands/doctor/shared/preview-warnings.test.ts
  • pnpm exec oxfmt --check src/commands/doctor/shared/bundled-plugin-load-paths.ts src/commands/doctor/shared/bundled-plugin-load-paths.test.ts src/commands/doctor/shared/preview-warnings.ts src/commands/doctor/shared/preview-warnings.test.ts src/commands/doctor/repair-sequencing.ts

Closes #54963

@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: M labels Mar 26, 2026
@SnowSky1

Copy link
Copy Markdown
Contributor Author

@codex review

@greptile-apps

greptile-apps Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds targeted detection and auto-repair of legacy bundled plugin load paths that still point at extensions/<id> rather than the current dist/extensions/<id> layout, wiring the new repair into both doctor --fix and the preview-warnings pipeline so users see an actionable hint before startup fails.\n\nKey changes:\n- bundled-plugin-load-paths.ts (new): scanBundledPluginLoadPathMigrations builds a map of expected legacy paths derived from each bundled plugin's current localPath, then matches against user config entries; maybeRepairBundledPluginLoadPaths rewrites matched entries in a structuredClone of the config and returns change notes; collectBundledPluginLoadPathWarnings formats the user-facing hint.\n- repair-sequencing.ts: inserts the new repair before maybeRepairStalePluginConfig, ensuring stale-plugin cleanup sees already-migrated paths.\n- preview-warnings.ts: integrates the scan into the existing warning pipeline.\n- Tests: new unit tests cover detection, repair, warning formatting, and the no-op (already-new-path) case; an integration test covers the end-to-end preview-warning path.\n\nOne minor concern: maybeRepairBundledPluginLoadPaths silently drops non-string entries from paths when rebuilding the array, whereas the scan phase preserves them. In practice paths entries are always strings, but making the two phases consistent would be more defensive.

Confidence Score: 5/5

Safe to merge; targeted fix with solid test coverage and no changes to startup-time behaviour.

Core detection and repair logic is correct: buildLegacyBundledPath correctly inverts the dist/extensions/ suffix, the seen-set deduplication handles the edge case where both the legacy and new paths are already present, and the repair is correctly ordered before maybeRepairStalePluginConfig. The only identified gap (non-string path entries silently dropped during repair) is a defensive-coding concern rather than a correctness bug under realistic configs. Test coverage is thorough.

No files require special attention; the P2 comment on bundled-plugin-load-paths.ts is optional hardening.

Important Files Changed

Filename Overview
src/commands/doctor/shared/bundled-plugin-load-paths.ts New module implementing legacy bundled plugin path detection and repair. Logic is correct and well-structured; one minor issue: non-string path entries are silently dropped when the repair rewrites the paths array.
src/commands/doctor/shared/bundled-plugin-load-paths.test.ts Good test coverage for detection, repair, warning formatting, and the no-op case. All three exported functions are exercised.
src/commands/doctor/shared/preview-warnings.ts Correctly integrates the new bundled plugin load path scan into the preview warnings pipeline, consistent with existing warning patterns.
src/commands/doctor/shared/preview-warnings.test.ts New integration test correctly mocks both manifest registry and bundled sources to verify the end-to-end warning surfaced to the user.
src/commands/doctor/repair-sequencing.ts New repair is correctly inserted before maybeRepairStalePluginConfig, which is the right order since stale-plugin cleanup should operate on already-migrated paths.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/commands/doctor/shared/bundled-plugin-load-paths.ts
Line: 120-132

Comment:
**Non-string path entries silently dropped during repair**

When a legacy hit is detected, `maybeRepairBundledPluginLoadPaths` rebuilds the `paths` array but silently omits any non-string entries via the `continue` branch. The scan phase correctly skips non-strings without affecting them, but the repair phase drops them entirely — so a config with `paths: ["/legacy/feishu", 42, "/other/path"]` would be written back as `["/new/feishu", "/other/path"]` with no corresponding entry in `changes`.

In practice `paths` entries should always be strings, so this is unlikely to matter today, but making the repair preserve unrecognised entries would make the behaviour consistent with the scan:

```suggestion
  for (const entry of paths) {
    if (typeof entry !== "string") {
      // Preserve non-string entries as-is; only rewrite known legacy paths.
      rewritten.push(entry as string);
      continue;
    }
    const resolved = path.normalize(resolveUserPath(entry, env));
    const replacement = replacements.get(resolved)?.toPath ?? entry;
    const replacementResolved = path.normalize(resolveUserPath(replacement, env));
    if (seen.has(replacementResolved)) {
      continue;
    }
    seen.add(replacementResolved);
    rewritten.push(replacement);
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(doctor): migrate legacy bundled plug..." | Re-trigger Greptile

Comment thread src/commands/doctor/shared/bundled-plugin-load-paths.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@SnowSky1

Copy link
Copy Markdown
Contributor Author

Follow-up:

  • preserved non-string plugins.load.paths entries during repair instead of silently dropping them
  • added regression coverage for mixed arrays like [legacyPath, 42, /other/path]

Local verification still passes:

  • pnpm test -- src/commands/doctor/shared/bundled-plugin-load-paths.test.ts src/commands/doctor/shared/preview-warnings.test.ts
  • pnpm exec oxfmt --check src/commands/doctor/shared/bundled-plugin-load-paths.ts src/commands/doctor/shared/bundled-plugin-load-paths.test.ts src/commands/doctor/shared/preview-warnings.ts src/commands/doctor/shared/preview-warnings.test.ts src/commands/doctor/repair-sequencing.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f6f55efec

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +22 to +23
const suffix = path.join("dist", "extensions", pluginId);
if (!normalized.endsWith(suffix)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Derive legacy bundled path from directory name

buildLegacyBundledPath assumes every bundled path ends in dist/extensions/<pluginId>, but this is not always true for bundled plugins (for example, extensions/kimi-coding/openclaw.plugin.json declares id: "kimi"). In that case the suffix check fails, the plugin is skipped from legacyPathMap, and openclaw doctor --fix will not migrate stale .../extensions/kimi-coding entries, so affected upgrades can still hit plugin path not found. Please derive the legacy path from the bundled directory segment in source.localPath (or equivalent manifest/package metadata) rather than pluginId alone.

Useful? React with 👍 / 👎.

@SnowSky1

Copy link
Copy Markdown
Contributor Author

@obviyus when you have a moment, could you take a quick look?

This branch is now fully green again after the follow-up. The latest update only preserves mixed plugins.load.paths entries during doctor repair and adds regression coverage for that edge case; the overall scope is still the same focused doctor --fix migration for legacy bundled plugin load paths.

Thanks!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f37cccb269

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +77 to +78
const normalized = path.normalize(resolveUserPath(rawPath, env));
const match = legacyPathMap.get(normalized);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Canonicalize load-path slashes before migration lookup

scanBundledPluginLoadPathMigrations compares plugins.load.paths entries with a plain path.normalize(resolveUserPath(...)) key lookup, but path.normalize preserves trailing separators (for example .../extensions/feishu/), while buildLegacyBundledPath stores keys without that trailing slash. In configs that contain a legacy bundled path with a trailing / or \, the lookup misses and openclaw doctor --fix will not rewrite the stale path, so the original plugin path not found failure can persist after repair. This only triggers for those slash-suffixed path variants, but it breaks the migration in that common manual-edit case.

Useful? React with 👍 / 👎.

@obviyus obviyus self-assigned this Mar 27, 2026
@obviyus obviyus force-pushed the codex/54963-feishu-dist-path-migration branch from f37cccb to ba35989 Compare March 27, 2026 03:34
@obviyus obviyus merged commit 7016659 into openclaw:main Mar 27, 2026
17 of 20 checks passed
@obviyus

obviyus commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Landed on main.

Thanks @SnowSky1.

pxnt pushed a commit to pxnt/openclaw that referenced this pull request Mar 27, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
godlin-gh pushed a commit to YouMindInc/openclaw that referenced this pull request Mar 27, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
alexcode-cc pushed a commit to alexcode-cc/clawdbot that referenced this pull request Mar 30, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…nowSky1)

* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (openclaw#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade breaks plugin path: extensions/feishu → dist/extensions/feishu not auto-migrated

2 participants