Skip to content

fix(nightly): preserve WeChat plugin across rebuild restore - #3852

Closed
ericksoa wants to merge 2 commits into
mainfrom
fix/preserve-wechat-plugin-restore
Closed

fix(nightly): preserve WeChat plugin across rebuild restore#3852
ericksoa wants to merge 2 commits into
mainfrom
fix/preserve-wechat-plugin-restore

Conversation

@ericksoa

@ericksoa ericksoa commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve image-managed OpenClaw extension directories (nemoclaw, openclaw-weixin) when restoring snapshot/rebuild state.
  • Restore user/non-managed extension entries from backup while filtering managed extension paths out of the restore tar.
  • Re-run the bundled WeChat seed helper after post-restore openclaw doctor --fix so channels.openclaw-weixin stays registered after config rewrites.

Validation

  • npx vitest run test/snapshot.test.ts test/seed-wechat-accounts.test.ts test/generate-openclaw-config.test.ts --testTimeout 60000
  • npm run build:cli
  • npm run typecheck:cli
  • git diff --check
  • commit-time pre-commit hook: full CLI coverage gate passed

Merge Gate

  • Wait for PR CI/CD checks to go green.
  • Then dispatch the full E2E / Nightly suite from trusted main with target_ref=a675a2ee6bd406c70b426e7cb6f44eb0c7d5da26 and pr_number=3852.
  • Admin merge only after the full nightly proof passes.

Summary by CodeRabbit

  • New Features

    • Re-run WeChat account/channel seeding (when present) after sandbox restore.
    • Ensure WeChat OpenClaw extension is installed and enabled during image build when missing.
  • Bug Fixes

    • Preserve image-managed extension directories during sandbox restore while restoring user extensions and data.
  • Tests

    • Added tests validating extension provisioning and preservation behavior during build and restore.

Review Change Stack

@ericksoa ericksoa added bug nightly-e2e Nightly E2E test failures labels May 20, 2026
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1d1f6750-58a5-4a03-8637-0db631a423f5

📥 Commits

Reviewing files that changed from the base of the PR and between a675a2e and ef740a1.

📒 Files selected for processing (2)
  • Dockerfile
  • test/sandbox-provisioning.test.ts

📝 Walkthrough

Walkthrough

Preserve OpenClaw image-managed extensions during sandbox restore and integrate selective tar/cleanup logic; ensure openclaw-weixin is installed/enabled during provisioning and re-run seed-wechat-accounts.py after rebuild's openclaw doctor --fix when present.

Changes

Image-Managed Extension Preservation During Restore

Layer / File(s) Summary
Extension preservation constants and helpers
src/lib/state/sandbox.ts
OPENCLAW_IMAGE_MANAGED_EXTENSION_DIRS enumerates managed extension names. shouldPreserveOpenClawManagedExtensions, buildRestoreTarArgs, and buildOpenClawExtensionsCleanupCommand/buildRestoreCleanupCommand provide preservation decision, selective tar input construction, and deterministic cleanup using find-exclusion predicates.
Integrate preservation into restore tar and cleanup
src/lib/state/sandbox.ts
Restore now computes preserveManagedExtensions, uses buildRestoreTarArgs for tar input selection, and calls buildRestoreCleanupCommand for pre-restore cleanup instead of unconditional rm -rf, allowing managed extensions to remain intact.
Test extension preservation logic
test/snapshot.test.ts
Strengthened cleanup-command assertions in existing test and added a test that simulates fresh image-managed extensions alongside user backups, verifies managed extensions keep fresh contents, stale user extensions are removed, user extensions restored, and cleanup uses ! -name exclusions.

WeChat Extension Provisioning & Rebuild Seeding

Layer / File(s) Summary
Dockerfile: ensure openclaw-weixin present
Dockerfile
Adds conditional RUN step to install @tencent-weixin/openclaw-weixin@2.4.2 when /sandbox/.openclaw/extensions/openclaw-weixin is missing, then verifies and enables the plugin in OpenClaw config.
Provisioning tests for WeChat extension
test/sandbox-provisioning.test.ts
New Vitest helper and suite that runs the Dockerfile provisioning block into a temp sandbox root, stubs openclaw to log install/config calls, and tests installing+enabling when missing and enabling-only when already present.
Re-run seeding after doctor fix
src/lib/actions/sandbox/rebuild.ts
After openclaw doctor --fix during rebuildSandbox, conditionally invokes seed-wechat-accounts.py if present inside the image, captures exit code and truncated stdout, and logs success or skipped messages to preserve seeded channels across config rewrites.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • NVIDIA/NemoClaw#3839: Also re-runs seed-wechat-accounts.py after OpenClaw config mutations to preserve seeding.
  • NVIDIA/NemoClaw#3682: Related work on ensuring openclaw-weixin is present in base/sandbox images and guarding seeding invocations.

Suggested labels

fix, Integration: WeChat, enhancement: messaging, Sandbox

Suggested reviewers

  • cv

Poem

🐰 I hop in sandbox directories with care,
Keeping image-grown plugins fresh and fair,
If config rewrites try to sweep and prune,
I run the seeder under the moon,
So WeChat channels wake and bloom anew.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(nightly): preserve WeChat plugin across rebuild restore' directly and accurately summarizes the main objective: preserving the WeChat plugin (openclaw-weixin extension) during sandbox rebuild/restore operations, which is the core purpose of all the changes across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/preserve-wechat-plugin-restore

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: rebuild-openclaw-e2e, snapshot-commands-e2e, channels-stop-start-e2e
Optional E2E: upgrade-stale-sandbox-e2e, state-backup-restore-e2e, credential-sanitization-e2e

Dispatch hint: rebuild-openclaw-e2e,snapshot-commands-e2e,channels-stop-start-e2e

Auto-dispatched E2E: rebuild-openclaw-e2e, snapshot-commands-e2e, channels-stop-start-e2e via nightly-e2e.yaml at ef740a15038f16825c650eb59a8e1cd918097e7fnightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • rebuild-openclaw-e2e (~60 minutes): Directly exercises OpenClaw sandbox rebuild, backup/restore of .openclaw state, post-restore doctor --fix, registry version update, credential-safe backups, and post-rebuild inference. This is the primary runtime coverage for the changed rebuild.ts and restoreSandboxState behavior.
  • snapshot-commands-e2e (~30 minutes): Snapshot restore uses the same restoreSandboxState path changed in this PR. It validates create/list/restore lifecycle, stale state cleanup, targeted restore, and no credential leakage in snapshots.
  • channels-stop-start-e2e (~120 minutes): Exercises OpenClaw and Hermes messaging channel lifecycle across telegram, discord, wechat, slack, and whatsapp, including rebuild after stop/start/remove. It is the closest existing E2E to validate WeChat/openclaw-weixin channel config survives rebuild and remains correctly attached or removed.

Optional E2E

  • upgrade-stale-sandbox-e2e (~60 minutes): Useful adjacent coverage for stale sandbox upgrade and rebuild with the current image. This PR targets stale published/base-image compatibility for the WeChat extension, but the existing test does not specifically assert WeChat plugin payload preservation.
  • state-backup-restore-e2e (~60 minutes): Adjacent confidence for workspace backup/destroy/recreate/restore user flow. It is less direct than snapshot/rebuild because it primarily uses backup-workspace.sh rather than the modified restoreSandboxState path.
  • credential-sanitization-e2e (~30-45 minutes): Optional security regression because restore behavior around preserved extension directories and state backups changed. This provides broader coverage for snapshot/backup credential and symlink-safety boundaries.

New E2E recommendations

  • messaging-wechat-stale-base-rebuild (high): Existing E2E coverage does not appear to prove the exact regression: current final Dockerfile built on top of a stale sandbox-base lacking /sandbox/.openclaw/extensions/openclaw-weixin, WeChat enabled, rebuild restore excludes old managed extension copies, doctor --fix rewrites openclaw.json, and seed-wechat-accounts.py re-applies channels.openclaw-weixin/accounts while preserving user extensions.
    • Suggested test: Add a WeChat stale-base rebuild E2E that builds or tags a base image without openclaw-weixin, onboards OpenClaw with WECHAT_* env vars, creates a user extension marker, runs nemoclaw rebuild --yes, then asserts openclaw-weixin exists in /sandbox/.openclaw/extensions, openclaw.json contains channels.openclaw-weixin and seeded account config, user extension state is restored, image-managed nemoclaw/openclaw-weixin contents were not overwritten from backup, and no credentials leaked into rebuild backups.

Dispatch hint

  • Workflow: nightly-e2e.yaml
  • jobs input: rebuild-openclaw-e2e,snapshot-commands-e2e,channels-stop-start-e2e

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/actions/sandbox/rebuild.ts (1)

810-835: Run the targeted stop/start lifecycle E2E for this rebuild-path change.

Given this file controls rebuild behavior tied to channel lifecycle persistence, run the focused channels-stop-start-e2e gate before merge.

As per coding guidelines: src/lib/actions/sandbox/rebuild.ts: "This file controls disabled channel resolution used during onboard and rebuild... E2E test recommendation: channels-stop-start-e2e."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/actions/sandbox/rebuild.ts` around lines 810 - 835, This change
touches rebuild behavior in src/lib/actions/sandbox/rebuild.ts around the
seed-wechat logic (symbols: seedWechatCommand, executeSandboxCommand,
seedWechatResult, log, sandboxName); before merging, run the targeted end-to-end
gate "channels-stop-start-e2e" (the focused stop/start lifecycle test) to
validate channel lifecycle persistence and that channels.openclaw-weixin remains
paired after rebuild, and report any failures tied to the seed-wechat step so we
can iterate on seed invocation or exit handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/sandbox/rebuild.ts`:
- Around line 823-834: The code calls seedWechatResult.stdout.includes(...)
without guaranteeing stdout is defined and also shows no user-facing message
when the seed helper is absent; update the seed handling in the seedWechatResult
block (the result of executeSandboxCommand with seedWechatCommand) to first
guard stdout (e.g., check typeof seedWechatResult?.stdout === "string" or
seedWechatResult?.stdout != null) before calling .includes, and when status ===
0 but stdout is missing/undefined emit an explicit "WeChat account seed skipped
(helper missing)" or similar console.log message so users see the skipped state;
keep the existing success message (console.log with G check) when stdout exists
and does not contain "not present; skipping".

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild.ts`:
- Around line 810-835: This change touches rebuild behavior in
src/lib/actions/sandbox/rebuild.ts around the seed-wechat logic (symbols:
seedWechatCommand, executeSandboxCommand, seedWechatResult, log, sandboxName);
before merging, run the targeted end-to-end gate "channels-stop-start-e2e" (the
focused stop/start lifecycle test) to validate channel lifecycle persistence and
that channels.openclaw-weixin remains paired after rebuild, and report any
failures tied to the seed-wechat step so we can iterate on seed invocation or
exit handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 265ac0d8-bde4-4cc8-9b4a-6fe4d4665e5b

📥 Commits

Reviewing files that changed from the base of the PR and between 11b1937 and a675a2e.

📒 Files selected for processing (3)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/state/sandbox.ts
  • test/snapshot.test.ts

Comment on lines +823 to +834
const seedWechatResult = executeSandboxCommand(sandboxName, seedWechatCommand);
log(
`seed-wechat-accounts.py: exit=${seedWechatResult?.status}, stdout=${(seedWechatResult?.stdout || "").substring(0, 200)}`,
);
if (seedWechatResult && seedWechatResult.status === 0) {
if (!seedWechatResult.stdout.includes("not present; skipping")) {
console.log(` ${G}\u2713${R} WeChat account seed reapplied`);
}
} else {
console.log(
` ${D}WeChat account seed skipped (seed helper returned ${seedWechatResult?.status ?? "null"})${R}`,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard stdout before .includes() and emit an explicit “skipped” message for missing helper.

Line 828 can throw if stdout is undefined (you already treat it as optional on Line 825). Also, when the helper is absent, this path exits 0 but prints no user-facing status.

Suggested fix
     const seedWechatResult = executeSandboxCommand(sandboxName, seedWechatCommand);
+    const seedWechatStdout = seedWechatResult?.stdout || "";
+    const seedWechatHelperMissing = seedWechatStdout.includes("not present; skipping");
     log(
-      `seed-wechat-accounts.py: exit=${seedWechatResult?.status}, stdout=${(seedWechatResult?.stdout || "").substring(0, 200)}`,
+      `seed-wechat-accounts.py: exit=${seedWechatResult?.status}, stdout=${seedWechatStdout.substring(0, 200)}`,
     );
     if (seedWechatResult && seedWechatResult.status === 0) {
-      if (!seedWechatResult.stdout.includes("not present; skipping")) {
+      if (seedWechatHelperMissing) {
+        console.log(`  ${D}WeChat account seed helper not present; skipping${R}`);
+      } else {
         console.log(`  ${G}\u2713${R} WeChat account seed reapplied`);
       }
     } else {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/actions/sandbox/rebuild.ts` around lines 823 - 834, The code calls
seedWechatResult.stdout.includes(...) without guaranteeing stdout is defined and
also shows no user-facing message when the seed helper is absent; update the
seed handling in the seedWechatResult block (the result of executeSandboxCommand
with seedWechatCommand) to first guard stdout (e.g., check typeof
seedWechatResult?.stdout === "string" or seedWechatResult?.stdout != null)
before calling .includes, and when status === 0 but stdout is missing/undefined
emit an explicit "WeChat account seed skipped (helper missing)" or similar
console.log message so users see the skipped state; keep the existing success
message (console.log with G check) when stdout exists and does not contain "not
present; skipping".

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26138975439
Target ref: a675a2ee6bd406c70b426e7cb6f44eb0c7d5da26
Workflow ref: main
Requested jobs: rebuild-openclaw-e2e,snapshot-commands-e2e,channels-stop-start-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
channels-stop-start-e2e ⚠️ cancelled
rebuild-openclaw-e2e ⚠️ cancelled
snapshot-commands-e2e ✅ success

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ❌ Some jobs failed

Run: 26139234396
Target ref: a675a2ee6bd406c70b426e7cb6f44eb0c7d5da26
Workflow ref: main
Requested jobs: all (no filter)
Summary: 41 passed, 2 failed, 2 skipped

Job Result
bedrock-runtime-compatible-anthropic-e2e ✅ success
brave-search-e2e ✅ success
channels-stop-start-e2e ⚠️ cancelled
cloud-e2e ✅ success
cloud-inference-e2e ✅ success
cloud-onboard-e2e ✅ success
credential-migration-e2e ✅ success
credential-sanitization-e2e ✅ success
device-auth-health-e2e ✅ success
diagnostics-e2e ✅ success
docs-validation-e2e ✅ success
double-onboard-e2e ✅ success
gpu-double-onboard-e2e ⏭️ skipped
gpu-e2e ⏭️ skipped
hermes-discord-e2e ✅ success
hermes-e2e ✅ success
hermes-inference-switch-e2e ✅ success
hermes-slack-e2e ✅ success
inference-routing-e2e ✅ success
issue-2478-crash-loop-recovery-e2e ✅ success
kimi-inference-compat-e2e ✅ success
launchable-smoke-e2e ✅ success
messaging-compatible-endpoint-e2e ❌ failure
messaging-providers-e2e ❌ failure
network-policy-e2e ✅ success
onboard-negative-paths-e2e ✅ success
onboard-repair-e2e ✅ success
onboard-resume-e2e ✅ success
openclaw-inference-switch-e2e ✅ success
openclaw-slack-pairing-e2e ✅ success
openshell-gateway-upgrade-e2e ✅ success
overlayfs-autofix-e2e ✅ success
rebuild-hermes-e2e ✅ success
rebuild-hermes-stale-base-e2e ✅ success
rebuild-openclaw-e2e ✅ success
runtime-overrides-e2e ✅ success
sandbox-operations-e2e ✅ success
sandbox-survival-e2e ✅ success
shields-config-e2e ✅ success
skill-agent-e2e ✅ success
snapshot-commands-e2e ✅ success
state-backup-restore-e2e ✅ success
telegram-injection-e2e ✅ success
token-rotation-e2e ✅ success
tunnel-lifecycle-e2e ✅ success
upgrade-stale-sandbox-e2e ✅ success

Failed jobs: messaging-compatible-endpoint-e2e, messaging-providers-e2e. Check run artifacts for logs.

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26140506032
Target ref: ef740a15038f16825c650eb59a8e1cd918097e7f
Workflow ref: main
Requested jobs: rebuild-openclaw-e2e,snapshot-commands-e2e,channels-stop-start-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
channels-stop-start-e2e ⚠️ cancelled
rebuild-openclaw-e2e ⚠️ cancelled
snapshot-commands-e2e ✅ success

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 26140857033
Target ref: ef740a15038f16825c650eb59a8e1cd918097e7f
Workflow ref: main
Requested jobs: all (no filter)
Summary: 44 passed, 0 failed, 2 skipped

Job Result
bedrock-runtime-compatible-anthropic-e2e ✅ success
brave-search-e2e ✅ success
channels-stop-start-e2e ✅ success
cloud-e2e ✅ success
cloud-inference-e2e ✅ success
cloud-onboard-e2e ✅ success
credential-migration-e2e ✅ success
credential-sanitization-e2e ✅ success
device-auth-health-e2e ✅ success
diagnostics-e2e ✅ success
docs-validation-e2e ✅ success
double-onboard-e2e ✅ success
gpu-double-onboard-e2e ⏭️ skipped
gpu-e2e ⏭️ skipped
hermes-discord-e2e ✅ success
hermes-e2e ✅ success
hermes-inference-switch-e2e ✅ success
hermes-slack-e2e ✅ success
inference-routing-e2e ✅ success
issue-2478-crash-loop-recovery-e2e ✅ success
kimi-inference-compat-e2e ✅ success
launchable-smoke-e2e ✅ success
messaging-compatible-endpoint-e2e ✅ success
messaging-providers-e2e ✅ success
network-policy-e2e ✅ success
onboard-negative-paths-e2e ✅ success
onboard-repair-e2e ✅ success
onboard-resume-e2e ✅ success
openclaw-inference-switch-e2e ✅ success
openclaw-slack-pairing-e2e ✅ success
openshell-gateway-upgrade-e2e ✅ success
overlayfs-autofix-e2e ✅ success
rebuild-hermes-e2e ✅ success
rebuild-hermes-stale-base-e2e ✅ success
rebuild-openclaw-e2e ✅ success
runtime-overrides-e2e ✅ success
sandbox-operations-e2e ✅ success
sandbox-survival-e2e ✅ success
shields-config-e2e ✅ success
skill-agent-e2e ✅ success
snapshot-commands-e2e ✅ success
state-backup-restore-e2e ✅ success
telegram-injection-e2e ✅ success
token-rotation-e2e ✅ success
tunnel-lifecycle-e2e ✅ success
upgrade-stale-sandbox-e2e ✅ success

@sandl99

sandl99 commented May 20, 2026

Copy link
Copy Markdown
Collaborator

The main branch already had channels-stop-start-e2e successful

You already fixed it on #3839. Nightly job failed is because it doesn't have your 3839 fixed

@cv cv added v0.0.47 and removed v0.0.46 labels May 20, 2026
@sandl99 sandl99 closed this May 20, 2026
@cv cv added the integration: whatsapp WhatsApp integration or channel behavior label May 30, 2026
@wscurran wscurran added area: ci CI workflows, checks, release automation, or GitHub Actions area: e2e End-to-end tests, nightly failures, or validation infrastructure bug-fix PR fixes a bug or regression chore Build, CI, dependency, or tooling maintenance and removed priority: high nightly-e2e Nightly E2E test failures chore Build, CI, dependency, or tooling maintenance labels Jun 3, 2026
@cv
cv deleted the fix/preserve-wechat-plugin-restore branch June 28, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI workflows, checks, release automation, or GitHub Actions area: e2e End-to-end tests, nightly failures, or validation infrastructure bug-fix PR fixes a bug or regression integration: whatsapp WhatsApp integration or channel behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants