Skip to content

fix(onboard): preserve slack messaging policy#3862

Open
yimoj wants to merge 1 commit into
NVIDIA:mainfrom
yimoj:fix/3753-slack-policy-preserve
Open

fix(onboard): preserve slack messaging policy#3862
yimoj wants to merge 1 commit into
NVIDIA:mainfrom
yimoj:fix/3753-slack-policy-preserve

Conversation

@yimoj
Copy link
Copy Markdown
Contributor

@yimoj yimoj commented May 20, 2026

Summary

Preserves messaging-required policy presets when onboarding resumes or reapplies a recorded policy selection. This prevents Slack messaging rebuilds from keeping the channel configuration while dropping the slack network policy.

Related Issue

Fixes #3753

Changes

  • Added a shared messaging-channel-to-policy helper for required policy presets.
  • Reused the helper for create-time sandbox policy composition and policy selection/replay paths.
  • Added focused regressions for resumed and custom non-interactive Slack policy preservation.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Focused verification run locally:

  • npx vitest run src/lib/onboard/messaging-policy-presets.test.ts src/lib/onboard/initial-policy.test.ts test/onboard-preset-diff.test.ts test/policy-tiers-onboard.test.ts
  • npm run typecheck:cli
  • npm run build:cli
  • cd nemoclaw && npm install && npm run build
  • npx vitest run test/ssrf-parity.test.ts nemoclaw/src/commands/migration-state.test.ts
  • codex review -c sandbox_mode=\"danger-full-access\" --uncommitted

Full npm test was attempted under isolated HOME=/tmp/nemoclaw-fix-3753-home / NEMOCLAW_HOME=/tmp/nemoclaw-fix-3753-home/.nemoclaw; it failed in unrelated broad-suite/environment paths (timeouts in CLI/e2e scenario tests and existing gateway-state-sensitive tests). Missing plugin dependency/dist blockers were addressed with cd nemoclaw && npm install && npm run build, and the affected ssrf-parity / migration-state tests then passed.


Signed-off-by: Yimo Jiang yimoj@nvidia.com

Summary by CodeRabbit

  • New Features

    • Onboarding now derives, normalizes, dedupes and merges messaging-channel–required policy presets and persists applied presets when resuming setup.
  • Bug Fixes

    • Presets tied to disabled messaging channels are pruned and excluded during resume, selection, and restore to avoid reapplication.
  • Tests

    • Added unit and regression tests for messaging-preset utilities, resume/selection behavior, and rebuild restoration.

Review Change Stack

@yimoj yimoj self-assigned this May 20, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

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: e7db950a-6cd3-421b-9007-64486bdd3947

📥 Commits

Reviewing files that changed from the base of the PR and between 174e2d8 and fb110d9.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

📝 Walkthrough

Walkthrough

Derives required policy presets from messaging channels, merges/prunes them across create, resume, interactive/non-interactive selection, and rebuild restore flows, and adds utilities plus tests validating normalization, merging, resume reconciliation, and restore-time pruning.

Changes

Messaging Policy Preset Requirements

Layer / File(s) Summary
Messaging policy preset utilities and tests
src/lib/onboard/messaging-policy-presets.ts, src/lib/onboard/messaging-policy-presets.test.ts
New helpers: mergePolicyMessagingChannels() merges channel sources with normalization and dedupe; requiredMessagingChannelPolicyPresets() computes normalized required presets from channels; mergeRequiredMessagingChannelPolicyPresets() appends missing required presets (optionally restricted by known names); pruneDisabledMessagingPolicyPresets() removes presets tied to disabled channels; hasDisabledMessagingPolicyPreset() and mergeAppliedPolicyPresetsForDisabledMessagingCleanup() support applied-preset cleanup. Unit tests validate normalization, merging, and pruning.
Policy selection setup merging
src/lib/onboard/policy-selection.ts
Adds mergeRequiredSetupPolicyPresets which composes Hermes-tool-gateway and messaging-channel required merges; computeSetupPresetSuggestions, preparePolicyPresetResumeSelection, and setupPoliciesWithSelection now use it in suggestions, resume, non-interactive, and interactive branches, and support disabledChannels to prune messaging-derived presets.
Initial policy creation integration
src/lib/onboard/initial-policy.ts
Create-time requested presets for messaging channels now come from requiredMessagingChannelPolicyPresets(activeMessagingChannels) (replacing the removed mapping constant).
Onboard resume flow and channel wiring
src/lib/onboard.ts
Onboard normalizes recorded presets/channels, computes policyMessagingChannels by merging selected + recorded + active channels (respecting disabled channels), uses preparePolicyPresetResumeSelection to reconcile recorded presets, passes policyMessagingChannels into setupPoliciesWithSelection, and persists returned applied presets to the session.
Rebuild restore preset pruning
src/lib/actions/sandbox/rebuild.ts
During sandbox rebuild restore, backupManifest.policyPresets are filtered via pruneDisabledMessagingPolicyPresets(..., rebuildDisabledChannels) before being reapplied.
Rebuild/resume regression tests
test/onboard-preset-diff.test.ts, test/rebuild-policy-presets.test.ts
Adds regression tests to assert the slack preset is preserved when Slack is enabled across resume and non-interactive custom flows, and tests that restore pruning omits presets tied to disabled channels while preserving required presets.
sequenceDiagram
  participant Onboard as src/lib/onboard.ts
  participant PolicySelection as src/lib/onboard/policy-selection.ts
  participant MessagingUtil as src/lib/onboard/messaging-policy-presets.ts
  participant SetupEngine as setupPoliciesWithSelection
  participant Rebuild as src/lib/actions/sandbox/rebuild.ts

  Onboard->>MessagingUtil: mergePolicyMessagingChannels(selected, recorded, active, disabled)
  MessagingUtil-->>Onboard: policyMessagingChannels
  Onboard->>PolicySelection: preparePolicyPresetResumeSelection(..., {recordedPolicyPresets, enabledChannels, disabledChannels})
  PolicySelection-->>Onboard: recordedPolicyPresetsForSupport + flags
  Onboard->>SetupEngine: setupPoliciesWithSelection(..., { enabledChannels: policyMessagingChannels, disabledChannels })
  SetupEngine->>PolicySelection: mergeRequiredSetupPolicyPresets(chosen/applied, {enabledChannels, hermesToolGateways})
  PolicySelection->>MessagingUtil: mergeRequiredMessagingChannelPolicyPresets(..., enabledChannels)
  MessagingUtil-->>PolicySelection: appended required presets
  Rebuild->>MessagingUtil: pruneDisabledMessagingPolicyPresets(backupManifest.policyPresets, rebuildDisabledChannels)
  MessagingUtil-->>Rebuild: savedPresets
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#3452: related to rebuild preset restoration ordering and interactions with messaging-driven preset restoration.
  • NVIDIA/NemoClaw#3532: touches disabledChannels preservation across rebuild/onboard resume; related to disabled-channel-driven preset behavior.

Suggested labels

fix, Integration: Slack, enhancement: policy, NemoClaw CLI

Suggested reviewers

  • jyaunches
  • cv
  • ericksoa

Poem

🐰 I hop through channels, tidy and bright,
I stitch the Slack preset back each night.
From create to rebuild, resume to test,
I chase disabled bits and tidy the rest.
A carrot for CI, a hop for the quest.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% 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(onboard): preserve slack messaging policy' clearly and specifically identifies the main change: addressing a bug where Slack messaging policy presets were not preserved during onboard resume/rebuild cycles.
Linked Issues check ✅ Passed The pull request fully addresses issue #3753's primary objective and acceptance criteria: it adds helper utilities for preserving messaging-required policy presets, implements preservation logic across create-time, resume, and rebuild paths, and adds focused regression tests validating Slack policy preservation.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to fixing the Slack messaging policy preservation issue: new messaging-policy utilities, policy selection/resume helpers, initial policy integration, rebuild filtering, and targeted regression tests with no unrelated modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/onboard.ts`:
- Around line 10028-10034: The new policy-channel merge (building
policyMessagingChannels from selectedMessagingChannels,
recordedMessagingChannels, and activeMessagingChannels) must be extracted from
onboard.ts into a small helper in src/lib/onboard (e.g.,
createPolicyMessagingChannels or mergePolicyMessagingChannels) and the top-level
onboard.ts should only call that helper with the three inputs and receive the
merged array; move the same extraction for the related block covering lines
~10054-10074 as well, ensure the helper validates
Array.isArray(activeMessagingChannels) and preserves Set de-duplication, and
update onboard.ts to import and call the helper so the entrypoint remains thin.
- Around line 10027-10034: The current merge that builds policyMessagingChannels
uses registry.getSandbox(sandboxName)?.messagingChannels
(activeMessagingChannels) without excluding channels listed in disabledChannels,
which can re-enable stopped channels like "slack"; update the logic that
constructs policyMessagingChannels to first read disabledChannels (e.g.,
registry.getSandbox(sandboxName)?.disabledChannels), ensure it's an array, and
filter out any channel names present in disabledChannels from
activeMessagingChannels (and from the combined set of
selectedMessagingChannels/recordedMessagingChannels) before creating the Set so
disabled channels are never included when deriving required policy presets.
🪄 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: c08f1f3e-cf98-48aa-8de7-23129f0805d4

📥 Commits

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

📒 Files selected for processing (6)
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts

Comment thread src/lib/onboard.ts Outdated
Comment thread src/lib/onboard.ts Outdated
@yimoj yimoj force-pushed the fix/3753-slack-policy-preserve branch from 687c34a to 4bd022a Compare May 20, 2026 05:40
@yimoj yimoj added the v0.0.48 Release target label May 20, 2026
@yimoj yimoj force-pushed the fix/3753-slack-policy-preserve branch 2 times, most recently from 80aebaa to 2371fba Compare May 20, 2026 06:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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)

760-763: Run the targeted channels stop/start e2e before merge.

Given this branch now prunes restored presets based on disabled channels, please run the recommended channels-stop-start-e2e workflow to validate stop/start persistence across destroy/recreate with cached credentials.

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 760 - 763, The change prunes
restored presets using pruneDisabledMessagingPolicyPresets (called into
savedPresets with rebuildDisabledChannels), so run the channels-stop-start-e2e
workflow before merging to validate stop/start persistence across
destroy/recreate with cached credentials; specifically, execute the
channels-stop-start-e2e pipeline against this branch, confirm that disabled
channel resolution (pruneDisabledMessagingPolicyPresets -> savedPresets)
preserves stop/start state after sandbox rebuild, and report any failing
scenarios to adjust how rebuildDisabledChannels is computed or applied during
restore.
🤖 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/onboard.ts`:
- Around line 10060-10078: The fast resume path only verifies that the remaining
(pruned) presets are present in the sandbox via arePolicyPresetsApplied, but it
does not detect extra applied presets that were removed because their channels
are now disabled (e.g., Slack still applied). Update the resumePolicies
condition to also ensure there are no additional applied policy presets beyond
recordedPolicyPresetsForSupport: either replace
arePolicyPresetsApplied(sandboxName, recordedPolicyPresetsForSupport) with a
strict equality check (sandboxAppliedPresets ===
recordedPolicyPresetsForSupport) or add a complementary function (e.g.,
arePolicyPresetsExactMatch or hasNoExtraAppliedPolicyPresets) and use that
together with recordedPolicyPresetsNeedReconcile, referencing
recordedPolicyPresetsForSupport, recordedPolicyPresetsNeedReconcile,
arePolicyPresetsApplied and resumePolicies.

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild.ts`:
- Around line 760-763: The change prunes restored presets using
pruneDisabledMessagingPolicyPresets (called into savedPresets with
rebuildDisabledChannels), so run the channels-stop-start-e2e workflow before
merging to validate stop/start persistence across destroy/recreate with cached
credentials; specifically, execute the channels-stop-start-e2e pipeline against
this branch, confirm that disabled channel resolution
(pruneDisabledMessagingPolicyPresets -> savedPresets) preserves stop/start state
after sandbox rebuild, and report any failing scenarios to adjust how
rebuildDisabledChannels is computed or applied during restore.
🪄 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: 2333fa41-c915-4b53-934e-fd74904099dd

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd022a and 80aebaa.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

Comment thread src/lib/onboard.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/lib/onboard.ts (1)

10052-10063: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't skip policy reconciliation while a disabled messaging preset is still applied.

Line 10063 only checks that the pruned set is present, not that disabled-channel presets are absent. Because Lines 10076-10080 persist recordedPolicyPresetsForSupport before setupPoliciesWithSelection() runs, an interrupted run can leave the session without slack while the sandbox still has it applied. The next --resume will satisfy this fast path and keep the stale egress policy.

💡 Minimal fix
+    const appliedPresetNames = policies.getAppliedPresets(sandboxName);
+    const hasDisabledMessagingPresetsApplied =
+      pruneDisabledMessagingPolicyPresets(
+        appliedPresetNames,
+        activeSandbox?.disabledChannels,
+      ).length !== appliedPresetNames.length;
     const resumePolicies =
       resume &&
       sandboxName &&
       !recordedPolicyPresetsNeedReconcile &&
+      !hasDisabledMessagingPresetsApplied &&
       arePolicyPresetsApplied(sandboxName, recordedPolicyPresetsForSupport);

Also applies to: 10076-10080

🤖 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/onboard.ts` around lines 10052 - 10063, The fast-path resume
incorrectly treats a pruned recordedPolicyPresetsForSupport as valid even when
it still contains presets that target disabled messaging channels; update the
recordedPolicyPresetsNeedReconcile logic (used to compute resumePolicies) to
consider not only length mismatch but also whether any preset in
recordedPolicyPresetsForSupport touches activeSandbox?.disabledChannels (i.e.,
detect preset.channel intersections) so the resume path is blocked when
disabled-channel presets remain; also ensure the code that persists
recordedPolicyPresetsForSupport (the block around where you save them before
calling setupPoliciesWithSelection) is moved or gated so persistence happens
only after setupPoliciesWithSelection completes successfully. Reference symbols:
recordedPolicyPresetsForSupport, recordedPolicyPresetsNeedReconcile,
pruneDisabledMessagingPolicyPresets, mergeRequiredSetupPolicyPresets,
arePolicyPresetsApplied, setupPoliciesWithSelection,
activeSandbox?.disabledChannels.
🧹 Nitpick comments (1)
src/lib/actions/sandbox/rebuild.ts (1)

760-763: Run the channels stop/start E2E for this path-sensitive change.

Given this logic now gates preset restore based on disabled channels, run the targeted lifecycle E2E to confirm channels stop/channels start behavior remains stable across rebuild.

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 760 - 763, This change
affects how disabled channels gate preset restoration in
src/lib/actions/sandbox/rebuild.ts (see pruneDisabledMessagingPolicyPresets,
savedPresets, rebuildDisabledChannels), so run the targeted lifecycle E2E suite
named channels-stop-start-e2e against the rebuild/onboard path to validate
channels stop/start behavior; if the E2E fails, trace failures back to
pruneDisabledMessagingPolicyPresets or the rebuildDisabledChannels resolution
logic and update the code or tests to ensure disabled-channel
preservation/restore semantics match expected behavior before merging.
🤖 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.

Duplicate comments:
In `@src/lib/onboard.ts`:
- Around line 10052-10063: The fast-path resume incorrectly treats a pruned
recordedPolicyPresetsForSupport as valid even when it still contains presets
that target disabled messaging channels; update the
recordedPolicyPresetsNeedReconcile logic (used to compute resumePolicies) to
consider not only length mismatch but also whether any preset in
recordedPolicyPresetsForSupport touches activeSandbox?.disabledChannels (i.e.,
detect preset.channel intersections) so the resume path is blocked when
disabled-channel presets remain; also ensure the code that persists
recordedPolicyPresetsForSupport (the block around where you save them before
calling setupPoliciesWithSelection) is moved or gated so persistence happens
only after setupPoliciesWithSelection completes successfully. Reference symbols:
recordedPolicyPresetsForSupport, recordedPolicyPresetsNeedReconcile,
pruneDisabledMessagingPolicyPresets, mergeRequiredSetupPolicyPresets,
arePolicyPresetsApplied, setupPoliciesWithSelection,
activeSandbox?.disabledChannels.

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild.ts`:
- Around line 760-763: This change affects how disabled channels gate preset
restoration in src/lib/actions/sandbox/rebuild.ts (see
pruneDisabledMessagingPolicyPresets, savedPresets, rebuildDisabledChannels), so
run the targeted lifecycle E2E suite named channels-stop-start-e2e against the
rebuild/onboard path to validate channels stop/start behavior; if the E2E fails,
trace failures back to pruneDisabledMessagingPolicyPresets or the
rebuildDisabledChannels resolution logic and update the code or tests to ensure
disabled-channel preservation/restore semantics match expected behavior before
merging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 65d36433-4682-45c9-a848-1c36a52da176

📥 Commits

Reviewing files that changed from the base of the PR and between 80aebaa and 2371fba.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

@yimoj yimoj removed the v0.0.48 Release target label May 20, 2026
@yimoj yimoj force-pushed the fix/3753-slack-policy-preserve branch 2 times, most recently from 63b2174 to f12a919 Compare May 20, 2026 07:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/lib/onboard.ts (2)

10072-10110: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't drop disabled-channel cleanup presets when session policy history is absent.

mergeAppliedPolicyPresetsForDisabledMessagingCleanup(...) can synthesize presets that must be shown to setupPoliciesWithSelection() so the stale disabled-channel policy gets removed. Lines 10105-10108 discard that state whenever latestSession.policyPresets is null, so resumed/legacy sessions can still leave a disabled preset like slack applied.

💡 Suggested change
+      const selectedPolicyPresetsForSetup =
+        Array.isArray(recordedPolicyPresets) || recordedPolicyPresetsForSupport.length > 0
+          ? recordedPolicyPresetsForSupport
+          : null;
       const setupAppliedPolicyPresets = await setupPoliciesWithSelection(sandboxName, {
-        selectedPresets:
-          Array.isArray(recordedPolicyPresets)
-            ? recordedPolicyPresetsForSupport
-            : null,
+        selectedPresets: selectedPolicyPresetsForSetup,
         enabledChannels: policyMessagingChannels,
         disabledChannels: activeSandbox?.disabledChannels,
         webSearchConfig,
🤖 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/onboard.ts` around lines 10072 - 10110, The code discards synthesized
cleanup presets from mergeAppliedPolicyPresetsForDisabledMessagingCleanup
whenever recordedPolicyPresets is null, so setupPoliciesWithSelection may not
receive necessary selectedPresets to remove stale disabled-channel policies;
change the selectedPresets argument passed into setupPoliciesWithSelection to
use recordedPolicyPresetsForSupport (or at least check its length) instead of
gating on Array.isArray(recordedPolicyPresets), ensuring that when
recordedPolicyPresetsForSupport contains synthesized presets they are forwarded
to setupPoliciesWithSelection; update the ternary at the selectedPresets key (or
its surrounding logic) to pass recordedPolicyPresetsForSupport when non-empty so
disabled-channel cleanup presets are not dropped.

10027-10124: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Extract this reconciliation block to clear the entrypoint-budget CI failure.

This chunk is the main new orchestration added to src/lib/onboard.ts, and CI is currently red because the file grew past the enforced budget. Moving this policy-state assembly behind a small helper in src/lib/onboard/* should unblock the check while keeping the entrypoint thin.

🤖 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/onboard.ts` around lines 10027 - 10124, Extract the policy-state
reconciliation block (starting at the
recordedPolicyPresets/recordedMessagingChannels lines through the computation of
resumePolicies and prior to the resume handling) into a new helper function
(e.g., assemblePolicyState) in a new module under src/lib/onboard/; the helper
should accept the minimal inputs (sandboxName, latestSession or
recordedPolicyPresets/recordedMessagingChannels, selectedMessagingChannels,
registry.getSandbox result or sandboxName+registry, policies,
hermesToolGateways, webSearchSupported, activeSandbox?.disabledChannels,
webSearchConfig, provider, model, agent, etc.) and return the computed values
used afterward (at least recordedPolicyPresetsForSupport,
policyMessagingChannels, appliedPolicyPresetsForSupport,
disabledMessagingPolicyPresetApplied, resumePolicies); use the existing helper
functions referenced in the block (mergePolicyMessagingChannels,
pruneDisabledMessagingPolicyPresets, policies.clampSetupPolicyPresetNames,
hasDisabledMessagingPolicyPreset,
mergeAppliedPolicyPresetsForDisabledMessagingCleanup,
mergeRequiredSetupPolicyPresets, arePolicyPresetsApplied) inside the new
assemblePolicyState function and export it, then replace the large inline block
in src/lib/onboard.ts with a single call to assemblePolicyState and use its
returned values unchanged.
🧹 Nitpick comments (2)
src/lib/onboard.ts (1)

10027-10124: Please run the lifecycle E2Es for this path before merge.

This touches resume, disabled-channel cleanup, and Slack policy preservation, so I’d specifically want channels-stop-start-e2e, rebuild-openclaw-e2e, and hermes-slack-e2e on this revision.

As per coding guidelines, channels-stop-start-e2e, rebuild-openclaw-e2e, and hermes-slack-e2e are recommended for onboarding changes in src/lib/onboard.ts.

🤖 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/onboard.ts` around lines 10027 - 10124, Summary: This change touches
resume logic and disabled-channel/policy preset handling; run the relevant E2E
suites before merging. Action: execute the lifecycle E2E tests for this
onboarding path — channels-stop-start-e2e, rebuild-openclaw-e2e, and
hermes-slack-e2e — and verify no regressions around resume, disabled-channel
cleanup, and Slack policy preservation; focus validation on flows that exercise
resume, mergeRequiredSetupPolicyPresets, pruneDisabledMessagingPolicyPresets,
mergeAppliedPolicyPresetsForDisabledMessagingCleanup,
hasDisabledMessagingPolicyPreset, arePolicyPresetsApplied, and
setupPoliciesWithSelection; if failures occur, capture failures, reproduce
locally, and update the code or tests until all three suites pass.
test/onboard-preset-diff.test.ts (1)

324-458: ⚡ Quick win

Harden these Slack regression tests against inherited messaging env leakage.

These new assertions can flake if parent DISCORD_*/TELEGRAM_* env vars leak into the spawned helper process and activate extra channels implicitly.

Proposed patch (in buildPreamble script body)
 return String.raw`
 // All stubs MUST be installed before requiring onboard so its module-level
 // destructuring picks up the patched functions.
 Object.defineProperty(process, "platform", { value: "darwin" });
+
+for (const key of Object.keys(process.env)) {
+  if (key.startsWith("DISCORD_") || key.startsWith("TELEGRAM_")) {
+    delete process.env[key];
+  }
+}

 const resolver = require(${resolveOpenshellPath});
 resolver.resolveOpenshell = () => "/fake/openshell";

Based on learnings: For hermetic messaging-channel tests in this repo, spawned helper scripts should remove inherited DISCORD_* and TELEGRAM_* env vars to avoid unintended channel activation.

🤖 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 `@test/onboard-preset-diff.test.ts` around lines 324 - 458, The tests flake
because inherited DISCORD_* and TELEGRAM_* env vars can activate messaging
channels in the spawned helper; modify the buildPreamble output to explicitly
remove any process.env keys that start with "DISCORD_" or "TELEGRAM_" before the
helper logic runs. Concretely, inside the buildPreamble-generated script (the
preamble string emitted by buildPreamble), add a short sanitization block that
iterates Object.keys(process.env) and deletes keys matching /^DISCORD_/ or
/^TELEGRAM_/ (e.g., for (const k of Object.keys(process.env)) if
(k.startsWith("DISCORD_") || k.startsWith("TELEGRAM_")) delete process.env[k]);
place this at the very top of the preamble so setupPoliciesWithSelection and any
channel-detection logic sees a hermetic env.
🤖 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.

Outside diff comments:
In `@src/lib/onboard.ts`:
- Around line 10072-10110: The code discards synthesized cleanup presets from
mergeAppliedPolicyPresetsForDisabledMessagingCleanup whenever
recordedPolicyPresets is null, so setupPoliciesWithSelection may not receive
necessary selectedPresets to remove stale disabled-channel policies; change the
selectedPresets argument passed into setupPoliciesWithSelection to use
recordedPolicyPresetsForSupport (or at least check its length) instead of gating
on Array.isArray(recordedPolicyPresets), ensuring that when
recordedPolicyPresetsForSupport contains synthesized presets they are forwarded
to setupPoliciesWithSelection; update the ternary at the selectedPresets key (or
its surrounding logic) to pass recordedPolicyPresetsForSupport when non-empty so
disabled-channel cleanup presets are not dropped.
- Around line 10027-10124: Extract the policy-state reconciliation block
(starting at the recordedPolicyPresets/recordedMessagingChannels lines through
the computation of resumePolicies and prior to the resume handling) into a new
helper function (e.g., assemblePolicyState) in a new module under
src/lib/onboard/; the helper should accept the minimal inputs (sandboxName,
latestSession or recordedPolicyPresets/recordedMessagingChannels,
selectedMessagingChannels, registry.getSandbox result or sandboxName+registry,
policies, hermesToolGateways, webSearchSupported,
activeSandbox?.disabledChannels, webSearchConfig, provider, model, agent, etc.)
and return the computed values used afterward (at least
recordedPolicyPresetsForSupport, policyMessagingChannels,
appliedPolicyPresetsForSupport, disabledMessagingPolicyPresetApplied,
resumePolicies); use the existing helper functions referenced in the block
(mergePolicyMessagingChannels, pruneDisabledMessagingPolicyPresets,
policies.clampSetupPolicyPresetNames, hasDisabledMessagingPolicyPreset,
mergeAppliedPolicyPresetsForDisabledMessagingCleanup,
mergeRequiredSetupPolicyPresets, arePolicyPresetsApplied) inside the new
assemblePolicyState function and export it, then replace the large inline block
in src/lib/onboard.ts with a single call to assemblePolicyState and use its
returned values unchanged.

---

Nitpick comments:
In `@src/lib/onboard.ts`:
- Around line 10027-10124: Summary: This change touches resume logic and
disabled-channel/policy preset handling; run the relevant E2E suites before
merging. Action: execute the lifecycle E2E tests for this onboarding path —
channels-stop-start-e2e, rebuild-openclaw-e2e, and hermes-slack-e2e — and verify
no regressions around resume, disabled-channel cleanup, and Slack policy
preservation; focus validation on flows that exercise resume,
mergeRequiredSetupPolicyPresets, pruneDisabledMessagingPolicyPresets,
mergeAppliedPolicyPresetsForDisabledMessagingCleanup,
hasDisabledMessagingPolicyPreset, arePolicyPresetsApplied, and
setupPoliciesWithSelection; if failures occur, capture failures, reproduce
locally, and update the code or tests until all three suites pass.

In `@test/onboard-preset-diff.test.ts`:
- Around line 324-458: The tests flake because inherited DISCORD_* and
TELEGRAM_* env vars can activate messaging channels in the spawned helper;
modify the buildPreamble output to explicitly remove any process.env keys that
start with "DISCORD_" or "TELEGRAM_" before the helper logic runs. Concretely,
inside the buildPreamble-generated script (the preamble string emitted by
buildPreamble), add a short sanitization block that iterates
Object.keys(process.env) and deletes keys matching /^DISCORD_/ or /^TELEGRAM_/
(e.g., for (const k of Object.keys(process.env)) if (k.startsWith("DISCORD_") ||
k.startsWith("TELEGRAM_")) delete process.env[k]); place this at the very top of
the preamble so setupPoliciesWithSelection and any channel-detection logic sees
a hermetic env.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 401b41ae-3c4f-45d5-b9e8-4fe21ac50f17

📥 Commits

Reviewing files that changed from the base of the PR and between 2371fba and 63b2174.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

@yimoj
Copy link
Copy Markdown
Contributor Author

yimoj commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/onboard.ts (1)

10024-10035: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the effective enabled channel set for the smoke verifier.

activeSandbox.messagingChannels is the configured set, not the enabled set; this file intentionally preserves disabled channels there so channels start can restore them later. Passing that value here means verifyCompatibleEndpointSandboxSmoke(...) can still treat a stopped channel as active even though the policy flow now uses policyMessagingChannels.

Suggested fix
-    const activeMessagingChannels = activeSandbox?.messagingChannels;
+    const activeMessagingChannels = activeSandbox?.messagingChannels;
     const policyMessagingChannels = mergePolicyMessagingChannels(selectedMessagingChannels, recordedMessagingChannels, activeMessagingChannels, activeSandbox?.disabledChannels);
@@
-      messagingChannels: Array.isArray(activeMessagingChannels) ? activeMessagingChannels : [],
+      messagingChannels: policyMessagingChannels,
🤖 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/onboard.ts` around lines 10024 - 10035, The smoke verifier is being
called with the raw configured set (activeMessagingChannels) which can include
channels marked disabled; instead pass the effective enabled set
(policyMessagingChannels) into verifyCompatibleEndpointSandboxSmoke so the
verifier uses the same channel view as the policy flow—update the call site that
currently passes Array.isArray(activeMessagingChannels) ?
activeMessagingChannels : [] to instead pass
Array.isArray(policyMessagingChannels) ? policyMessagingChannels : []
(referencing activeSandbox, activeMessagingChannels, policyMessagingChannels and
verifyCompatibleEndpointSandboxSmoke to locate the change).
🧹 Nitpick comments (1)
test/rebuild-policy-presets.test.ts (1)

181-188: ⚡ Quick win

Consider adding a clarifying comment to explain the test expectation.

The test expects telegram to be preserved when disabled, while test 1 expects slack to be removed when disabled. Without context, this appears contradictory. The test would benefit from a brief comment explaining the distinction (e.g., "telegram channel does not require a 'telegram' policy preset, so a preset named 'telegram' is preserved" or "only channels with known policy mappings are pruned").

The phrase "for later start and rebuild" in the test name is also unclear—consider clarifying what "later start" refers to in the test documentation.

📝 Suggested clarifying comment
+    // Unlike Slack (which requires the 'slack' preset), Telegram does not have
+    // a required policy preset mapping, so a preset named 'telegram' is preserved
+    // even when the Telegram channel is disabled.
     it("preserves non-required channel presets for later start and rebuild", () => {
🤖 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 `@test/rebuild-policy-presets.test.ts` around lines 181 - 188, Add a short
clarifying comment inside the test "preserves non-required channel presets for
later start and rebuild" explaining why "telegram" is expected to be preserved
(e.g., that pruneDisabledMessagingPolicyPresets only prunes presets that map to
known/required channels, and "telegram" in this case is considered non-required
so it should remain), and rename or expand the test name to clarify what "later
start" means (for example "preserves non-required channel presets for later
start/rebuild when channel mapping is absent"); update references in the test to
pruneDisabledMessagingPolicyPresets, manifest.policyPresets and savedPresets to
make the intent explicit.
🤖 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.

Outside diff comments:
In `@src/lib/onboard.ts`:
- Around line 10024-10035: The smoke verifier is being called with the raw
configured set (activeMessagingChannels) which can include channels marked
disabled; instead pass the effective enabled set (policyMessagingChannels) into
verifyCompatibleEndpointSandboxSmoke so the verifier uses the same channel view
as the policy flow—update the call site that currently passes
Array.isArray(activeMessagingChannels) ? activeMessagingChannels : [] to instead
pass Array.isArray(policyMessagingChannels) ? policyMessagingChannels : []
(referencing activeSandbox, activeMessagingChannels, policyMessagingChannels and
verifyCompatibleEndpointSandboxSmoke to locate the change).

---

Nitpick comments:
In `@test/rebuild-policy-presets.test.ts`:
- Around line 181-188: Add a short clarifying comment inside the test "preserves
non-required channel presets for later start and rebuild" explaining why
"telegram" is expected to be preserved (e.g., that
pruneDisabledMessagingPolicyPresets only prunes presets that map to
known/required channels, and "telegram" in this case is considered non-required
so it should remain), and rename or expand the test name to clarify what "later
start" means (for example "preserves non-required channel presets for later
start/rebuild when channel mapping is absent"); update references in the test to
pruneDisabledMessagingPolicyPresets, manifest.policyPresets and savedPresets to
make the intent explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1bbda8e1-3f2f-46a5-bb47-3a3b45e30719

📥 Commits

Reviewing files that changed from the base of the PR and between 2371fba and f12a919.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

@yimoj yimoj force-pushed the fix/3753-slack-policy-preserve branch from f12a919 to 174e2d8 Compare May 20, 2026 07:26
@yimoj
Copy link
Copy Markdown
Contributor Author

yimoj commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/onboard/policy-selection.ts`:
- Around line 192-198: The current truthy checks (e.g., if
(options.recordedPolicyPresets)) skip empty-array selections and thus drop
required presets on resume; change these guards to explicitly check for
undefined/null (for example: if (options.recordedPolicyPresets !== undefined &&
options.recordedPolicyPresets !== null) or use
Array.isArray(options.recordedPolicyPresets)) so an empty [] is treated as valid
input and still calls mergeRequiredSetupPolicyPresets with the same payload
(include selectablePolicyPresets.map(...) etc.); apply the same explicit-check
fix to the other occurrence around lines 264-272 to ensure resume/replay always
merges required messaging presets when channels are enabled.

In `@test/onboard-preset-diff.test.ts`:
- Around line 324-458: The tests are flaky because the spawned test script
inherits host env vars that can enable messaging channels; update the runScript
helper to sanitize the environment before spawning the child: copy process.env,
remove any keys starting with "DISCORD_" or "TELEGRAM_" (and any other
messaging-related prefixes you see causing leakage), and pass that cleaned env
into the child process spawn/exec call (the runScript function is the unique
symbol to change). Ensure the rest of runScript behavior (stdout/stderr capture,
exit status) remains unchanged.
🪄 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: 3a295cff-be85-4026-9701-4a709a176570

📥 Commits

Reviewing files that changed from the base of the PR and between f12a919 and 174e2d8.

📒 Files selected for processing (8)
  • src/lib/actions/sandbox/rebuild.ts
  • src/lib/onboard.ts
  • src/lib/onboard/initial-policy.ts
  • src/lib/onboard/messaging-policy-presets.test.ts
  • src/lib/onboard/messaging-policy-presets.ts
  • src/lib/onboard/policy-selection.ts
  • test/onboard-preset-diff.test.ts
  • test/rebuild-policy-presets.test.ts

Comment thread src/lib/onboard/policy-selection.ts Outdated
Comment thread test/onboard-preset-diff.test.ts
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj yimoj force-pushed the fix/3753-slack-policy-preserve branch from 174e2d8 to fb110d9 Compare May 20, 2026 07:36
@yimoj
Copy link
Copy Markdown
Contributor Author

yimoj commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yimoj
Copy link
Copy Markdown
Contributor Author

yimoj commented May 20, 2026

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Reviews resumed.

@yimoj yimoj added the v0.0.48 Release target label May 20, 2026
@wscurran wscurran added enhancement: policy fix Integration: Slack Use this label to identify Slack integration issues with NemoClaw. labels May 20, 2026
@wscurran
Copy link
Copy Markdown
Contributor

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

Labels

enhancement: policy fix Integration: Slack Use this label to identify Slack integration issues with NemoClaw. v0.0.49 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slack messaging rebuild can preserve channel config but drop slack network policy

3 participants