Skip to content

Bundled plugins duplicate persistent-state and doctor migration plumbing instead of SDK family seams #99841

Description

@steipete

Bundled plugins copy-paste the same state/doctor plumbing instead of using (or getting) plugin SDK family seams. A jscpd sweep (min 15 lines / 120 tokens, production code only) plus manual reading found three clusters that account for the largest verbatim duplication in the repo:

1. Best-effort persistent dedupe caches (4 files, ~640 lines total)

extensions/slack/src/sent-thread-cache.ts, extensions/slack/src/monitor/inbound-delivery-state.ts, extensions/mattermost/src/mattermost/thread-participation.ts, and extensions/msteams/src/sent-message-cache.ts each hand-roll the same dual-layer cache: a global in-memory dedupe layer plus a lazily opened runtime.state.openKeyedStore wrapper with disable-on-error semantics. Slack <-> Mattermost are ~95% identical whole files (the Mattermost header even says "Mirrors the Slack sent-thread-cache dual-layer pattern"). The top jscpd hit in the repo is a 40-line verbatim clone between the two.

2. Matrix approval reactions bypass an existing SDK helper (~90 lines)

openclaw/plugin-sdk/approval-reaction-runtime already ships createApprovalReactionTargetStore, used by imessage, signal, and whatsapp. extensions/matrix/src/approval-reactions.ts reimplements the entire store locally (memory map + persisted {version: 1, target} records + disable-on-error), and its local copy has drifted: the in-memory map is unbounded and never expires entries, unlike the SDK store that all sibling channels use.

3. Doctor legacy-state migration helpers (10 files)

fileExists is copied verbatim into 10 extension doctor-contract-api.ts files, and archiveLegacySource into 9, with per-plugin label prefixes baked into otherwise identical message templates (acpx, active-memory, device-pair, matrix, memory-core, memory-wiki, msteams, nostr, phone-control, voice-call).

extensions/AGENTS.md is explicit about this failure mode: "If two bundled providers share the same ... shape, stop copying the logic. Extract one shared helper and migrate both call sites in the same change."

Proposed fix (PR follows):

  • add a createPersistentDedupeCache family helper to openclaw/plugin-sdk/dedupe-runtime and migrate the four presence-cache call sites;
  • migrate matrix approval reactions onto createApprovalReactionTargetStore (keeping the matrix-specific emoji bindings local);
  • export shared legacyStateFileExists / archiveLegacyStateSource helpers from openclaw/plugin-sdk/runtime-doctor and migrate all ten doctor contracts.

Out of scope, reviewed and intentionally left: the msteams-local estimateBase64DecodedBytes copy (documented hot-path load-cost tradeoff vs the broad media-runtime barrel), the discord components-registry store wrapper (linked two-store disable + richer error detail), and the core-side src/plugins/provider-openai-chatgpt-oauth-tls.ts doctor fast-path twin of the openai plugin runtime (needs a doctor-contract owner decision).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-priority cleanup, docs, polish, ergonomics, or speculative work.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maintainerMaintainer-authored PRmaturity:stableIssue affects a taxonomy feature currently scored M4/M5.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions