Skip to content

feat(plugin-state): add registerIfAbsent keyed store#77135

Merged
amknight merged 1 commit intomainfrom
ak/plugin-state-claim-check
May 4, 2026
Merged

feat(plugin-state): add registerIfAbsent keyed store#77135
amknight merged 1 commit intomainfrom
ak/plugin-state-claim-check

Conversation

@amknight
Copy link
Copy Markdown
Member

@amknight amknight commented May 4, 2026

Summary

  • Problem: plugin dedupe consumers need an atomic state-store claim primitive instead of lookup-plus-register races.
  • Why it matters: channel/tool plugins can avoid double-processing without owning SQLite details or crossing plugin isolation boundaries.
  • What changed: added registerIfAbsent(key, value, opts?) to PluginStateKeyedStore, backed by a BEGIN IMMEDIATE SQLite transaction and INSERT OR IGNORE, with tests for duplicate, expiry, isolation, eviction, limits, and parallel claims.
  • What did NOT change (scope boundary): did not touch BlueBubbles or Feishu consumers; existing register upsert semantics remain unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
  • Target test or file: src/plugin-state/plugin-state-store.test.ts, src/plugin-state/plugin-state-store.runtime.test.ts, src/plugin-state/plugin-state-store.e2e.test.ts
  • Scenario the test should lock in: first claim wins, live duplicate returns false without overwriting, expired row can be claimed again, plugin/namespace isolation, eviction/row caps, and practical same-process parallel claim safety.
  • Why this is the smallest reliable guardrail: the state-store seam owns validation and SQLite transaction behavior directly.

User-visible / Behavior Changes

Bundled plugins can now call api.runtime.state.openKeyedStore(...).registerIfAbsent(...) for atomic keyed-store dedupe claims.

Diagram (if applicable)

Before:
plugin -> lookup(key) -> register(key) -> race window

After:
plugin -> registerIfAbsent(key, value) -> true if claimed, false if live row exists

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS local worktree
  • Runtime/container: Node/OpenClaw repo scripts
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): isolated OpenClaw test state fixtures

Steps

  1. Add registerIfAbsent to the state-store type and implementation.
  2. Exercise first insert, duplicate, expired insert, isolation, limits, and runtime proxy tests.
  3. Run focused format, tests, type checks, SDK API baseline check, and diff whitespace check.

Expected

  • registerIfAbsent returns true only when the claim is inserted and false for an existing live key without mutating it.

Actual

  • Focused tests and type checks passed.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What I personally verified:

  • pnpm exec oxfmt --check --threads=1 src/plugin-state/plugin-state-store.ts src/plugin-state/plugin-state-store.sqlite.ts src/plugin-state/plugin-state-store.types.ts src/plugin-state/plugin-state-store.test.ts src/plugin-state/plugin-state-store.e2e.test.ts src/plugin-state/plugin-state-store.runtime.test.ts docs/plugins/sdk-runtime.md CHANGELOG.md
  • pnpm test src/plugin-state/plugin-state-store.test.ts src/plugin-state/plugin-state-store.runtime.test.ts src/plugin-state/plugin-state-store.e2e.test.ts -- --reporter=verbose
  • pnpm test src/plugin-state/plugin-state-store.permissions.test.ts -- --reporter=verbose
  • pnpm tsgo:core
  • pnpm tsgo:core:test
  • pnpm plugin-sdk:api:check
  • git diff --check

Edge cases checked:

  • Duplicate live key does not overwrite value, createdAt, or TTL.
  • Expired key can be inserted again.
  • Plugin and namespace isolation remain intact.
  • Namespace eviction and per-plugin row cap behavior remain intact.
  • Parallel same-process claim attempts produce one winner.

What I did not verify:

  • Full pnpm check:changed/full suite; this PR used targeted local proof for the touched state-store seam.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: claim logic could diverge from existing register validation/limits.
    • Mitigation: shared register parameter preparation plus direct state-store tests for validation-adjacent behavior, isolation, TTL, eviction, row cap, and runtime exposure.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation plugin: file-transfer size: M maintainer Maintainer-authored PR labels May 4, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 4, 2026

Codex review: needs maintainer review before merge.

Summary
Adds PluginStateKeyedStore.registerIfAbsent, a SQLite INSERT OR IGNORE implementation, focused state-store tests, SDK runtime docs, and a changelog entry.

Reproducibility: not applicable. this is an additive API feature PR, not a bug report. Current main source confirms the method is absent, and the PR diff adds the method plus focused coverage.

Next step before merge
Maintainers need to resolve the protected-label API decision and the overlap with #77134; there is no narrow repair task from this review.

Security
Cleared: No concrete security or supply-chain regression found; the diff changes scoped plugin-state storage API/tests/docs/changelog without new dependencies, secrets, network calls, CI, or execution surfaces.

Review details

Best possible solution:

Land one approved atomic keyed-store claim primitive with docs, changelog, and focused tests, then handle BlueBubbles and Feishu consumer migrations separately after their retry semantics are settled.

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

Not applicable; this is an additive API feature PR, not a bug report. Current main source confirms the method is absent, and the PR diff adds the method plus focused coverage.

Is this the best way to solve the issue?

Yes, with maintainer approval: the primitive-only shape is a narrow maintainable way to unblock dedupe consumers and avoids the BlueBubbles consumer semantic blocker found on #77134.

Acceptance criteria:

  • pnpm test src/plugin-state/plugin-state-store.test.ts src/plugin-state/plugin-state-store.runtime.test.ts src/plugin-state/plugin-state-store.e2e.test.ts -- --reporter=verbose
  • pnpm test src/plugin-state/plugin-state-store.permissions.test.ts -- --reporter=verbose
  • pnpm tsgo:core
  • pnpm tsgo:core:test
  • pnpm plugin-sdk:api:check

What I checked:

Likely related people:

  • amknight: Authored the merged SQLite plugin state store work in feat(plugins): add SQLite plugin state store #74190, including the keyed-store contract, docs, tests, and API baseline, and this PR extends that same contract. (role: introduced plugin-state foundation and current API extender; confidence: high; commits: bbf985d50a64, 98a302e8b700, 555c832aba97; files: src/plugin-state/plugin-state-store.ts, src/plugin-state/plugin-state-store.sqlite.ts, src/plugin-state/plugin-state-store.types.ts)

Remaining risk / open question:

  • The PR overlaps with open feat(plugin-state): add atomic dedupe claims #77134, so maintainers should choose whether to land this narrow primitive first or fold it into the broader consumer migration work.
  • No local tests were executed in this read-only review; the author-reported focused checks and source review are the available validation evidence here.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 361737d1f1cb.

Re-review progress:

@amknight amknight merged commit fcb396b into main May 4, 2026
94 of 101 checks passed
@amknight amknight deleted the ak/plugin-state-claim-check branch May 4, 2026 08:20
arieldiego73 pushed a commit to arieldiego73/openclaw that referenced this pull request May 5, 2026
lxe pushed a commit to lxe/openclaw that referenced this pull request May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation maintainer Maintainer-authored PR plugin: file-transfer size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant