Compatibility registry
Plugin compatibility contracts are tracked in the core registry atsrc/plugins/compat/registry.ts. Each record has:
- a stable compatibility code
- status:
active,deprecated,removal-pending, orremoved - owner:
sdk,config,setup,channel,provider,plugin-execution,agent-runtime, orcore - introduction and deprecation dates when applicable
- replacement guidance
- docs, diagnostics, and tests that cover the old and new behavior
src/commands/doctor/shared/deprecation-compat.ts. Those records cover old
config shapes, install-ledger layouts, and repair shims that may need to
stay available after the runtime compatibility path is removed.
Release sweeps should check both registries. Do not delete a doctor
migration just because the matching runtime or config compatibility record
expired; first verify there is no supported upgrade path that still needs
the repair. Revalidate each replacement annotation during release planning
too, since plugin ownership and config footprint can change as providers
and channels move out of core.
Deprecation policy
OpenClaw should not remove a documented plugin contract in the same release that introduces its replacement. Migration sequence:- Add the new contract.
- Keep the old behavior wired through a named compatibility adapter.
- Emit diagnostics or warnings when plugin authors can act.
- Document the replacement and timeline.
- Test both old and new paths.
- Wait through the announced migration window.
- Remove only with explicit breaking-release approval.
active instead.
Current compatibility areas
The registry currently tracks around 70 compatibility codes across these areas. New plugin code should use the replacement in each area and in the specific migration guide; existing plugins can keep using a compatibility path until docs, diagnostics, and release notes announce a removal window.- legacy broad SDK imports such as
openclaw/plugin-sdk/compat - legacy hook-only plugin shapes and
before_agent_start - legacy
api.on("deactivate", ...)cleanup hook names while plugins migrate togateway_stop - legacy
activate(api)plugin entrypoints while plugins migrate toregister(api) - legacy SDK aliases such as
openclaw/extension-api,openclaw/plugin-sdk/channel-runtime,openclaw/plugin-sdk/command-authstatus builders,openclaw/plugin-sdk/test-utils(replaced by focusedopenclaw/plugin-sdk/*test subpaths), and theClawdbotConfig/OpenClawSchemaTypetype aliases - bundled plugin allowlist and enablement behavior
- legacy provider/channel env-var manifest metadata
- legacy provider plugin hooks and type aliases while providers move to explicit catalog, auth, thinking, replay, and transport hooks
- legacy runtime aliases such as
api.runtime.taskFlow,api.runtime.subagent.getSession,api.runtime.stt, and deprecatedapi.runtime.config.loadConfig()/api.runtime.config.writeConfigFile(...) - WhatsApp
WebInboundMessageflat callback fields (see below) - WhatsApp
WebInboundMessagetop-level admission fields (see below) - legacy memory-plugin split registration while memory plugins move to
registerMemoryCapability - legacy memory-specific embedding provider registration while embedding
providers move to
api.registerEmbeddingProvider(...)andcontracts.embeddingProviders - legacy channel SDK helpers for native message schemas, mention gating, inbound envelope formatting, and approval capability nesting
- legacy channel route key and comparable-target helper aliases while
plugins move to
openclaw/plugin-sdk/channel-route - activation hints being replaced by manifest contribution ownership
setup-apiruntime fallback while setup descriptors move to coldsetup.requiresRuntime: falsemetadata- provider
discoveryhooks while provider catalog hooks move tocatalog.run(...) - channel
showConfigured/showInSetupmetadata while channel packages move toopenclaw.channel.exposure - legacy runtime-policy config keys while doctor migrates operators to
agentRuntime - generated bundled channel config metadata fallback while registry-first
channelConfigsmetadata lands - persisted plugin registry disable and install-migration env flags while
repair flows migrate operators to
openclaw plugins registry --refreshandopenclaw doctor --fix - legacy plugin-owned web search, web fetch, and x_search config paths
while doctor migrates them to
plugins.entries.<plugin>.config - legacy
plugins.installsauthored config and bundled plugin load-path aliases while install metadata moves into the state-managed plugin ledger
WhatsApp inbound callback flat aliases
WhatsApp runtime callbacks deliverWebInboundMessage: the canonical
nested event, payload, quote, group, and platform contexts plus
deprecated flat aliases for the shipped callback fields. New callback code
should read the nested contexts. Code that constructs clean nested callback
messages can use WebInboundCallbackMessage; compatibility listeners that
still inject old flat test or plugin messages should use
LegacyFlatWebInboundMessage or WebInboundMessageInput.
The flat aliases remain available until 2026-08-30; that window applies
only to flat alias access, not to the nested shape, which is the canonical
runtime contract. Each flat alias’s TypeScript @deprecated annotation
names its exact nested replacement. Common examples:
id,timestamp, andisBatchedmove underevent.body,mediaPath,mediaType,mediaFileName,mediaUrl,location, anduntrustedStructuredContextmove underpayload.to,chatId, sender/self fields,sendComposing,reply(...), andsendMedia(...)move underplatform.replyTo*fields move underquote; group subject/participant/mention fields move undergroup.
payload.untrustedStructuredContext is extracted from inbound provider
payloads. Plugins should inspect label, source, and type before
treating its payload as authoritative.
WhatsApp inbound admission fields
Accepted WhatsApp callback messages carryadmission, a public-safe
envelope for the access-control decision that admitted the message. New
callback code should read admission facts from msg.admission instead of
the older top-level admission fields.
The top-level fields remain available until 2026-08-30. Each field’s
TypeScript @deprecated annotation names its replacement:
fromandconversationIdmove toadmission.conversation.id.accountIdmoves toadmission.accountId.accessControlPassedis a derived compatibility view ofadmission.ingress.decision === "allow"; on messages that already carryadmission, writing the legacy boolean does not rewrite the ingress graph.chatTypemoves toadmission.conversation.kind.
Plugin inspector package
The plugin inspector should live outside the core OpenClaw repo as a separate package/repository backed by the versioned compatibility and manifest contracts. The day-one CLI should be:--json for stable
machine-readable output in CI annotations. OpenClaw core should expose
contracts and fixtures the inspector can consume, but should not publish the
inspector binary from the main openclaw package.
Maintainer acceptance lane
Use Crabbox-backed Blacksmith Testbox for the installable-package acceptance lane when validating the external inspector against OpenClaw plugin packages. Run it from a clean OpenClaw checkout after the package is built:Release notes
Release notes should include upcoming plugin deprecations with target dates and links to migration docs, before a compatibility path moves toremoval-pending or removed.