Skip to content

fix(plugins): forward install records to channel catalog registry#77269

Merged
odysseus0 merged 5 commits intoopenclaw:mainfrom
pumpkinxing1:fix/channel-catalog-include-install-records
May 5, 2026
Merged

fix(plugins): forward install records to channel catalog registry#77269
odysseus0 merged 5 commits intoopenclaw:mainfrom
pumpkinxing1:fix/channel-catalog-include-install-records

Conversation

@pumpkinxing1
Copy link
Copy Markdown
Contributor

@pumpkinxing1 pumpkinxing1 commented May 4, 2026

Summary

  • Problem: listChannelCatalogEntries in src/plugins/channel-catalog-registry.ts calls discoverOpenClawPlugins without forwarding installRecords. As a result, channels installed via openclaw plugins install <npm-spec> are absent from the CLI channel catalog—openclaw channels add --channel <id> and openclaw channels login --channel <id> report Unsupported channel: <id> / Unknown channel: <id> even though the ledger entry under ~/.openclaw/plugins/installs.json is healthy.
  • Why it matters: Every third-party npm-installed channel plugin is unreachable through the channels CLI surface. Bundled plugins (qqbot, telegram, slack, etc.) take the stock discovery path and were unaffected, which masked the regression.
  • What changed: listChannelCatalogEntries now lazy-loads the persisted plugin install ledger via loadInstalledPluginIndexInstallRecordsSync when the caller does not specify origin === "bundled", and forwards the records to discovery. Bundled-only callers continue to skip the disk read. Callers that already hold a record map can pass it via the new optional installRecords parameter to skip the lazy load. Reader errors fall back silently to "no install records" (no behavioural regression).
  • What did NOT change (scope boundary): Other discoverOpenClawPlugins consumers (bundled-capability-runtime.ts, bundled-sources.ts, config-contracts.ts) are untouched—they target bundled-only paths or pre-load records elsewhere. No public manifest/contract types changed. manifest-registry.ts, loader.ts, and installed-plugin-index-registry.ts were already forwarding installRecords correctly; this PR only reaches feature parity for the channel-catalog-registry path.

This PR also registers `@tencent-weixin/openclaw-weixin` in `scripts/lib/official-external-channel-catalog.json` so the channel appears in onboarding flows that consult the catalog directly (mirroring the WeCom and Yuanbao entries).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra (catalog entry addition)

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

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause

  • Root cause: `listChannelCatalogEntries` predates the move to the persisted install ledger as the canonical source for npm-installed plugin discovery (see `installed-plugin-index-registry.ts`, `manifest-registry.ts`, and `loader.ts`, which all forward `installRecords` to `discoverOpenClawPlugins`). The catalog-registry consumer was missed during that migration.
  • Missing detection / guardrail: No test asserted that `listChannelCatalogEntries` could see npm-installed plugins. The closest existing coverage (`bundled-channel-catalog-read.fail-soft.test.ts`) only exercises the bundled soft-fail path; the `discoverOpenClawPlugins` tests don't reach this consumer.
  • Contributing context: The CLI surface (`channels add`, `channels login`) reaches `listChannelCatalogEntries` indirectly through `listChannelPluginCatalogEntries` in `src/channels/plugins/catalog.ts`. The bundled discovery path stays green even with this bug, so bundled channel tests didn't catch it.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: `src/plugins/channel-catalog-registry.test.ts` (new)
  • Scenario the test should lock in: When `origin !== "bundled"`, `listChannelCatalogEntries` MUST forward install records to `discoverOpenClawPlugins`. When `origin === "bundled"`, the ledger MUST NOT be read. Caller-supplied `installRecords` MUST replace the lazy load. Empty ledgers and reader errors MUST omit `installRecords` from the discovery call without throwing.
  • Why this is the smallest reliable guardrail: The bug was a silently-omitted argument; the test mocks both `discoverOpenClawPlugins` and the ledger reader, then asserts the call signature, so any future regression that drops the parameter again surfaces immediately.
  • Existing test that already covers this: None.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • `openclaw channels add --channel ` and `openclaw channels login --channel ` now resolve npm-installed channel plugins recorded in `~/.openclaw/plugins/installs.json`. Previously they returned `Unsupported channel: ` / `Unknown channel: ` for any third-party plugin.
  • `@tencent-weixin/openclaw-weixin` appears in the official external channel catalog (`source: "external"`, `kind: "channel"`).

Diagram

```text
Before:
channels add/login → listChannelCatalogEntries → discoverOpenClawPlugins({ workspaceDir, env })
^ no installRecords; npm-installed plugins invisible to catalog

After:
channels add/login → listChannelCatalogEntries
├─ if origin !== "bundled": loadInstalledPluginIndexInstallRecordsSync()
└─ discoverOpenClawPlugins({ workspaceDir, env, installRecords })
^ npm-installed plugins discoverable
```

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No (`~/.openclaw/plugins/installs.json` was already read by sibling discovery callers)
  • If any `Yes`, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux (TencentOS, x64)
  • Runtime/container: Node v22.x, host source checkout via `pnpm install && pnpm start`
  • Model/provider: N/A
  • Integration/channel: `openclaw-weixin`
  • Relevant config (redacted): `OPENCLAW_STATE_DIR=/tmp/openclaw-fix-verify`

Steps

  1. `pnpm install`
  2. `OPENCLAW_STATE_DIR=/tmp/openclaw-fix-verify pnpm start plugins install @tencent-weixin/openclaw-weixin`
  3. `OPENCLAW_STATE_DIR=/tmp/openclaw-fix-verify pnpm start channels add --channel openclaw-weixin --account default ...`

Expected

  • Channel resolves; the per-channel `applyAccountConfig` flow runs.

Actual

  • Before fix: `Unknown channel: openclaw-weixin`.
  • After fix: channel resolves and the add flow proceeds.

Evidence

  • Failing test/log before + passing after — 5 unit cases in `src/plugins/channel-catalog-registry.test.ts` (all pass via `npx vitest run src/plugins/channel-catalog-registry.test.ts`).
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification

  • Verified scenarios:
    • Local source checkout on Linux. Installed `@tencent-weixin/[email protected]` against an isolated `OPENCLAW_STATE_DIR`; ledger entry verified; `pnpm start channels add --channel openclaw-weixin` now resolves the channel through the patched catalog path.
    • 5/5 unit tests in the new `channel-catalog-registry.test.ts` cover lazy-load, bundled-skip, caller-supplied, empty-ledger, and reader-error paths.
  • Edge cases checked:
    • Bundled-only callers continue to skip the ledger read (no extra disk I/O).
    • Reader failure does not throw — silently falls back to no `installRecords`.
  • What I did NOT verify:
    • Non-Linux environments (macOS, Windows).
    • End-to-end UX in onboarding/control-ui surfaces (beyond the channels CLI).

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
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: `loadInstalledPluginIndexInstallRecordsSync` performs a synchronous disk read on every catalog list call when `origin` is unspecified. This is consistent with sibling discovery consumers (`installed-plugin-index-registry.ts`, `loader.ts`, etc.) but adds one ledger read on hot CLI paths like `channels add`.
    • Mitigation: Bundled-only callers skip the read. Callers that already loaded records can pass `installRecords` to avoid the duplicate read.
  • Risk: The new catalog entry intentionally omits `expectedIntegrity` (`npmSpec` is unpinned, matching the `@openclaw/*` entries). External-source entries like `wecom` and `yuanbao` are version-pinned.
    • Mitigation: Treat the entry as discovery metadata only; install integrity is enforced by the install ledger, not the catalog file. We can pin the spec + integrity in a follow-up once a stable release line is in place.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: S labels May 4, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 4, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR forwards installed plugin ledger records into channel catalog discovery, adds Weixin external catalog metadata and changelog entries, and updates focused regression/test expectations.

Reproducibility: yes. by source inspection. Current main omits installRecords in the channel catalog call, discovery only scans persisted npm install paths when those records are supplied, and the channels add/login catalog path reaches that function.

Real behavior proof
Needs stronger real behavior proof before merge: The PR body describes a Linux verification but does not include terminal/log output or a linked artifact, and the current Real behavior proof check failed.

Next step before merge
This needs contributor or maintainer proof plus mergeability handling rather than an automated code repair.

Security
Cleared: The diff uses an existing read-only ledger reader and pinned external catalog metadata; I found no concrete workflow, secret, lifecycle-script, or package-resolution broadening concern.

Review details

Best possible solution:

Land the narrow ledger-forwarding and catalog metadata patch after accepted real behavior proof is attached, the branch is mergeable, and targeted plus changed-lane checks are green.

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

Yes, by source inspection. Current main omits installRecords in the channel catalog call, discovery only scans persisted npm install paths when those records are supplied, and the channels add/login catalog path reaches that function.

Is this the best way to solve the issue?

Yes for the code direction: forwarding the existing read-only ledger records in this catalog path is the narrow maintainable fix. The PR is not merge-ready until real behavior proof and mergeability are resolved.

Acceptance criteria:

  • pnpm test src/plugins/channel-catalog-registry.test.ts
  • pnpm test test/official-channel-catalog.test.ts src/commands/doctor/shared/stale-plugin-config.test.ts src/plugins/bundled-plugin-metadata.test.ts
  • pnpm check:changed

What I checked:

  • Current main omits install records: listChannelCatalogEntries calls discoverOpenClawPlugins with workspaceDir and env only, so persisted npm install records are not visible in this catalog path. (src/plugins/channel-catalog-registry.ts:26, 7188e4f4ad87)
  • Discovery depends on installRecords: Plugin discovery only follows persisted installed-plugin paths through collectInstalledPluginRecordPaths(params.installRecords, env). (src/plugins/discovery.ts:1156, 7188e4f4ad87)
  • Channels CLI catalog reaches the affected path: listChannelPluginCatalogEntries builds channel setup catalog entries from listChannelCatalogEntries, matching the reported add/login surface. (src/channels/plugins/catalog.ts:407, 7188e4f4ad87)
  • PR forwards ledger records: The PR resolves install records, skips lazy loading for bundled-only calls, and forwards non-empty records into discovery. (src/plugins/channel-catalog-registry.ts:36, 60188c7fae84)
  • Regression coverage targets the omitted argument: The new test covers lazy ledger loading, bundled-origin skip, caller-supplied records, empty ledgers, and reader-error fallback. (src/plugins/channel-catalog-registry.test.ts:53, 60188c7fae84)
  • Real behavior proof gate is failing: The PR body describes a Linux verification, but the current head has a failed Real behavior proof check and no copied terminal/log output or linked artifact in the provided discussion. (60188c7fae84)

Likely related people:

  • vincentkoc: Recent channel/plugin catalog work includes loading third-party official channel packages, externalized npm install catalog behavior, and external catalog contract metadata around the same catalog surface. (role: adjacent owner; confidence: high; commits: 4781b46056e8, d4268b1b2b74, b5affa64b3bb; files: src/channels/plugins/catalog.ts, scripts/lib/official-external-channel-catalog.json)
  • steipete: Recent work touches the installed plugin ledger reader/recovery and channel catalog boundary cleanup that this PR reuses. (role: recent maintainer; confidence: high; commits: 4047f4d0b420, 181a50e14659, 4ec1efbcbcd7; files: src/plugins/installed-plugin-index-record-reader.ts, src/plugins/channel-catalog-registry.ts, scripts/lib/official-external-channel-catalog.json)
  • shakkernerd: The split installed-plugin index record reader and manifest-missing install record preservation are directly related to the ledger reader the PR imports. (role: introduced related ledger reader behavior; confidence: medium; commits: 9e086d6ed833, babbad81a939; files: src/plugins/installed-plugin-index-record-reader.ts)

Remaining risk / open question:

  • The PR still lacks accepted after-fix real behavior output from a real setup; the dedicated proof check is failing.
  • GitHub currently reports the branch as dirty/unmergeable, so the PR may need rebase or conflict resolution before normal review can complete.
  • I did not run tests in this read-only review; targeted tests and the changed-lane gate still need maintainer or CI proof.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 7188e4f4ad87.

@pumpkinxing1 pumpkinxing1 force-pushed the fix/channel-catalog-include-install-records branch from d889b57 to d23cadb Compare May 4, 2026 12:02
@odysseus0 odysseus0 self-assigned this May 5, 2026
@odysseus0 odysseus0 force-pushed the fix/channel-catalog-include-install-records branch from 06d36bb to c11cedf Compare May 5, 2026 16:05
@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed size: S labels May 5, 2026
@odysseus0 odysseus0 force-pushed the fix/channel-catalog-include-install-records branch from c11cedf to 60188c7 Compare May 5, 2026 17:22
pumpkinxing1 and others added 5 commits May 5, 2026 10:32
`listChannelCatalogEntries` invoked `discoverOpenClawPlugins` without
forwarding `installRecords`, so npm-installed channel plugins recorded
in `~/.openclaw/plugins/installs.json` were absent from the CLI channel
catalog. `openclaw channels add --channel <id>` and
`openclaw channels login --channel <id>` therefore reported
"Unsupported channel" / "Unknown channel" for any third-party plugin
even when its ledger entry was healthy. Bundled plugins (qqbot, telegram,
etc.) reach the same catalog via the stock discovery path, which is why
they were unaffected.

Lazy-load the persisted ledger via
`loadInstalledPluginIndexInstallRecordsSync` when the caller does not
specify `origin === "bundled"`, and forward the records to discovery.
Bundled-only callers continue to skip the disk read; callers that
already loaded records (e.g. tests, batch flows) can pass them
explicitly. Reader failures fall back silently to "no install records",
preserving prior behaviour.

Also register `@tencent-weixin/openclaw-weixin` in
`scripts/lib/official-external-channel-catalog.json` so the channel
appears in onboarding flows that consult the catalog directly.

Co-authored-by: Cursor <[email protected]>
- Pin `@tencent-weixin/openclaw-weixin` to `2.4.1` and add
  `expectedIntegrity` so the third-party external catalog entry satisfies
  the `exact-with-integrity` policy enforced in
  `test/official-channel-catalog.test.ts`.
- Add CHANGELOG entries for the install-records ledger fix in
  `channel-catalog-registry` and the new Weixin catalog entry.

Co-authored-by: Cursor <[email protected]>
Importing `loadInstalledPluginIndexInstallRecordsSync` from
`./installed-plugin-index-records.js` pulled in the store/config-state
re-export chain, closing a runtime value cycle through
`channels/bundled-channel-catalog-read.ts` ->
`plugins/channel-catalog-registry.ts` ->
`plugins/installed-plugin-index-records.ts` ->
`plugins/installed-plugin-index-store.ts` ->
`plugins/config-state.ts` ->
`plugins/config-normalization-shared.ts` ->
`channels/ids.ts` ->
`channels/bundled-channel-catalog-read.ts`.

That tripped `pnpm check:import-cycles` and produced module-init TDZ
errors like
`ReferenceError: Cannot access 'OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH'
before initialization` across many CI shards.

Switch the import to the lower-level
`./installed-plugin-index-record-reader.js`, which only depends on
filesystem helpers and path resolution, and update the unit-test mock
target to match. Functional behaviour is unchanged; the same reader
function is re-exported from the records module.

Co-authored-by: Cursor <[email protected]>
@odysseus0 odysseus0 force-pushed the fix/channel-catalog-include-install-records branch from 60188c7 to d06034b Compare May 5, 2026 17:46
@odysseus0 odysseus0 merged commit 5fae1c3 into openclaw:main May 5, 2026
85 of 87 checks passed
@odysseus0
Copy link
Copy Markdown
Contributor

Merged via squash.

Thanks @pumpkinxing1!

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

Labels

commands Command implementations scripts Repository scripts size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants