Skip to content

feat(plugins): accept slot owner records#87086

Closed
kklouzal wants to merge 53 commits into
openclaw:mainfrom
kklouzal:feat/plugin-slot-owner-records
Closed

feat(plugins): accept slot owner records#87086
kklouzal wants to merge 53 commits into
openclaw:mainfrom
kklouzal:feat/plugin-slot-owner-records

Conversation

@kklouzal

@kklouzal kklouzal commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements the read-side normalization layer proposed in #70823: plugins.slots.memory and plugins.slots.contextEngine can now be read as either a bare plugin id string or an object-form ownership record with an owner field.

What problem does this PR solve?

  • Today, OpenClaw slot ownership is represented only as a bare string such as plugins.slots.memory = "memory-core".
  • That makes ownership checks brittle: every runtime, doctor, validation, install/update, and plugin trust path has to assume the raw slot value is a string.
  • It also blocks the next step in RFC: machine-readable memory-slot ownership — plugins.slots.<slot>.owner #70823: recording slot ownership provenance without breaking existing configs.

What changes?

  • Adds PluginSlotOwnerRecord and PluginSlotValue config types.
  • Extends the config schema so plugins.slots.memory and plugins.slots.contextEngine accept either "plugin-id" or { "owner": "plugin-id", ...metadata }.
  • Adds resolvePluginSlotOwner(value) as the shared read-side helper for slot owner normalization.
  • Updates slot consumers across config normalization, validation, stale config scanning, doctor repair paths, startup plugin preloading, active runtime/status paths, context-engine resolution, memory dreaming, provider auth alias/env-var trust, uninstall cleanup, and plugin ID migration.
  • Preserves object-form metadata when plugin ID migration changes only the owner field.
  • Updates docs and focused regression coverage for object-form slot values.

What is intentionally out of scope?

Intended outcome:

  • Existing configs keep working unchanged.
  • New object-form slot records are accepted everywhere relevant on read.
  • Later PRs can extend this groundwork instead of reimplementing slot-owner parsing in each surface.

Linked context

Closes #70823.

Directly related work:

Supporting related PRs and issues:

Problem / Root Cause

Before this change, slot readers commonly assumed plugins.slots.<slot> was a string. That assumption is exactly what #70823 needs to retire.

The problem is not just schema shape. If only the schema accepts object-form records, but runtime paths keep reading raw values directly, then object-form configs fail in subtle places:

  • startup preloading may skip the selected plugin;
  • doctor and stale-config scans may miss or misreport owners;
  • context-engine resolution may fall back to legacy;
  • memory status/tool-disable paths may misread none;
  • provider auth alias/env-var trust may ignore a selected workspace context engine;
  • plugin update can collapse or lose ownership metadata.

This PR makes the read-side contract real by routing those consumers through one shared helper.

What Changed

  • src/config/types.plugins.ts

    • Adds PluginSlotOwnerRecord.
    • Adds PluginSlotValue = string | PluginSlotOwnerRecord.
    • Applies the value type to plugins.slots.memory and plugins.slots.contextEngine.
  • src/config/zod-schema.ts

    • Accepts string slot values and object values with required owner.
    • Allows additional object fields so future provenance fields can be carried without another schema turn.
  • src/plugins/slots.ts

    • Adds resolvePluginSlotOwner(value).
    • Normalizes strings and object-form owners to one scalar owner id.
    • Treats blank values as unset and none as the slot-disabled sentinel.
    • Uses the helper in exclusive slot switching so existing object-form ownership is recognized.
  • Runtime/config consumers now normalize slot owners before comparison:

    • config normalization
    • config validation
    • stale plugin config detection
    • doctor route and context-engine compatibility checks
    • gateway startup plugin id selection
    • context-engine resolution
    • memory dreaming config resolution
    • memory status and tool-disable logic
    • provider auth alias/env-var workspace trust checks
    • uninstall cleanup
    • plugin update ID migration
  • Documentation now states that slot values may be string ids or { owner: "<plugin-id>" } records.

Merge-Order Compatibility

This PR is deliberately a groundwork PR, not a competing architecture.

If this lands before #86210:

If #86210 lands before this:

  • This branch should rebase by applying the object-form slot value support to the new memory role keys as well as existing memory and contextEngine.
  • The core helper remains valid; it just gets called from more role-slot consumers.

If #83637 lands before or after this:

  • The two changes should stay complementary.
  • feat: add per-agent compaction overrides (AI-assisted) #83637 controls which agent scope supplies compaction/context-pruning settings.
  • This PR controls how global plugin-slot owners are represented and normalized on read.
  • Later compaction/context-engine work should consume normalized contextEngine ownership where it reads that slot, without changing per-agent compaction semantics.

Follow-up intended after this PR:

  • Write-side canonicalization to produce object-form owner records.
  • Slot overwrite invariants and explicit force/override behavior.
  • Plugin-side adoption of the normalized owner abstraction.

Real behavior proof

  • Behavior or issue addressed: object-form plugins.slots.memory and plugins.slots.contextEngine records should behave the same as bare string slot owners across config parsing, validation, doctor checks, startup planning, runtime memory/context-engine resolution, provider auth trust checks, plugin lifecycle cleanup, and plugin ID migration.
  • Real environment tested: Dev-01 source checkout at /home/kklouzal/.openclaw/workspace/repos/openclaw; branch feat/plugin-slot-owner-records; head 27585fb987df8d6c68886566a6280e755a507456; local Node/Vitest/OpenClaw repo toolchain.
  • Exact steps or command run after this patch:
npx vitest run src/plugins/config-state.test.ts src/plugins/slots.test.ts src/config/config-misc.test.ts src/config/config.plugin-validation.test.ts src/agents/provider-auth-aliases.test.ts src/commands/doctor/shared/context-engine-host-compat.test.ts src/commands/status.scan.shared.test.ts src/context-engine/context-engine.test.ts src/gateway/tools-invoke-shared.test.ts src/memory-host-sdk/dreaming.test.ts src/secrets/provider-env-vars.dynamic.test.ts
npx vitest run src/plugins/update.test.ts
node --import tsx scripts/generate-config-doc-baseline.ts --check
node --import tsx scripts/generate-base-config-schema.ts --check
npx oxfmt --check <touched files>
git diff --check
node scripts/run-tsgo.mjs -p tsconfig.core.json
  • Evidence after fix:
Test Files 11 passed (11)
Tests 301 passed (301)

Test Files 1 passed (1)
Tests 80 passed (80)

OK docs/.generated/config-baseline.sha256
[base-config-schema] ok

All matched files use the correct format.
git diff --check passed
tsgo core check exitCode=0
  • Observed result after fix: object-form slot owners are accepted by schema, normalize to the same owner ids as string slots, preserve none behavior, participate in context-engine and memory runtime resolution, validate missing plugin references correctly, trust selected workspace context-engine plugins for provider auth only when the normalized owner matches, reset/update selected slots during uninstall/update, and preserve extra object metadata when plugin ID migration only changes owner.
  • What was not tested: live production gateway behavior, write-side object canonicalization, overwrite enforcement, or future feat(memory): add multi-slot memory role architecture #86210 memory role slots. Those are out of scope for this read-side normalization PR.
  • Proof limitations or environment constraints: this is a config/runtime parsing and normalization groundwork PR, so the proof is focused local deterministic execution rather than a live model/channel proof. No production config, gateway restart, external provider account, or live plugin registry state was mutated.
  • Before evidence: RFC: machine-readable memory-slot ownership — plugins.slots.<slot>.owner #70823 documents the pre-change limitation: slot ownership was only machine-readable through direct string comparison, leaving no durable place for ownership provenance and forcing each consumer/plugin to repeat brittle raw-slot checks.

Tests and validation

Commands run:

npx vitest run src/plugins/config-state.test.ts src/plugins/slots.test.ts src/config/config-misc.test.ts src/config/config.plugin-validation.test.ts src/agents/provider-auth-aliases.test.ts src/commands/doctor/shared/context-engine-host-compat.test.ts src/commands/status.scan.shared.test.ts src/context-engine/context-engine.test.ts src/gateway/tools-invoke-shared.test.ts src/memory-host-sdk/dreaming.test.ts src/secrets/provider-env-vars.dynamic.test.ts
npx vitest run src/plugins/update.test.ts
node --import tsx scripts/generate-config-doc-baseline.ts --check
node --import tsx scripts/generate-base-config-schema.ts --check
npx oxfmt --check --threads=1 <touched files>
git diff --check
node scripts/run-tsgo.mjs -p tsconfig.core.json

Regression coverage added or updated:

  • Slot normalization for string owners, object owners, blank values, and none.
  • Config schema acceptance for object-form memory and contextEngine.
  • Config validation diagnostics for missing plugin references inside object-form slot owners.
  • Exclusive slot selection when the previous owner is object-form.
  • Context-engine resolution for object-form contextEngine.
  • Doctor host compatibility and route-warning paths with object-form context-engine owners.
  • Memory status/tool-disable/dreaming paths with object-form memory.
  • Provider auth alias/env-var trust checks for selected workspace context engines represented as owner records.
  • Plugin update migration preserving object-form metadata while changing the owner.

Compatibility and Risk

Backward compatibility:

  • Existing string slot configs remain valid.
  • Existing "none" memory-slot behavior remains valid.
  • Existing default behavior remains unchanged: memory defaults to memory-core; context engine defaults to legacy.
  • Existing write paths that still write strings remain valid.

Compatibility changes reviewers should inspect:

  • Config schema now accepts object records for existing slot values.
  • Consumers that previously compared raw slot strings now compare normalized owners.
  • Plugin update migration now preserves object-form slot metadata where possible.

Primary risks:

Risk mitigation:

  • The branch adds one shared resolver rather than multiple ad hoc conversions.
  • Focused tests cover schema, normalization, runtime consumers, doctor paths, provider auth trust, status/tool behavior, and lifecycle migration.
  • This PR avoids write-side canonicalization/enforcement, keeping the blast radius to read compatibility.

Current Branch State

  • Base branch: main
  • Head branch: feat/plugin-slot-owner-records
  • Current head: 27585fb987df8d6c68886566a6280e755a507456
  • Commit: feat(plugins): accept slot owner records
  • Worktree at draft time: clean, branch ahead of origin/main by 1
  • Push/PR creation: not done yet

Current Review State

Next action:

  • Review this PR body draft.
  • Push feat/plugin-slot-owner-records after approval.
  • Open the PR against openclaw/openclaw:main.

Still waiting on:

  • Schwi approval of the PR body.
  • Push authorization.
  • GitHub Actions after PR creation.

@kklouzal
kklouzal requested a review from a team as a code owner May 27, 2026 00:07
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime commands Command implementations agents Agent runtime and tooling size: M proof: supplied External PR includes structured after-fix real behavior proof. labels May 27, 2026
@clawsweeper

clawsweeper Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution. I reviewed the branch, and this PR is not a good landing base for OpenClaw.

This branch should not remain open as a landing candidate: the slot-owner normalization idea is useful, but the live PR surface is dirty/unmergeable and includes unrelated release, dependency, workflow, and broad runtime churn that would require rebuilding or cherry-picking the useful work onto a fresh branch.

So I’m closing this PR rather than keeping an unmergeable branch open. A new narrow PR that carries only the useful part is welcome.

Review details

Best possible solution:

Close this branch as a landing candidate and accept a fresh narrow PR, or maintainer cherry-pick, that contains only the slot-owner schema/helper/consumer/docs/tests with real behavior proof and no dependency, workflow, changelog, or release-version churn.

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

Not applicable as a bug reproduction: this is a new config/API shape, and current main source confirms the existing slot contract is string-only.

Is this the best way to solve the issue?

No; the narrow slot-owner design may be reasonable, but this branch is not the best way to solve it because the landing surface is polluted by unrelated release, dependency, workflow, and runtime changes.

Security review:

Security review needs attention: The branch introduces concrete security and supply-chain review blockers through dependency graph, shrinkwrap/lockfile, and CodeQL workflow changes unrelated to the stated feature.

  • [high] Blocked dependency graph changes — package.json:1848
    The PR changes package manifests and lock/shrinkwrap files from an external branch without the security authorization required by the repository dependency guard.
    Confidence: 0.95
  • [medium] Security workflow changes mixed into feature branch — .github/workflows/codeql-critical-quality.yml:431
    The diff changes the critical CodeQL workflow and network-boundary analysis path, which can alter security coverage independently of the plugin slot owner feature.
    Confidence: 0.86

AGENTS.md: found and applied where relevant.

What I checked:

  • Root repository policy read: The full root policy was read from disk; it treats plugin APIs, config loading/defaults, provider routing, fallback behavior, lockfiles, shrinkwrap, and release-owned changelog edits as compatibility or security-sensitive review surfaces. (AGENTS.md:1, 9a00d740447b)
  • Scoped plugin policy read: The plugin boundary guide requires manifest-first behavior, public SDK/docs alignment for plugin-author changes, and no private bundled-only seams; this applies to the proposed slot owner config contract. (src/plugins/AGENTS.md:1, 9a00d740447b)
  • Current main still has string-only slot types: Current main defines PluginSlotsConfig.memory and contextEngine as strings, so the central feature is not already implemented on main. (src/config/types.plugins.ts:41, 9a00d740447b)
  • Current main schema still accepts only string slots: Current main's Zod schema keeps both slot fields as z.string().optional(), confirming object-form slot records remain a real proposed change rather than an obsolete duplicate. (src/config/zod-schema.ts:1254, 9a00d740447b)
  • Useful slot-owner work is much narrower than the PR surface: The contributor's slot-owner commits after the v2026.5.28 release tag touch 34 files with 451 additions and 89 deletions, which is a salvageable but separate narrow change set. (8fc80111602c)
  • Live PR surface is not a clean landing candidate: The provided GitHub context reports the PR as mergeable=false, mergeableState=dirty, 195 changed files, dependency-graph guard comments, and broad labels across channels, web UI, gateway, CLI, scripts, Docker, agents, and multiple plugins. (8fc80111602c)

Likely related people:

  • fuller-stack-dev: Authored the merged multi-kind plugin slot ownership work that established the current src/plugins/slots.ts helper surface this PR extends. (role: introduced related slot behavior; confidence: high; commits: 235908c30e1b; files: src/plugins/slots.ts, src/config/validation.ts)
  • hclsys: Authored the merged context-engine slot preservation fix, which is directly adjacent to the normalization behavior this PR changes. (role: adjacent slot normalization contributor; confidence: high; commits: 8a28a3b056e1; files: src/plugins/config-normalization-shared.ts, src/plugins/config-state.test.ts)
  • vincentkoc: Related merged plugin install slot-selection work is cited by the PR body and is relevant to deciding how slot ownership should be updated during install/enable flows. (role: adjacent install-slot contributor; confidence: medium; commits: 70095f08f471; files: src/cli/plugins-command-helpers.ts, src/plugins/slots.ts)
  • steipete: Recent main history shows repeated config/plugin/release work in the affected areas, including release and config refactors that overlap the branch's noisy surfaces. (role: recent area contributor; confidence: medium; commits: 00d8d7ead0, 4c33aaa86c, e93216080a; files: src/plugins, src/config, CHANGELOG.md)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9a00d740447b.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels May 27, 2026
@clawsweeper

clawsweeper Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@kklouzal
kklouzal force-pushed the feat/plugin-slot-owner-records branch from 27585fb to e08bd34 Compare May 27, 2026 02:10
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels May 27, 2026
@kklouzal
kklouzal force-pushed the feat/plugin-slot-owner-records branch from 027eef9 to 4c28504 Compare May 27, 2026 03:06
@openclaw-barnacle openclaw-barnacle Bot added the cli CLI command changes label May 27, 2026
@kklouzal
kklouzal force-pushed the feat/plugin-slot-owner-records branch from 4c28504 to ef64e0f Compare May 27, 2026 03:44
@openclaw-barnacle openclaw-barnacle Bot added the extensions: memory-core Extension: memory-core label May 27, 2026
@kklouzal
kklouzal force-pushed the feat/plugin-slot-owner-records branch 2 times, most recently from 14f164b to 4e91055 Compare May 27, 2026 17:04
@openclaw-barnacle openclaw-barnacle Bot removed the extensions: memory-core Extension: memory-core label May 27, 2026
@kklouzal
kklouzal force-pushed the feat/plugin-slot-owner-records branch 4 times, most recently from 4bda98d to 0a436b0 Compare May 27, 2026 22:56
steipete and others added 25 commits May 30, 2026 07:48
Resolve raw plugin config environment references before plugin discovery and validation, while preserving the existing single-pass behavior for configs already loaded through config IO.

The loader now resolves raw config opt-ins with config.env vars included, bypasses active/cache reuse for that mode, and redacts plugin entry config from raw-mode cache keys so resolved secrets do not enter registry keys or reentry errors.

Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/plugins/loader.test.ts src/plugins/loader.runtime-registry.test.ts
- autoreview --mode branch --base origin/main
- pnpm check:changed on Blacksmith Testbox tbx_01ksw36bp7zygwxgq3jcsvjv3b / GitHub Actions run 26680322889
- PR CI green on facb776

Co-authored-by: Peter Lindsey <[email protected]>
(cherry picked from commit 6b41a06)
Keep session lock cleanup from removing live OpenClaw-owned locks solely because they are old. Cleanup now reports age-only stale locks without deleting them, while still removing dead, orphaned, recycled, malformed-old, and non-OpenClaw-owned locks.

Update doctor docs and regression coverage for the cleanup/repair contract.

Refs openclaw#87779
Fixes openclaw#87438.

Bound unset heartbeat run timeouts so background heartbeat turns no longer inherit the built-in 48-hour interactive agent default. Timeout precedence is explicit heartbeat timeout, explicit global agent timeout, then heartbeat cadence capped at 600 seconds.

Verification:
- git diff --check
- Testbox tbx_01kstna69zvznn4fq7zrqr04a1: corepack pnpm test src/infra/heartbeat-runner.model-override.test.ts -- --reporter=verbose passed 13 tests
- Direct node --import tsx runtime probe verified 300s, 600s, 60s, and 45s timeout precedence cases
- Autoreview clean

Known CI state:
- PR CI run 26661465248 has failures matching latest main CI run 26661386468 at a7820b2; failures are outside this six-file heartbeat/docs diff.
…w#84535)

* fix(gateway): resolve message action config from runtime snapshot

* fix(gateway): preserve runtime config matching through auto-enable

* fix(gateway): preserve auto-enabled message action fallback

* fix(gateway): use canonical runtime snapshot for message actions

* fix(discord): route credential actions through gateway

---------

Co-authored-by: Merlin <[email protected]>
Co-authored-by: joshavant <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Guard

This PR changes dependency-related files. Maintainers should confirm these changes are intentional.

Changed files:

  • extensions/copilot/package.json
  • extensions/feishu/npm-shrinkwrap.json
  • extensions/feishu/package.json
  • extensions/workboard/package.json
  • npm-shrinkwrap.json
  • package.json
  • packages/net-policy/package.json
  • packages/sdk/package.json
  • pnpm-lock.yaml
  • pnpm-workspace.yaml

Maintainer follow-up:

  • Review whether the dependency changes are intentional.
  • Inspect resolved package deltas when lockfile, shrinkwrap, or workspace dependency policy changes are present.
  • Treat package-lock.json and npm-shrinkwrap.json diffs as security-review surfaces.
  • Run pnpm deps:changes:report -- --base-ref origin/main --markdown /tmp/dependency-changes.md --json /tmp/dependency-changes.json locally for detailed release-style evidence.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency graph changes are blocked

OpenClaw does not accept dependency graph changes through PRs unless security explicitly authorizes the current head SHA. Dependency updates are generated internally by maintainers so external PRs cannot change the resolved graph.

Detected dependency graph changes:

  • extensions/feishu/npm-shrinkwrap.json changed.
  • npm-shrinkwrap.json changed.
  • pnpm-lock.yaml changed.
  • extensions/copilot/package.json changed version.
  • extensions/feishu/package.json changed peerDependencies, version.
  • extensions/workboard/package.json changed peerDependencies, version.
  • package.json changed dependencies, version.
  • packages/net-policy/package.json changed dependencies, name, version.
  • packages/sdk/package.json changed dependencies.

Auto-scrub was not attempted because this PR changes package manifest dependency graph fields:

  • extensions/copilot/package.json changed version.
  • extensions/feishu/package.json changed peerDependencies, version.
  • extensions/workboard/package.json changed peerDependencies, version.
  • package.json changed dependencies, version.
  • packages/net-policy/package.json changed dependencies, name, version.
  • packages/sdk/package.json changed dependencies.

Dependency graph changes must be reviewed by security or handled by maintainers internally. Please remove lockfile changes manually if they are not needed.

To remove lockfile changes, restore them from the target branch:

git fetch origin
git checkout 'origin/main' -- 'extensions/feishu/npm-shrinkwrap.json' 'npm-shrinkwrap.json' 'pnpm-lock.yaml'
git commit -m 'chore: remove dependency lockfile change'
git push

If this PR intentionally needs a dependency graph change, ask a member of @openclaw/openclaw-secops to comment:

/allow-dependencies-change

The action will approve the current head SHA (8fc80111602cb1a3c0f007cf5ac9378b4ebfb450) when it reruns. A later push requires a fresh approval.

@clawsweeper

clawsweeper Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@kklouzal

kklouzal commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

Closing this stale release-base PR. The branch is being kept in the fork for local release-line testing, and any upstream work will be reopened later as a fresh, clean PR.

#88507

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

Labels

agents Agent runtime and tooling app: web-ui App: web-ui channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: imessage Channel integration: imessage channel: telegram Channel integration: telegram channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo cli CLI command changes commands Command implementations dependencies-changed PR changes dependency-related files docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: acpx extensions: codex extensions: codex-supervisor Extension: codex-supervisor extensions: copilot extensions: xiaomi gateway Gateway runtime merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. plugin: workboard proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. scripts Repository scripts size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: machine-readable memory-slot ownership — plugins.slots.<slot>.owner