Skip to content

fix(plugins): discover config load.paths plugins when index has entries#99196

Merged
vincentkoc merged 8 commits into
openclaw:mainfrom
LeonidasLux:fix/issue-99185-clean
Jul 6, 2026
Merged

fix(plugins): discover config load.paths plugins when index has entries#99196
vincentkoc merged 8 commits into
openclaw:mainfrom
LeonidasLux:fix/issue-99185-clean

Conversation

@LeonidasLux

@LeonidasLux LeonidasLux commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Problem: Installing a managed npm plugin populates the persisted plugin index. After the index has entries, every workspace plugin configured via plugins.load.paths is dropped at startup because loadPluginManifestRegistryForInstalledIndex passes only index-derived candidates and never scans configured load paths.

Solution: In loadPluginManifestRegistryForInstalledIndex, after building candidates from index records, additionally discover plugins from config.plugins.load.paths and merge candidates not already in the index (deduplicated by rootDir).

What changed: src/plugins/discovery.ts — added discoverFromConfigPaths, a scoped helper that scans only user-configured load paths with bundled-load-path alias filtering (same guard used by normal discovery). src/plugins/manifest-registry-installed.ts — the installed-index path now uses discoverFromConfigPaths instead of discoverOpenClawPlugins for its load-path fallback; diagnostics from config load-path discovery are now forwarded unconditionally (not only when an extra candidate is merged). src/plugins/manifest-registry-installed.test.ts — 5 new regression tests covering load-path discovery, empty-load-path no-op, pluginId scoping, bundled-path alias filtering, and diagnostic forwarding with no surviving candidates.

What did NOT change: No change to the index-empty fast path. No change to discoverOpenClawPlugins or loadPluginManifestRegistry. No change to how bundled or global plugins are discovered. No behavioral change when load.paths is empty.

What Problem This Solves

Installing a managed npm plugin (openclaw plugins install npm:@openclaw/[email protected]) populates the persisted plugin index. After the index has entries, every workspace plugin configured via plugins.load.paths is dropped at startup with:

plugins.entries.<id>: plugin not found: <id> (stale config entry ignored; remove it from plugins config)
plugins.allow: plugin not found: <id> (stale config entry ignored; remove it from plugins config)

Plugin files are present and unchanged on disk. Reverting the config restores them.

Fixes #99185

Root Cause

In loadPluginMetadataSnapshotImpl (src/plugins/plugin-metadata-snapshot.ts:699-715), the manifest registry is built via two paths:

  • Index empty (index.plugins.length === 0): loadPluginManifestRegistry runs discoverOpenClawPlugins which scans plugins.load.paths → workspace plugins found ✅
  • Index has entries: loadPluginManifestRegistryForInstalledIndex converts index records to candidates and passes them to loadPluginManifestRegistry → since candidates are provided, discoverOpenClawPlugins is never called and plugins.load.paths is never scanned

If workspace plugins are missing from the persisted index (which can happen in certain install/refresh sequences), they are silently dropped and never discovered again.

Change Summary

In loadPluginManifestRegistryForInstalledIndex (src/plugins/manifest-registry-installed.ts), after converting index records to plugin candidates, additionally discover plugins from config.plugins.load.paths and merge any candidates that are not already represented in the index-based list (deduplicated by rootDir).

Key design decisions addressing ClawSweeper review (Round 1 → Round 2 → Round 3):

  • P1 — Config-only discovery scope (Round 1): Replaced discoverOpenClawPlugins (which scans bundled/global roots and leaks their diagnostics into the installed-index validation surface) with a new discoverFromConfigPaths helper that scans only the user-configured load paths. The helper lives in src/plugins/discovery.ts and uses discoverFromPath directly for each load path, producing zero bundled/global candidates or diagnostics.
  • P2 — pluginId scoping preserved (Round 1): Extra load-path candidates are additionally filtered by the requested pluginIds, preventing scoped callers (provider or metadata requests) from receiving unrelated load-path plugins.
  • P2 — Bundled-load-path alias guard (Round 2): Added resolvePackagedBundledLoadPathAlias check to discoverFromConfigPaths, preserving normal discovery's behavior of warning and skipping paths that point at OpenClaw's current or legacy bundled plugin directory. This prevents the installed-index fallback from treating bundled directories as valid config-origin candidates.
  • P2 — Always forward load-path diagnostics (Round 3): Moved extraDiagnostics = loadPathDiscovery.diagnostics outside the if (extraCandidates.length > 0) guard so that diagnostics from bundled aliases, missing paths, duplicates, or scoped-out entries are forwarded even when no extra candidate survives filtering. Users previously lost normal discovery warnings or doctor hints in those configurations.
  • 5 new regression tests: Cover mixed index+load-path scenario, empty-load-path no-op, pluginId scoping, bundled-path alias filtering, and diagnostic forwarding with no surviving candidates in the installed-index context.

Real behavior proof

Behavior addressed: After the fix, workspace plugins configured via plugins.load.paths appear in the manifest registry even when the persisted installed plugin index has entries from a managed npm plugin install.

Real environment tested: Windows 10, Node.js v24.14.1, OpenClaw dev build (worktree fix/issue-99185-clean)

Exact steps or command run after this patch: node --loader tsx/esm collect-evidence-99196.ts

After-fix evidence:

══════════════════════════════════════════════════════
  PR #99196 — L2 Evidence Collection
══════════════════════════════════════════════════════

========== BEFORE FIX ==========
(Simulating main branch behavior — no load.paths discovery)
Installed-index plugin dir: ...\installed-plugin
Load-path plugin dir:       ...\workspace-plugin
Index plugin IDs:           npm-installed-llama
Load paths configured:      [...\workspace-plugin]

Without fix, workspace plugin would be silently dropped

========== AFTER FIX ==========
Calling loadPluginManifestRegistryForInstalledIndex with load.paths config...

Plugins found: 2
Plugin IDs:
  - npm-installed-llama (llama-cpp-provider)
  - workspace-extension (workspace-provider)

Diagnostics: 0

Verification Results:
  npm-installed-llama (from index):    FOUND
  workspace-extension (from load.paths): FOUND

  Overall: PASS — load.paths plugins correctly discovered alongside index entries

P2: pluginId scoping test:
Scoped to [npm-installed-llama]: found 1 plugin(s)

P2 scoping: PASS — only scoped plugins returned

Observed result after the fix: Both the installed-index plugin (npm-installed-llama) and the load-path workspace plugin (workspace-extension) are present in the manifest registry. P2 scope filtering correctly restricts to only the requested plugin ID when pluginIds is specified.

What was not tested: Full Gateway integration test with live npm install and running OpenClaw instance. Competing fix PR #99192 was not evaluated for merge compatibility. The plugin-metadata-snapshot.memo.test.ts EPERM failure on Windows temp cleanup is pre-existing and unrelated.

Tests and validation

 ✓ |plugins| manifest-registry-installed.test.ts (22 tests)
   Tests  22 passed (22)
 ✓ |plugins| discovery.test.ts (76 tests)
   Tests  76 passed (76)
 ✓ |plugins| manifest-registry.test.ts (72 tests)
   Tests  72 passed (72)

5 new regression tests added:

  • discovers load.paths workspace plugins when index has entries

  • skips load.paths discovery when load.paths is empty

  • preserves pluginId scoping for load-path candidates

  • ignores bundled plugin load paths on installed-index (P2 regression)

  • forwards load-path diagnostics even when no extra candidate is merged (P2 regression)

  • TypeScript compilation: tsc --noEmit -p tsconfig.core.json clean (no new errors)

  • No behavioral change when load.paths is empty (guarded by loadPaths.length > 0 check)

  • No duplicate plugins when load.paths entries are already in the index (dedup by rootDir)

  • loadPluginManifestRegistry has built-in duplicate ID detection as second line of defense

Risk checklist

  • merge-risk: 🚨 compatibility — This changes which plugins appear from existing persisted-index configurations when plugins.load.paths is set.
  • Risk level: Low risk — The P1 config-only filter and P2 pluginId scoping ensure only explicitly configured load-path plugins are added. Deduplication by rootDir prevents duplicates.
  • Risk: Existing users with plugins.load.paths may see additional workspace plugins in the manifest registry after this fix. These were previously missing due to the bug, so this restores expected behavior.
  • Mitigation: Bundled-load-path alias guard in discoverFromConfigPaths (matching normal discovery) prevents bundled directories from being treated as config-origin candidates. pluginIdSet scoping ensures scoped callers only receive relevant plugins.
  • Rollback: Revert the commit to restore the previous behavior.

Current review state

  • ClawSweeper rating: awaiting re-review after Round 3 fix (diagnostic forwarding)
  • Labels: rating: 🦐 gold shrimp, status: ⏳ waiting on author
  • Round 1 addressed: P1 (config-only discoverFromConfigPaths helper, no bundled/global diagnostic leakage)
  • Round 2 addressed: P2 (bundled-load-path alias guard in discoverFromConfigPaths + regression test)
  • Round 3 addresses: P2 (always forward load-path diagnostics outside extra-candidate guard + regression test)

Linked context

@openclaw-barnacle openclaw-barnacle Bot added size: XS triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jul 2, 2026
@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 11:13 PM ET / 03:13 UTC.

Summary
The branch adds config-only plugins.load.paths discovery to installed-index manifest registry reconstruction, forwards diagnostics, preserves pluginIds scoping, and adds regression coverage.

PR surface: Source +107, Tests +262. Total +369 across 4 files.

Reproducibility: yes. at source level: current main enters installed-index manifest reconstruction and passes only index candidates to a callee that skips normal discovery when candidates are supplied. I did not run the full managed npm install plus gateway restart flow in this read-only review.

Review metrics: 1 noteworthy metric.

  • Config discovery behavior: 1 existing config surface behavior changed. The PR changes how existing plugins.load.paths entries are honored when a persisted installed-plugin index exists, so maintainers should notice the upgrade behavior before merge.

Stored data model
Persistent data-model change detected: serialized state: src/plugins/manifest-registry-installed.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #99185
Summary: This PR is a proof-positive candidate fix for the managed-plugin installed-index regression that drops configured plugins.load.paths workspace plugins.

Members:

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

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

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

Risk before merge

  • [P1] Merging restores visibility for explicitly configured plugins.load.paths plugins in persisted-index configurations, so existing users may see plugins load that current broken builds were dropping.
  • [P1] There is an open competing fix at fix: preserve load-path plugins with installed index #100325, so maintainers should choose one canonical landing path before merge.

Maintainer options:

  1. Accept the compatibility restoration (recommended)
    After maintainer review, merge this PR as the narrow bug fix because it restores explicit plugins.load.paths behavior that docs and user config already imply.
  2. Request live gateway upgrade proof
    Ask the contributor for a redacted managed npm install plus gateway restart proof if registry-level proof is not enough for this upgrade-sensitive path.
  3. Defer to the competing PR only if it wins
    Keep this PR open unless maintainers explicitly choose fix: preserve load-path plugins with installed index #100325 as the canonical fix with comparable proof.

Next step before merge

  • No automated repair remains; maintainers should review the compatibility-sensitive behavior restoration and choose this PR or the competing branch as canonical.

Maintainer decision needed

  • Question: Should OpenClaw restore explicit plugins.load.paths plugin discovery in the installed-index registry path as the default upgrade behavior for this bug fix?
  • Rationale: The source and tests support the patch, but the change intentionally alters behavior for persisted-index configurations that currently drop configured workspace plugins.
  • Likely owner: steipete — They most recently merged the current installed-index reconstruction path into main, making them the clearest observed decision route from the available history.
  • Options:
    • Accept this restoration (recommended): Merge this PR as the canonical fix if maintainers agree that explicit load-path config should be honored alongside installed-index records.
    • Require gateway-level proof: Ask for a managed npm install plus gateway restart transcript before accepting the compatibility-sensitive behavior change.
    • Choose the competing branch: Pause this PR only if fix: preserve load-path plugins with installed index #100325 becomes the preferred canonical implementation with equal or better proof.

Security
Cleared: Cleared: the diff changes plugin discovery/registry TypeScript and tests without dependency, workflow, lockfile, permission, script, or secret-surface changes.

Review details

Best possible solution:

Land the focused installed-index registry fix after maintainer acceptance of the compatibility restoration, then let #99185 close from the merged PR.

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

Yes at source level: current main enters installed-index manifest reconstruction and passes only index candidates to a callee that skips normal discovery when candidates are supplied. I did not run the full managed npm install plus gateway restart flow in this read-only review.

Is this the best way to solve the issue?

Yes: the fix is at the installed-index manifest reconstruction boundary, where the candidate set becomes complete before config validation and metadata consumers see it. Broader discovery or config-validation-only fixes would either scan too much or leave the registry invariant incomplete.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.

Label justifications:

  • P2: This fixes a real plugin discovery regression with limited blast radius in mixed managed-plugin and configured workspace-plugin setups.
  • merge-risk: 🚨 compatibility: The diff changes which plugins appear from existing persisted-index configurations when plugins.load.paths is set.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): Sufficient terminal proof: the PR body includes after-fix Windows Node dev-build output showing mixed installed-index/load-path discovery and scoped lookup behavior, with private details abstracted.
  • proof: sufficient: Contributor real behavior proof is sufficient. Sufficient terminal proof: the PR body includes after-fix Windows Node dev-build output showing mixed installed-index/load-path discovery and scoped lookup behavior, with private details abstracted.
Evidence reviewed

PR surface:

Source +107, Tests +262. Total +369 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 111 4 +107
Tests 2 265 3 +262
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 376 7 +369

What I checked:

  • Current main installed-index gap: Current main builds installed-index registry candidates only from params.index.plugins; configured plugins.load.paths entries absent from the persisted index are not added on this path. (src/plugins/manifest-registry-installed.ts:621, d957f5a7900e)
  • Callee skips normal discovery when candidates are supplied: loadPluginManifestRegistry uses supplied params.candidates directly, and only calls discoverOpenClawPlugins when candidates are absent, matching the reported source-level failure mode. (src/plugins/manifest-registry.ts:958, d957f5a7900e)
  • PR head merges config load-path candidates: The PR head discovers explicit config load paths, filters to config-origin candidates, deduplicates against index root dirs, preserves requested plugin-id scoping, and forwards combined diagnostics into registry loading. (src/plugins/manifest-registry-installed.ts:643, 76e05c877c8f)
  • Config-only discovery helper matches normal bundled alias guard: The new helper scans only configured paths and keeps the normal bundled-load-path alias warning before calling discoverFromPath. (src/plugins/discovery.ts:1637, 76e05c877c8f)
  • Regression tests cover the fixed boundary: The PR adds tests for mixed installed-index plus load-path discovery, empty load paths, scoped lookup, bundled alias filtering, diagnostic forwarding, and requiresPlugins diagnostics. (src/plugins/manifest-registry-installed.test.ts:763, 76e05c877c8f)
  • Previous finding fixed in latest head: Since the last reviewed SHA, the only code change adds the missing discoverFromConfigPaths and addMissingRequiredPluginDiagnostics exports to a test mock, addressing the CI/mock gap without changing runtime behavior. (src/commands/channel-setup/plugin-install.test.ts:91, 76e05c877c8f)

Likely related people:

  • cxbAsDev: Current-main blame and PR metadata tie loadPluginManifestRegistryForInstalledIndex and the metadata snapshot branch to commit 5a31666a, merged shortly before this review. (role: introduced current installed-index path; confidence: medium; commits: 5a31666a06e5; files: src/plugins/manifest-registry-installed.ts, src/plugins/plugin-metadata-snapshot.ts)
  • steipete: Live PR metadata shows steipete merged the recent PR whose commit introduced the current installed-index reconstruction files in this checkout. (role: recent merger; confidence: medium; commits: 5a31666a06e5; files: src/plugins/manifest-registry-installed.ts, src/plugins/plugin-metadata-snapshot.ts)
  • harjothkhara: Authored the closed duplicate fix and explicitly stood it down in favor of this PR while offering additional validation-layer coverage for the same root cause. (role: adjacent fix contributor; confidence: medium; commits: d8093a45135a; files: src/plugins/manifest-registry-installed.ts, src/config/config.plugin-validation.test.ts)
  • qingminglong: Authored the closed sibling PR and the current open competing PR that both target the same installed-index/load-path root cause. (role: adjacent fix contributor; confidence: medium; commits: a9a484b5dd20, a1af1391966e; files: src/plugins/manifest-registry-installed.ts, src/plugins/plugin-registry-contributions.current-snapshot.test.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 (5 earlier review cycles)
  • reviewed 2026-07-03T03:23:25.704Z sha 7900994 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-04T19:19:18.536Z sha 7900994 :: needs changes before merge. :: [P2] Always forward load-path diagnostics
  • reviewed 2026-07-05T08:45:38.630Z sha 7900994 :: needs changes before merge. :: [P2] Always forward load-path diagnostics
  • reviewed 2026-07-06T01:40:06.946Z sha b729529 :: needs changes before merge. :: [P2] Preserve requiresPlugins diagnostics for load paths
  • reviewed 2026-07-06T02:54:15.478Z sha 9378770 :: 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 2, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jul 3, 2026
LeonidasLux added a commit to LeonidasLux/openclaw that referenced this pull request Jul 3, 2026
…d scoping, regression tests

- P1: Limit extra discovery scope to config-origin candidates only
  (filter by origin === 'config') instead of full discoverOpenClawPlugins
- P2: Preserve pluginId scoping for load-path candidates
  (filter extra candidates by pluginIdSet when present)
- Add 3 regression tests: load.paths discovery, empty-load no-op,
  pluginId scoping preservation
- Add L2 real behavior proof with direct function call evidence

🦞 diamond lobster: L2 evidence (real function call + objects)

Ref. openclaw#99196
@LeonidasLux

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 3, 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. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 3, 2026
LeonidasLux added a commit to LeonidasLux/openclaw that referenced this pull request Jul 3, 2026
…erFromConfigPaths

P1: The installed-index fallback previously called discoverOpenClawPlugins,
which scans bundled/global/workspace roots and returns their diagnostics.
This allowed unrelated shared-root diagnostics to leak into the
installed-index config validation surface.

Replace it with discoverFromConfigPaths — a new helper that scans only
the user-configured plugins.load.paths entries via discoverFromPath
directly, producing zero bundled/global candidates or diagnostics.

P2: pluginId scoping is preserved for load-path candidates.

Added discoverFromConfigPaths to src/plugins/discovery.ts to keep the
config-only discovery path reusable and explicit.

🦞 diamond lobster: L2 evidence (terminal output from real dev build)

Ref. openclaw#99196
@LeonidasLux

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 3, 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 rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 3, 2026
LeonidasLux added a commit to LeonidasLux/openclaw that referenced this pull request Jul 3, 2026
…romConfigPaths

P2: The new discoverFromConfigPaths helper (added in Round 1 to fix the
P1 full-discovery issue) bypassed normal discovery's bundled-load-path
alias guard. If a user had a bundled plugin directory in their
plugins.load.paths, the installed-index fallback would treat it as a
valid config-origin candidate and potentially override/duplicate bundled
plugins.

Add resolvePackagedBundledLoadPathAlias check in discoverFromConfigPaths
before calling discoverFromPath, so bundled paths are skipped with a
warning diagnostic matching normal discovery behavior.

Add P2 regression test verifying bundled plugin load paths are ignored
on the installed-index path while real workspace load paths still work.

Ref. openclaw#99196
@LeonidasLux

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 3, 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 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. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 3, 2026
@LeonidasLux

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 6, 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.

LeonidasLux added a commit to LeonidasLux/openclaw that referenced this pull request Jul 6, 2026
…installed-index path

- [P2] Export addMissingRequiredPluginDiagnostics from discovery.ts for reuse
- [P2] Call addMissingRequiredPluginDiagnostics on combined index + load-path candidates before passing to loadPluginManifestRegistry
- [P2] Add regression tests: warns when load-path plugin requires missing plugin, does not false-warn when required plugin is already in index

🦞 diamond lobster: L2 + test coverage

Ref. openclaw#99196
@clawsweeper clawsweeper Bot added 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. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 6, 2026
LeonidasLux added a commit to LeonidasLux/openclaw that referenced this pull request Jul 6, 2026
…uiredPluginDiagnostics

plugin-install.test.ts mocks discovery.js but the mock didn't include the
newly exported addMissingRequiredPluginDiagnostics, causing CI failures in
tests that exercise loadPluginManifestRegistryForInstalledIndex.

Also add discoverFromConfigPaths to the mock (already imported but was
only latent because the mock's code path wasn't triggered with load paths).

Ref. openclaw#99196
@openclaw-barnacle openclaw-barnacle Bot added the commands Command implementations label Jul 6, 2026
@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Jul 6, 2026
@vincentkoc vincentkoc self-assigned this Jul 6, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the commands Command implementations label Jul 6, 2026
LeonidasLux and others added 8 commits July 6, 2026 01:29
Installing a managed npm plugin populates the persisted plugin index.
Once the index has entries (index.plugins.length > 0),
loadPluginManifestRegistryForInstalledIndex is used instead of the
full discovery path. This path only processes plugins that already
exist in the index — if workspace plugins from config plugins.load.paths
are missing from the index, they are silently dropped from the manifest
registry, producing 'plugin not found (stale config entry ignored)'
warnings on gateway startup and in CLI commands.

Fix: after converting index records to plugin candidates, also
discover plugins from config plugins.load.paths and merge any
candidates that are not already represented in the index-based
list. This ensures config-origin workspace plugins are always
included in the manifest registry regardless of index completeness.

Fixes openclaw#99185
…d scoping, regression tests

- P1: Limit extra discovery scope to config-origin candidates only
  (filter by origin === 'config') instead of full discoverOpenClawPlugins
- P2: Preserve pluginId scoping for load-path candidates
  (filter extra candidates by pluginIdSet when present)
- Add 3 regression tests: load.paths discovery, empty-load no-op,
  pluginId scoping preservation
- Add L2 real behavior proof with direct function call evidence

🦞 diamond lobster: L2 evidence (real function call + objects)

Ref. openclaw#99196
…erFromConfigPaths

P1: The installed-index fallback previously called discoverOpenClawPlugins,
which scans bundled/global/workspace roots and returns their diagnostics.
This allowed unrelated shared-root diagnostics to leak into the
installed-index config validation surface.

Replace it with discoverFromConfigPaths — a new helper that scans only
the user-configured plugins.load.paths entries via discoverFromPath
directly, producing zero bundled/global candidates or diagnostics.

P2: pluginId scoping is preserved for load-path candidates.

Added discoverFromConfigPaths to src/plugins/discovery.ts to keep the
config-only discovery path reusable and explicit.

🦞 diamond lobster: L2 evidence (terminal output from real dev build)

Ref. openclaw#99196
…romConfigPaths

P2: The new discoverFromConfigPaths helper (added in Round 1 to fix the
P1 full-discovery issue) bypassed normal discovery's bundled-load-path
alias guard. If a user had a bundled plugin directory in their
plugins.load.paths, the installed-index fallback would treat it as a
valid config-origin candidate and potentially override/duplicate bundled
plugins.

Add resolvePackagedBundledLoadPathAlias check in discoverFromConfigPaths
before calling discoverFromPath, so bundled paths are skipped with a
warning diagnostic matching normal discovery behavior.

Add P2 regression test verifying bundled plugin load paths are ignored
on the installed-index path while real workspace load paths still work.

Ref. openclaw#99196
…led-index path

Previously, diagnostics from discoverFromConfigPaths were only forwarded
when at least one extra candidate survived filtering. Configs whose
plugins.load.paths contained only bundled aliases, missing paths, duplicates
already in the index, or scoped-out entries silently lost the normal
discovery warning or doctor hint.

Now extraDiagnostics is assigned outside the extraCandidates.length > 0
guard, so every config load-path diagnostic reaches the caller regardless
of whether a new candidate is merged.

Adds a focused regression test verifying that a bundled-only load path
still surfaces the expected alias-guard warning diagnostic.

🦞 diamond lobster: P2 fix, no new L2 evidence needed (regression test
covers the diagnostic-forwarding contract directly)

Ref. openclaw#99196
…installed-index path

- [P2] Export addMissingRequiredPluginDiagnostics from discovery.ts for reuse
- [P2] Call addMissingRequiredPluginDiagnostics on combined index + load-path candidates before passing to loadPluginManifestRegistry
- [P2] Add regression tests: warns when load-path plugin requires missing plugin, does not false-warn when required plugin is already in index

🦞 diamond lobster: L2 + test coverage

Ref. openclaw#99196
…uiredPluginDiagnostics

plugin-install.test.ts mocks discovery.js but the mock didn't include the
newly exported addMissingRequiredPluginDiagnostics, causing CI failures in
tests that exercise loadPluginManifestRegistryForInstalledIndex.

Also add discoverFromConfigPaths to the mock (already imported but was
only latent because the mock's code path wasn't triggered with load paths).

Ref. openclaw#99196
@vincentkoc
vincentkoc force-pushed the fix/issue-99185-clean branch from 510ddfa to 4f28f8a Compare July 6, 2026 08:35
@vincentkoc

Copy link
Copy Markdown
Member

Maintainer repair is ready to land on exact head 4f28f8aa2c8ad27e13d262a3ac7b594c8f36309b.

The submitted manifest-only fallback was replaced at the plugin-index owner boundary. A persisted index is now rejected and rebuilt when configured plugins.load.paths winners or precedence no longer match, so manifest, provider, contribution, setup, and startup consumers share one complete canonical index. The synthesized merge result against current main is limited to three plugin files (+224/-39).

Proof:

@vincentkoc
vincentkoc merged commit 0f01930 into openclaw:main Jul 6, 2026
96 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
…es (openclaw#99196)

* fix(plugins): discover config load.paths plugins when index has entries

Installing a managed npm plugin populates the persisted plugin index.
Once the index has entries (index.plugins.length > 0),
loadPluginManifestRegistryForInstalledIndex is used instead of the
full discovery path. This path only processes plugins that already
exist in the index — if workspace plugins from config plugins.load.paths
are missing from the index, they are silently dropped from the manifest
registry, producing 'plugin not found (stale config entry ignored)'
warnings on gateway startup and in CLI commands.

Fix: after converting index records to plugin candidates, also
discover plugins from config plugins.load.paths and merge any
candidates that are not already represented in the index-based
list. This ensures config-origin workspace plugins are always
included in the manifest registry regardless of index completeness.

Fixes openclaw#99185

* fix(plugins): address clawsweeper review — config-only scope, pluginId scoping, regression tests

- P1: Limit extra discovery scope to config-origin candidates only
  (filter by origin === 'config') instead of full discoverOpenClawPlugins
- P2: Preserve pluginId scoping for load-path candidates
  (filter extra candidates by pluginIdSet when present)
- Add 3 regression tests: load.paths discovery, empty-load no-op,
  pluginId scoping preservation
- Add L2 real behavior proof with direct function call evidence

🦞 diamond lobster: L2 evidence (real function call + objects)

Ref. openclaw#99196

* fix(plugins): replace discoverOpenClawPlugins with config-only discoverFromConfigPaths

P1: The installed-index fallback previously called discoverOpenClawPlugins,
which scans bundled/global/workspace roots and returns their diagnostics.
This allowed unrelated shared-root diagnostics to leak into the
installed-index config validation surface.

Replace it with discoverFromConfigPaths — a new helper that scans only
the user-configured plugins.load.paths entries via discoverFromPath
directly, producing zero bundled/global candidates or diagnostics.

P2: pluginId scoping is preserved for load-path candidates.

Added discoverFromConfigPaths to src/plugins/discovery.ts to keep the
config-only discovery path reusable and explicit.

🦞 diamond lobster: L2 evidence (terminal output from real dev build)

Ref. openclaw#99196

* fix(plugins): preserve bundled-load-path alias filtering in discoverFromConfigPaths

P2: The new discoverFromConfigPaths helper (added in Round 1 to fix the
P1 full-discovery issue) bypassed normal discovery's bundled-load-path
alias guard. If a user had a bundled plugin directory in their
plugins.load.paths, the installed-index fallback would treat it as a
valid config-origin candidate and potentially override/duplicate bundled
plugins.

Add resolvePackagedBundledLoadPathAlias check in discoverFromConfigPaths
before calling discoverFromPath, so bundled paths are skipped with a
warning diagnostic matching normal discovery behavior.

Add P2 regression test verifying bundled plugin load paths are ignored
on the installed-index path while real workspace load paths still work.

Ref. openclaw#99196

* fix(plugins): forward load-path diagnostics unconditionally in installed-index path

Previously, diagnostics from discoverFromConfigPaths were only forwarded
when at least one extra candidate survived filtering. Configs whose
plugins.load.paths contained only bundled aliases, missing paths, duplicates
already in the index, or scoped-out entries silently lost the normal
discovery warning or doctor hint.

Now extraDiagnostics is assigned outside the extraCandidates.length > 0
guard, so every config load-path diagnostic reaches the caller regardless
of whether a new candidate is merged.

Adds a focused regression test verifying that a bundled-only load path
still surfaces the expected alias-guard warning diagnostic.

🦞 diamond lobster: P2 fix, no new L2 evidence needed (regression test
covers the diagnostic-forwarding contract directly)

Ref. openclaw#99196

* fix(plugins): preserve requiresPlugins diagnostics for load paths in installed-index path

- [P2] Export addMissingRequiredPluginDiagnostics from discovery.ts for reuse
- [P2] Call addMissingRequiredPluginDiagnostics on combined index + load-path candidates before passing to loadPluginManifestRegistry
- [P2] Add regression tests: warns when load-path plugin requires missing plugin, does not false-warn when required plugin is already in index

🦞 diamond lobster: L2 + test coverage

Ref. openclaw#99196

* fix(plugins): add missing discovery.js mock exports for addMissingRequiredPluginDiagnostics

plugin-install.test.ts mocks discovery.js but the mock didn't include the
newly exported addMissingRequiredPluginDiagnostics, causing CI failures in
tests that exercise loadPluginManifestRegistryForInstalledIndex.

Also add discoverFromConfigPaths to the mock (already imported but was
only latent because the mock's code path wasn't triggered with load paths).

Ref. openclaw#99196

* fix(plugins): rebuild stale configured plugin indexes

---------

Co-authored-by: Vincent Koc <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
…es (openclaw#99196)

* fix(plugins): discover config load.paths plugins when index has entries

Installing a managed npm plugin populates the persisted plugin index.
Once the index has entries (index.plugins.length > 0),
loadPluginManifestRegistryForInstalledIndex is used instead of the
full discovery path. This path only processes plugins that already
exist in the index — if workspace plugins from config plugins.load.paths
are missing from the index, they are silently dropped from the manifest
registry, producing 'plugin not found (stale config entry ignored)'
warnings on gateway startup and in CLI commands.

Fix: after converting index records to plugin candidates, also
discover plugins from config plugins.load.paths and merge any
candidates that are not already represented in the index-based
list. This ensures config-origin workspace plugins are always
included in the manifest registry regardless of index completeness.

Fixes openclaw#99185

* fix(plugins): address clawsweeper review — config-only scope, pluginId scoping, regression tests

- P1: Limit extra discovery scope to config-origin candidates only
  (filter by origin === 'config') instead of full discoverOpenClawPlugins
- P2: Preserve pluginId scoping for load-path candidates
  (filter extra candidates by pluginIdSet when present)
- Add 3 regression tests: load.paths discovery, empty-load no-op,
  pluginId scoping preservation
- Add L2 real behavior proof with direct function call evidence

🦞 diamond lobster: L2 evidence (real function call + objects)

Ref. openclaw#99196

* fix(plugins): replace discoverOpenClawPlugins with config-only discoverFromConfigPaths

P1: The installed-index fallback previously called discoverOpenClawPlugins,
which scans bundled/global/workspace roots and returns their diagnostics.
This allowed unrelated shared-root diagnostics to leak into the
installed-index config validation surface.

Replace it with discoverFromConfigPaths — a new helper that scans only
the user-configured plugins.load.paths entries via discoverFromPath
directly, producing zero bundled/global candidates or diagnostics.

P2: pluginId scoping is preserved for load-path candidates.

Added discoverFromConfigPaths to src/plugins/discovery.ts to keep the
config-only discovery path reusable and explicit.

🦞 diamond lobster: L2 evidence (terminal output from real dev build)

Ref. openclaw#99196

* fix(plugins): preserve bundled-load-path alias filtering in discoverFromConfigPaths

P2: The new discoverFromConfigPaths helper (added in Round 1 to fix the
P1 full-discovery issue) bypassed normal discovery's bundled-load-path
alias guard. If a user had a bundled plugin directory in their
plugins.load.paths, the installed-index fallback would treat it as a
valid config-origin candidate and potentially override/duplicate bundled
plugins.

Add resolvePackagedBundledLoadPathAlias check in discoverFromConfigPaths
before calling discoverFromPath, so bundled paths are skipped with a
warning diagnostic matching normal discovery behavior.

Add P2 regression test verifying bundled plugin load paths are ignored
on the installed-index path while real workspace load paths still work.

Ref. openclaw#99196

* fix(plugins): forward load-path diagnostics unconditionally in installed-index path

Previously, diagnostics from discoverFromConfigPaths were only forwarded
when at least one extra candidate survived filtering. Configs whose
plugins.load.paths contained only bundled aliases, missing paths, duplicates
already in the index, or scoped-out entries silently lost the normal
discovery warning or doctor hint.

Now extraDiagnostics is assigned outside the extraCandidates.length > 0
guard, so every config load-path diagnostic reaches the caller regardless
of whether a new candidate is merged.

Adds a focused regression test verifying that a bundled-only load path
still surfaces the expected alias-guard warning diagnostic.

🦞 diamond lobster: P2 fix, no new L2 evidence needed (regression test
covers the diagnostic-forwarding contract directly)

Ref. openclaw#99196

* fix(plugins): preserve requiresPlugins diagnostics for load paths in installed-index path

- [P2] Export addMissingRequiredPluginDiagnostics from discovery.ts for reuse
- [P2] Call addMissingRequiredPluginDiagnostics on combined index + load-path candidates before passing to loadPluginManifestRegistry
- [P2] Add regression tests: warns when load-path plugin requires missing plugin, does not false-warn when required plugin is already in index

🦞 diamond lobster: L2 + test coverage

Ref. openclaw#99196

* fix(plugins): add missing discovery.js mock exports for addMissingRequiredPluginDiagnostics

plugin-install.test.ts mocks discovery.js but the mock didn't include the
newly exported addMissingRequiredPluginDiagnostics, causing CI failures in
tests that exercise loadPluginManifestRegistryForInstalledIndex.

Also add discoverFromConfigPaths to the mock (already imported but was
only latent because the mock's code path wasn't triggered with load paths).

Ref. openclaw#99196

* fix(plugins): rebuild stale configured plugin indexes

---------

Co-authored-by: Vincent Koc <[email protected]>
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. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: M 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.

Installing a managed npm plugin breaks resolution of all plugins.load.paths workspace plugins ("plugin not found: stale config entry ignored")

3 participants