Skip to content

fix(status): distinguish runtime-loaded plugins from installed inventory#97479

Merged
vincentkoc merged 2 commits into
openclaw:mainfrom
masatohoshino:fix/status-loaded-vs-installed
Jun 28, 2026
Merged

fix(status): distinguish runtime-loaded plugins from installed inventory#97479
vincentkoc merged 2 commits into
openclaw:mainfrom
masatohoshino:fix/status-loaded-vs-installed

Conversation

@masatohoshino

@masatohoshino masatohoshino commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

/status plugins could list disk-discovered / config-enabled plugins under Loaded: even when
they were not present in any live runtime registry surface. The detailed view merges an
installed/disk-scan plugin report with the runtime registry, and the disk scan marks every
config-enabled discoverable plugin "loaded" from configuration alone — before any runtime load. So
a plugin that is installed and enabled in config but not actually running showed up under
Loaded:, misleadingly answering the operator question "are the plugins I configured actually
running?"

Why This Change Was Made

The gateway intentionally loads only the plugins its startup plan requires, so
installed-but-not-running is a normal healthy state, not an error. This records the plugin ids
confirmed loaded across all live runtime registry surfaces — active plus any pinned
channel / http-route / session-extension registry (via the canonical collectLivePluginRegistries()
set) — and renders Loaded: from that set directly. A plugin live only through a pinned surface
still counts as loaded. Installed/discovered plugins that are not active are shown as a separate,
neutral Installed (not active) inventory line. When a snapshot carries no runtime provenance (the
compact path), rendering falls back to the previous behavior. Observer/status only: no
repair/retry/reload/install, no config write, no startup or doctor behavior change, and no new
diagnostics or errors for inactive inventory.

User Impact

openclaw operators reading /status plugins now get a truthful split: Loaded: means
"running in a live runtime registry," and installed-but-not-active plugins are listed separately as
inventory instead of being counted as loaded. Errors, disabled plugins, dependency issues, channel
failures, diagnostics, compatibility notices, and the compact status line are unchanged.

Evidence

Actual OpenClaw runtime / terminal proof — no mocked disk-scan or channel-resolution I/O.
On the final PR HEAD (ae8289d), in an isolated OPENCLAW_STATE_DIR, one real bundled plugin
(diffs) was enabled and loaded through the production plugin loader (loadOpenClawPlugins, real
on-disk scan + activation). The real /status plugins handler (buildStatusPluginsReply
collectInstalledPluginHealthSnapshotformatDetailedPluginHealth) then produced:

🔌 Plugins: OK
Loaded: 1 (diffs)
Disabled: 133
Installed (not active): 1 (memory-core)
Full inventory: /plugins list

diffs is genuinely runtime-loaded; memory-core is installed/discovered (found by the real disk
scan) but not active in any live runtime registry. The compact line stays 🔌 Plugins: OK, and no
Errors: / Diagnostics: are emitted for the inactive inventory. Nothing was mocked — the disk
scan and channel resolution ran for real.

Supporting (deterministic, same production collection path). Before vs after this change for an
installed-but-not-running plugin, via collectInstalledPluginHealthSnapshot on the final HEAD:

=== BEFORE (main) ===
🔌 Plugins: OK
Loaded: 2 (installed-idle, runtime-ok)
Disabled: 0

=== AFTER (this PR) ===
🔌 Plugins: OK
Loaded: 1 (runtime-ok)
Disabled: 0
Installed (not active): 1 (installed-idle)

Tests (src/status/status-plugin-health.test.ts, src/status/status-plugin-health.runtime.test.ts,
src/plugins/active-runtime-registry.test.ts): runtime-loaded vs installed-not-active separation,
the field-absent fallback, the installed+runtime merge, per-surface pinned-registry divergence
(channel / http-route / session-extension), cross-surface dedup, and the runtime-loaded-but-absent-
from-records render case. 25 status + 6 active-runtime-registry tests pass; typecheck (tsgo core +
core-tests) clean.

AI-assisted.

masatohoshino and others added 2 commits June 28, 2026 20:00
/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 8:11 AM ET / 12:11 UTC.

Summary
This PR adds runtime-loaded plugin provenance to status snapshots and formats /status plugins so runtime-loaded plugins and installed-but-inactive inventory are reported separately.

PR surface: Source +58, Tests +158. Total +216 across 5 files.

Reproducibility: yes. from source inspection: current main marks enabled installed inventory as loaded, merges it with runtime state, and detailed status counts every merged loaded record under Loaded:. I did not run a live setup in this read-only review.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • No automated repair is needed; the PR is clean and the remaining action is maintainer review and merge gating.

Security
Cleared: The diff changes TypeScript status/plugin runtime code and tests only; it adds no dependencies, workflows, package scripts, secret handling, or external code execution paths.

Review details

Best possible solution:

Land the status-only provenance fix after ordinary maintainer review, keeping Loaded: sourced from live runtime registries and installed inventory as neutral detail.

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

Yes, from source inspection: current main marks enabled installed inventory as loaded, merges it with runtime state, and detailed status counts every merged loaded record under Loaded:. I did not run a live setup in this read-only review.

Is this the best way to solve the issue?

Yes, this appears to be the best fix shape: it records runtime provenance at the status snapshot boundary and reuses the existing live-registry surface set instead of changing installed inventory semantics or eagerly loading plugins.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 08d15ec32db3.

Label changes

Label justifications:

  • P2: This is a normal-priority user-facing status correctness fix with limited runtime blast radius and no evidence of broken plugin execution.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): Sufficient: the PR body includes terminal output from an isolated real OpenClaw runtime showing diffs as loaded and memory-core as installed not active after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. Sufficient: the PR body includes terminal output from an isolated real OpenClaw runtime showing diffs as loaded and memory-core as installed not active after the fix.
Evidence reviewed

PR surface:

Source +58, Tests +158. Total +216 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 3 63 5 +58
Tests 2 159 1 +158
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 222 6 +216

What I checked:

  • Root policy read: Read the full root AGENTS.md; its ClawSweeper review policy applies because this touches plugin runtime/status boundaries and requires whole-path and best-fix review. (AGENTS.md, 08d15ec32db3)
  • Scoped plugin policy read: Read src/plugins/AGENTS.md; the relevant boundary is keeping discovery/inventory separate from runtime execution and reusing lifecycle-owned runtime facts. (src/plugins/AGENTS.md:1, 08d15ec32db3)
  • Current main installed inventory source: Current main builds installed-index plugin records with status: plugin.enabled ? "loaded" : "disabled", so disk/config-enabled inventory can look loaded before runtime activation. (src/plugins/status-snapshot.ts:91, 08d15ec32db3)
  • Current main detailed status rendering: Current main formats detailed Loaded: by filtering every merged plugin record with status === "loaded", which explains the misleading operator output. (src/status/status-plugin-health.ts:237, 08d15ec32db3)
  • User-visible entry point: The /status plugins reply calls collectInstalledPluginHealthSnapshot and then formatDetailedPluginHealth, matching the PR's affected output path. (src/auto-reply/reply/commands-status.ts:52, 08d15ec32db3)
  • Existing live registry surface: Current main already has collectLivePluginRegistries() for active and pinned http-route/channel/session-extension registries, making it the right source for runtime-loaded provenance. (src/plugins/runtime.ts:134, 08d15ec32db3)

Likely related people:

  • giodl73-repo: Current-main blame for the formatter, installed snapshot conversion, and live registry helper points to the merged structured Doctor/status follow-up that carried this code forward. (role: recent area contributor; confidence: high; commits: cb4244fe15e3; files: src/status/status-plugin-health.ts, src/plugins/status-snapshot.ts, src/plugins/runtime.ts)
  • jalehman: The merged plugin health status PR introduced the detailed /status plugins surface, formatter, runtime snapshot path, and slash-command wiring that this PR refines. (role: feature introducer; confidence: high; commits: 10a4c7c10b6a; files: src/status/status-plugin-health.ts, src/status/status-plugin-health.runtime.ts, src/auto-reply/reply/commands-status.ts)
  • steipete: The pinned plugin HTTP-route registry work is directly related to one of the live runtime surfaces that this PR now counts as loaded. (role: adjacent registry-surface contributor; confidence: medium; commits: a69f6190ab56; files: src/plugins/runtime.ts, src/gateway/server/plugins-http.ts)
  • vincentkoc: Git history shows prior work on imported runtime state and lazy runtime registry behavior near the runtime-loaded status surfaces used by this PR. (role: adjacent plugin runtime contributor; confidence: medium; commits: def5b954a869, 6451beddb2e4; files: src/plugins/runtime.ts, src/plugins/status.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@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. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed 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. labels Jun 28, 2026
@vincentkoc
vincentkoc merged commit 949b1af into openclaw:main Jun 28, 2026
156 of 164 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 29, 2026
…ory (openclaw#97479)

* fix(status): distinguish runtime-loaded plugins from installed inventory

/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(status): include pinned runtime registries in loaded ids

Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…ory (openclaw#97479)

* fix(status): distinguish runtime-loaded plugins from installed inventory

/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(status): include pinned runtime registries in loaded ids

Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
@masatohoshino
masatohoshino deleted the fix/status-loaded-vs-installed branch July 1, 2026 14:52
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
…ory (openclaw#97479)

* fix(status): distinguish runtime-loaded plugins from installed inventory

/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(status): include pinned runtime registries in loaded ids

Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 2, 2026
…ory (openclaw#97479)

* fix(status): distinguish runtime-loaded plugins from installed inventory

/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(status): include pinned runtime registries in loaded ids

Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
(cherry picked from commit 949b1af)
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
…ory (openclaw#97479)

* fix(status): distinguish runtime-loaded plugins from installed inventory

/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(status): include pinned runtime registries in loaded ids

Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants