Skip to content

fix: apply [agents.channel] config when spawning platform channels#550

Open
jamiepine wants to merge 1 commit intomainfrom
fix/agent-channel-config-not-applied
Open

fix: apply [agents.channel] config when spawning platform channels#550
jamiepine wants to merge 1 commit intomainfrom
fix/agent-channel-config-not-applied

Conversation

@jamiepine
Copy link
Copy Markdown
Member

Summary

  • [agents.channel] settings (response_mode, save_attachments) were loaded from TOML but silently discarded at runtime — agent_default was always None in every ResolvedConversationSettings::resolve() call for platform channels and the idle-worker resume path
  • Adds ChannelConfig::to_conversation_settings() in config/types.rs to centralise the conversion logic
  • Passes the result as agent_default across all six resolve() call sites (new-message spawn path + idle-worker resume path)
  • Also fixes a secondary bug: the old inline construction was gated on response_mode.is_some(), which would silently drop a configured save_attachments = true when no response_mode was set

Test plan

  • Config with response_mode = "quiet" under [agents.channel] now suppresses responses without needing a /quiet slash command
  • Config with save_attachments = true under [agents.channel] now applies even when response_mode is unset
  • Idle-worker resumed channels inherit [agents.channel] defaults correctly
  • Existing DB-persisted overrides (via /quiet, /active) still take precedence over config

🤖 Generated with Claude Code

The agent_default argument to ResolvedConversationSettings::resolve() was
always None for platform channels (Slack, Discord, etc.) and the idle-worker
resume path, so [agents.channel] settings (response_mode, save_attachments)
were loaded from TOML but silently discarded at runtime.

Adds ChannelConfig::to_conversation_settings() to centralise the conversion,
and passes the result as agent_default in all six resolve() call sites
(new-message path and idle-worker resume path).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

Walkthrough

A new to_conversation_settings() method was added to ChannelConfig that converts channel configuration to conversation settings, with special handling to return None when values match system defaults. This method is integrated into the configuration resolution logic in main.rs to provide agent-level channel defaults during conversation settings resolution.

Changes

Cohort / File(s) Summary
Configuration Type Extension
src/config/types.rs
Added pub fn to_conversation_settings(&self) -> Option<crate::conversation::ConversationSettings> method to ChannelConfig that conditionally transforms channel configuration to conversation settings, returning None for no-op configurations.
Configuration Resolution Integration
src/main.rs
Modified ResolvedConversationSettings::resolve calls to incorporate agent_channel_default derived from the new method, replacing None arguments in both idle worker resumption and normal conversation channel creation paths; also updated fallback logic when settings load fails.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: applying [agents.channel] config settings when spawning platform channels.
Description check ✅ Passed The description is directly related to the changeset, explaining the bug being fixed, the solution implemented, and providing a comprehensive test plan.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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/agent-channel-config-not-applied

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

pub fn to_conversation_settings(
&self,
) -> Option<crate::conversation::ConversationSettings> {
if self.response_mode.is_none() && !self.save_attachments {
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.

to_conversation_settings() currently only treats response_mode = None as “default”, so response_mode = "active" still injects a no-op layer. Also, when response_mode is set, this will always write save_attachments: Some(false) even though it's the default.

Suggested change
if self.response_mode.is_none() && !self.save_attachments {
let response_mode = self.response_mode.unwrap_or_default();
let save_attachments = self.save_attachments.then_some(true);
if response_mode == crate::conversation::settings::ResponseMode::Active
&& save_attachments.is_none()
{
return None;
}
Some(crate::conversation::ConversationSettings {
response_mode,
save_attachments,
..Default::default()
})

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

Caution

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

⚠️ Outside diff range comments (1)
src/main.rs (1)

1963-2020: ⚠️ Potential issue | 🟠 Major

Resume path still skips binding-level defaults.

Every branch here resolves with None as the channel layer, while the normal spawn path later in this file passes binding_settings.as_ref(). Once the resumed channel is inserted into active_channels, later inbound messages reuse it instead of re-resolving settings, so a restart can silently drop binding-level response_mode / save_attachments defaults for channels that have idle workers but no DB override. Please persist or reconstruct the matched binding settings before creating the resumed channel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.rs` around lines 1963 - 2020, The resume branch currently calls
ResolvedConversationSettings::resolve with None for the channel/binding
settings, dropping binding-level defaults; locate the resume block that builds
resolved_settings (the match on PortalConversationStore::get /
ChannelSettingsStore::get) and reconstruct or look up the matched binding
settings (the same binding_settings used in the normal spawn path and referenced
later as binding_settings.as_ref()), then pass binding_settings.as_ref() into
ResolvedConversationSettings::resolve instead of None for every branch
(including the Ok(None) and Err branches), and ensure the resumed channel
inserted into active_channels carries those resolved binding-level defaults.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config/types.rs`:
- Around line 955-970: The ChannelConfig::to_conversation_settings helper
currently ignores the deprecated listen_only_mode when deciding whether to
return None and when filling response_mode; compute an effective_response_mode
first (use self.response_mode if Some, otherwise set to listen-only mode when
self.listen_only_mode is true, otherwise default), use that to decide the early
return (return None only when effective_response_mode is default and
save_attachments is false), and set response_mode to that effective value when
constructing ConversationSettings (replace the current unwrap_or_default usage).

---

Outside diff comments:
In `@src/main.rs`:
- Around line 1963-2020: The resume branch currently calls
ResolvedConversationSettings::resolve with None for the channel/binding
settings, dropping binding-level defaults; locate the resume block that builds
resolved_settings (the match on PortalConversationStore::get /
ChannelSettingsStore::get) and reconstruct or look up the matched binding
settings (the same binding_settings used in the normal spawn path and referenced
later as binding_settings.as_ref()), then pass binding_settings.as_ref() into
ResolvedConversationSettings::resolve instead of None for every branch
(including the Ok(None) and Err branches), and ensure the resumed channel
inserted into active_channels carries those resolved binding-level defaults.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab726b17-f76a-4688-b043-a1794a2955f8

📥 Commits

Reviewing files that changed from the base of the PR and between ac01c5b and 6e6f6c6.

📒 Files selected for processing (2)
  • src/config/types.rs
  • src/main.rs

Comment on lines +955 to +970
impl ChannelConfig {
/// Convert to a `ConversationSettings` for use as the agent-level default in
/// `ResolvedConversationSettings::resolve`. Returns `None` when both fields
/// match their system defaults (avoids injecting a no-op layer).
pub fn to_conversation_settings(
&self,
) -> Option<crate::conversation::ConversationSettings> {
if self.response_mode.is_none() && !self.save_attachments {
return None;
}
Some(crate::conversation::ConversationSettings {
response_mode: self.response_mode.unwrap_or_default(),
save_attachments: Some(self.save_attachments),
..Default::default()
})
}
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

Honor listen_only_mode when deriving the agent default.

This helper ignores the deprecated compatibility field completely. If a config still uses listen_only_mode = true with no explicit response_mode, Line 962 returns None, so the channel default is still dropped instead of producing the effective mode promised by the struct docs. Please derive the effective response mode from both fields before the early return.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config/types.rs` around lines 955 - 970, The
ChannelConfig::to_conversation_settings helper currently ignores the deprecated
listen_only_mode when deciding whether to return None and when filling
response_mode; compute an effective_response_mode first (use self.response_mode
if Some, otherwise set to listen-only mode when self.listen_only_mode is true,
otherwise default), use that to decide the early return (return None only when
effective_response_mode is default and save_attachments is false), and set
response_mode to that effective value when constructing ConversationSettings
(replace the current unwrap_or_default usage).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant