channels: make plugin state persistence best effort by default#75053
channels: make plugin state persistence best effort by default#75053
Conversation
…ams, matrix
Adds plugins.entries.<id>.config.experimentalPersistentState (default
false) to slack/discord/msteams/matrix, mirroring four in-memory caches
into the SDK-backed plugin state store while keeping the existing
process-local fast paths as the default behavior:
- Slack thread participation (sent-thread-cache)
- Discord component & modal registries (components-registry)
- MS Teams sent-message dedupe (sent-message-cache)
- Matrix approval reaction targets (approval-reactions)
Errors from the persistent path are logged via the plugin runtime and
never break the synchronous record/lookup paths. Each plugin gets a
*ForConfig async variant used by handlers that already run in async
contexts; opt-in is gated by resolvePluginConfigObject(cfg, ...).
Tests: extends each plugin's cache test with no-op-when-disabled and
persistent register/lookup with a fake runtime store. Updates relevant
docs (channels/{slack,discord,msteams,matrix}.md, plugins/sdk-runtime.md)
and the changelog.
Address draft PR review feedback for the opt-in SDK-backed channel state migration: - backfill Slack and MS Teams in-memory caches after persistent lookup hits so hot inbound paths only pay the SQLite lookup once per key after restart - reset cached persistent store handles from existing cache clear helpers so tests and runtime reset paths reopen stores from the current runtime - use a narrow Matrix plugins config lookup type instead of a double cast - document why Slack persists agentId even though current reads only check participation presence Validation: pnpm test extensions/slack/src/sent-thread-cache.test.ts extensions/discord/src/components.test.ts extensions/msteams/src/sent-message-cache.test.ts extensions/matrix/src/approval-reactions.test.ts; pnpm check:changed.
Build the alternative default-on version of the bundled plugin state migration. The four migrated caches now keep their existing in-memory fast paths as the source of truth and automatically try the SDK-backed keyed store once their plugin runtime is available. If opening SQLite or a store operation fails, the plugin logs once for that path, disables further persistent access for the process, and keeps the previous process-local behavior. This removes the experimentalPersistentState plugin config gate and the cfg threading that only existed for that opt-in. Slack/MS Teams continue to backfill memory after restart lookups; Discord keeps consume/delete semantics; Matrix keeps versioned approval reaction targets. Tests cover both available persistent stores and unavailable SQLite fallback. Validation: targeted plugin cache tests, relevant call-site tests, oxfmt check, and pnpm check:changed.
|
Codex review: needs maintainer review before merge. What this changes: The PR makes Slack, Discord, Microsoft Teams, and Matrix bundled plugin caches automatically use the SQLite-backed SDK keyed state store as best-effort restart durability behind their existing in-memory caches, with docs, changelog, and focused tests updated. Maintainer follow-up before merge: Protected maintainer-labeled draft PR needs human review to choose default-on versus opt-in persistence and confirm validation; there is no discrete repair task for an automated replacement PR. Security review: Security review cleared: Read-only diff review found no workflow, dependency, lockfile, lifecycle script, secret-handling, or new third-party code execution changes; the security-relevant change is persisted channel metadata using the existing bundled-plugin state store. Review detailsBest possible solution: Have a maintainer choose the intended rollout model between this default-on PR and #74933, then land the selected bundled-plugin migration with the existing keyed-store SDK contract, focused cache tests, docs, and changed-gate proof. Do we have a high-confidence way to reproduce the issue? Not applicable as a feature PR rather than a bug report. The motivating limitation is statically verifiable on current main: the targeted Slack, Discord, Microsoft Teams, and Matrix registries are process-local, so a Gateway restart drops their state. Is this the best way to solve the issue? Unclear pending maintainer review. The patch is a plausible narrow implementation of default-on best-effort persistence, but the existence of the opt-in alternative and the protected maintainer label mean this should not be treated as automatically merge-ready. Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 58a0b077c1c5. |
Summary
Alternative to #74933 that removes the per-plugin
experimentalPersistentStateopt-in and instead treats the SQLite-backed SDK state store as best-effort durability behind the existing in-memory caches.The existing process-local maps/dedupe caches remain the fast path and source of truth. Once a bundled plugin runtime is available, each migrated cache automatically tries
api.runtime.state.openKeyedStore. If opening the store or any store operation fails, the plugin logs the failure, disables further persistent-store use for that process, and continues with the previous process-local behavior.Migrations
sent-thread-cache)slack.thread-participationcomponents-registry)discord.components,discord.modalssent-message-cache)msteams.sent-messagesapproval-reactions)matrix.approval-reactionsWhy this shape
This keeps the old behavior as the fallback path without requiring operators to opt in through config:
experimentalPersistentStatemanifest schema entries.resolvePluginConfigObject(...)gates in the hot paths.cfgthreading solely for persistence.The behavior change when SQLite works is restart durability for existing bundled plugin caches. When SQLite is unavailable, behavior falls back to the pre-persistence process-local cache behavior.
Tests
Added/updated tests for:
Validation
Local from
../openclaw-plugin-fixes-sdk-default-on:pnpm test extensions/slack/src/sent-thread-cache.test.ts extensions/discord/src/components.test.ts extensions/msteams/src/sent-message-cache.test.ts extensions/matrix/src/approval-reactions.test.ts— passedpnpm test extensions/slack/src/action-runtime.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts extensions/discord/src/monitor/monitor.test.ts extensions/matrix/src/matrix/monitor/reaction-events.test.ts extensions/matrix/src/approval-handler.runtime.test.ts extensions/msteams/src/monitor-handler/message-handler.authz.test.ts— passedpnpm exec oxfmt --check ...on touched TS files — passedgit diff HEAD --check— passedpnpm check:changed— passedNotes
This branch is intentionally separate from #74933 so reviewers can compare the opt-in version against the default-on best-effort version.