Skip to content

fix: hot-reload opencode settings in active agents#302

Merged
jamiepine merged 4 commits intomainfrom
fix/opencode-settings-hot-reload
Mar 3, 2026
Merged

fix: hot-reload opencode settings in active agents#302
jamiepine merged 4 commits intomainfrom
fix/opencode-settings-hot-reload

Conversation

@jamiepine
Copy link
Member

@jamiepine jamiepine commented Mar 3, 2026

Summary

  • Reload runtime config immediately after update_global_settings writes config.toml, so settings changes apply to live agents without waiting on file-watcher timing.
  • Include defaults.opencode in RuntimeConfig::reload_config, so opencode.enabled toggles update channel prompts/tool definitions and worker dispatch behavior without a restart.
  • Strengthen channel and tool guidance so OpenCode usage is explicit: when claiming OpenCode, spawn_worker must include worker_type: \"opencode\" plus directory, and coding-heavy tasks default to OpenCode when enabled.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

Walkthrough

Adds immediate post-write config reloads in the settings API, makes OpenCode defaults and server-pool rebuilds part of runtime config reloads, switches opencode server pool storage to ArcSwap, derives equality for OpenCode config/permissions, and clarifies OpenCode worker guidance in templates and spawn_worker descriptions/schema.

Changes

Cohort / File(s) Summary
Settings API Reload
src/api/settings.rs
After writing config (both structured and raw), load the new config from disk and attempt to call runtime_config.reload_config(&new_config, &agent_id, &mcp_manager) for each runtime/mcp target; failures are logged as warnings and do not fail the API call.
Runtime Config & OpenCode pool
src/config.rs
OpenCodeConfig now derives PartialEq, Eq. RuntimeConfig now stores the opencode pool as ArcSwap<OpenCodeServerPool> and initializes with ArcSwap::from_pointee(...). Reload logic updates self.opencode, compares old vs new opencode fields, and conditionally rebuilds the OpenCode server pool when relevant fields changed.
Opencode types equality
src/opencode/types.rs
OpenCodePermissions now derives PartialEq and Eq to enable equality comparisons.
Consumer of opencode pool
src/agent/channel_dispatch.rs
Access changed from cloning the stored pool to using rc.opencode_server_pool.load().clone() when spawning Opencode workers (adjusts retrieval to ArcSwap usage).
Spawn worker text/schema
src/tools/spawn_worker.rs, prompts/en/tools/spawn_worker_description.md.j2
Clarified opencode note: require worker_type: "opencode" and a directory to claim OpenCode; omission defaults to builtin worker. Updated descriptive text and schema guidance.
Prompts / Worker guidance
prompts/en/fragments/worker_capabilities.md.j2
Added explicit guidance to prefer OpenCode for coding-heavy tasks when enabled and require worker_type: "opencode" plus directory in tool calls; note omission spawns builtin worker.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: hot-reload opencode settings in active agents' accurately summarizes the primary change: enabling immediate hot-reloading of OpenCode settings for live agents instead of waiting for file-watcher timing.
Description check ✅ Passed The description clearly relates to the changeset, detailing three interconnected improvements: immediate config reload post-write, OpenCode config inclusion in runtime reload, and strengthened OpenCode usage guidance.
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 (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/opencode-settings-hot-reload

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.

@jamiepine jamiepine marked this pull request as ready for review March 3, 2026 20:48
Copy link
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.

🧹 Nitpick comments (1)
src/api/settings.rs (1)

340-351: Consider extracting the reload logic into a helper function.

This reload block is duplicated nearly verbatim at lines 461–489 in update_raw_config. Extracting it into a shared helper (e.g., reload_runtime_configs) would reduce duplication and centralize the reload behavior.

♻️ Suggested helper extraction
async fn reload_runtime_configs(
    state: &ApiState,
    config_path: &std::path::Path,
) {
    match crate::config::Config::load_from_path(config_path) {
        Ok(new_config) => {
            let runtime_configs = state.runtime_configs.load();
            let mcp_managers = state.mcp_managers.load();
            let reload_targets = runtime_configs
                .iter()
                .filter_map(|(agent_id, runtime_config)| {
                    mcp_managers.get(agent_id).map(|mcp_manager| {
                        (
                            agent_id.clone(),
                            runtime_config.clone(),
                            mcp_manager.clone(),
                        )
                    })
                })
                .collect::<Vec<_>>();
            drop(runtime_configs);
            drop(mcp_managers);

            for (agent_id, runtime_config, mcp_manager) in reload_targets {
                runtime_config
                    .reload_config(&new_config, &agent_id, &mcp_manager)
                    .await;
            }
        }
        Err(error) => {
            tracing::warn!(%error, "config written but failed to reload immediately");
        }
    }
}

Then both call sites simplify to:

reload_runtime_configs(&state, &config_path).await;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/settings.rs` around lines 340 - 351, The duplicated reload logic that
builds reload_targets from runtime_configs and mcp_managers appears twice (once
here and again in update_raw_config); extract it into a shared async helper
named e.g. reload_runtime_configs(state: &ApiState, config_path: &Path) that
loads the new Config, captures state.runtime_configs.load() and
state.mcp_managers.load(), builds the same reload_targets (using
agent_id.clone(), runtime_config.clone(), mcp_manager.clone()), drops the
guards, iterates and calls runtime_config.reload_config(&new_config, &agent_id,
&mcp_manager).await for each target, and logs on Err; then replace both inline
blocks with a single await call to reload_runtime_configs(&state,
&config_path).await to remove duplication and centralize behavior (references:
runtime_configs, mcp_managers, runtime_config.reload_config, update_raw_config).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/api/settings.rs`:
- Around line 340-351: The duplicated reload logic that builds reload_targets
from runtime_configs and mcp_managers appears twice (once here and again in
update_raw_config); extract it into a shared async helper named e.g.
reload_runtime_configs(state: &ApiState, config_path: &Path) that loads the new
Config, captures state.runtime_configs.load() and state.mcp_managers.load(),
builds the same reload_targets (using agent_id.clone(), runtime_config.clone(),
mcp_manager.clone()), drops the guards, iterates and calls
runtime_config.reload_config(&new_config, &agent_id, &mcp_manager).await for
each target, and logs on Err; then replace both inline blocks with a single
await call to reload_runtime_configs(&state, &config_path).await to remove
duplication and centralize behavior (references: runtime_configs, mcp_managers,
runtime_config.reload_config, update_raw_config).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69627b4 and 778f65d.

📒 Files selected for processing (2)
  • src/api/settings.rs
  • src/config.rs

drop(runtime_configs);
drop(mcp_managers);

for (agent_id, runtime_config, mcp_manager) in reload_targets {
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor perf thing: this is a serial .await loop. If reload_targets can get large, bounded concurrency (or join_all) might keep this endpoint snappy.

Copy link
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.

🧹 Nitpick comments (1)
src/config.rs (1)

5644-5645: Align pool initialization comment with actual behavior.

The field comment says lazy init, but RuntimeConfig::new eagerly constructs OpenCodeServerPool. Please either update the comment or defer creation to first use.

Also applies to: 5672-5677, 5706-5706


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e053f0 and f935cca.

📒 Files selected for processing (4)
  • src/agent/channel_dispatch.rs
  • src/api/settings.rs
  • src/config.rs
  • src/opencode/types.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api/settings.rs

@jamiepine jamiepine merged commit d682996 into main Mar 3, 2026
3 of 4 checks passed
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