Skip to content

fix(plugins): preserve memory capability across snapshot plugin loads#69219

Merged
steipete merged 2 commits into
openclaw:mainfrom
zeroaltitude:fix/memory-cap-restore
Apr 20, 2026
Merged

fix(plugins): preserve memory capability across snapshot plugin loads#69219
steipete merged 2 commits into
openclaw:mainfrom
zeroaltitude:fix/memory-cap-restore

Conversation

@zeroaltitude

Copy link
Copy Markdown
Contributor

Problem

The plugin loader's per-plugin snapshot/rollback dance captures several previousMemory* values to restore after a non-activating load — but previousMemoryCapability was missing. Both restore sites (the if (!shouldActivate) branch post-register, and the register-catch branch) called restoreMemoryPluginState({...}) without a capability field.

Because restoreMemoryPluginState treats the field as authoritative (state.capability ? {...} : void 0), omitting the field was effectively a wipe.

Symptom

Any non-activating load — for example resolvePluginWebSearchProviders calling loadOpenClawPlugins({ activate: false }) — iterates every plugin, re-registers, then restores to the pre-snapshot state without capability. This clears whatever capability the memory plugin had registered on the live load.

In practice: listActiveMemoryPublicArtifacts returns [] and wiki.bridge.import returns 0 artifacts after the first non-activating load, even though memory plugins appear loaded and their register() runs correctly.

Fix

  • Capture previousMemoryCapability = getMemoryCapabilityRegistration() alongside the other per-plugin snapshots
  • Include capability: previousMemoryCapability in both restoreMemoryPluginState call sites

The cached-load site (around line 1453 in current main) already did this correctly — it's the two per-plugin sites inside the iteration that were the outliers.

Testing

Added preserves previously registered memory capability across activate:false snapshot loads in src/plugins/loader.test.ts:

  • Registers a memory-kind plugin that sets a publicArtifacts capability
  • Asserts listActiveMemoryPublicArtifacts returns the artifact
  • Triggers a second loadOpenClawPlugins({ activate: false }) (same shape as the real secondary loaders)
  • Asserts the capability still returns the artifact

Red-green verified locally: without the fix the second listActiveMemoryPublicArtifacts returns []; with the fix it returns the expected artifact. All 94 tests in loader.test.ts pass.

Discovery context

Found while debugging wiki.bridge.import returning 0 artifacts with memory-core installed and the vaultMode: "bridge" config. The misleading clue: getMemoryCapabilityRegistration() immediately after registerMemoryCapability() always returned the right value, because the wipe happened on a later iteration of the rollback loop (including repeated calls from resolvePluginWebSearchProviders).

@greptile-apps

greptile-apps Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a memory capability wipe caused by missing capability field in two restoreMemoryPluginState calls within the per-plugin snapshot/rollback loop in the non-activating (activate: false) load path. The fix captures previousMemoryCapability before each plugin's register() and passes it to both restore sites, consistent with how the cached-load site already handled it. A regression test is added that verifies the capability survives a follow-up activate: false load.

Confidence Score: 5/5

Safe to merge — targeted one-line fix with a red-green verified regression test covering the exact failure mode.

The change is minimal and precise: it adds the missing capability field to two restoreMemoryPluginState calls, matching the pattern already used in the cached-load site. The root cause analysis in the PR description is accurate and confirmed by reading restoreMemoryPluginState — omitting capability unconditionally clears it via the ternary. The new test exercises the exact multi-plugin snapshot rollback path with both a memory-kind plugin and a sidecar, red-green verified by the author. No regressions or edge cases left unaddressed.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(plugins): preserve memory capability..." | Re-trigger Greptile

@zeroaltitude
zeroaltitude force-pushed the fix/memory-cap-restore branch from 1322334 to 7791bb9 Compare April 20, 2026 06:00
zeroaltitude and others added 2 commits April 20, 2026 20:37
The loader per-plugin snapshot/rollback dance captured
`previousMemoryCorpusSupplements`, `previousMemoryPromptBuilder`, and
related memory-state fields, but never captured
`previousMemoryCapability`. Both restore sites (the post-register
`if (!shouldActivate)` branch and the register `catch` branch) called
`restoreMemoryPluginState({...})` without a `capability` field.

Because `restoreMemoryPluginState` treats `state.capability ? {...} : void 0`,
omitting the field was effectively a wipe. Any non-activating load
(e.g. `resolvePluginWebSearchProviders` calling
`loadOpenClawPlugins({ activate: false })`) would iterate every plugin,
re-register, then restore to the pre-snapshot state without
`capability` — clearing whatever capability the memory plugin had
registered. In practice this caused `listActiveMemoryPublicArtifacts`
to return `[]` and broke `wiki.bridge.import` after the first
non-activating load.

Fix: capture `previousMemoryCapability = getMemoryCapabilityRegistration()`
alongside the other previous-* snapshots and include
`capability: previousMemoryCapability` in both `restoreMemoryPluginState`
call sites. The cached-load site already did this correctly.

Adds a regression test that asserts the memory capability survives an
activate:false snapshot load after a prior active load.
@steipete
steipete force-pushed the fix/memory-cap-restore branch from 7791bb9 to e2ff6b6 Compare April 20, 2026 19:42
@steipete
steipete merged commit 8595e6c into openclaw:main Apr 20, 2026
10 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via temp rebase onto main.

  • Gate: pnpm test src/plugins/loader.test.ts; pnpm check:changed
  • Rebased PR head: e2ff6b6
  • Merge commit: 8595e6c

Thanks @zeroaltitude!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants