Skip to content

refactor: extract config.rs into focused submodules#306

Merged
jamiepine merged 3 commits intomainfrom
config-refactor
Mar 4, 2026
Merged

refactor: extract config.rs into focused submodules#306
jamiepine merged 3 commits intomainfrom
config-refactor

Conversation

@jamiepine
Copy link
Member

Summary

  • Mechanically extract the 8,324-line src/config.rs into 8 focused submodules under src/config/, with no behavior changes
  • All public type names remain stable via pub use re-exports from the module root — 127+ downstream importers are unaffected
  • Phase 1 of a larger config + runtime reload redesign

Module layout

File Lines Contents
config.rs 1,692 Module root, re-exports, tests (~1,650 lines)
types.rs 2,201 Domain types (Config, AgentConfig, Binding, messaging configs, etc.)
load.rs 2,084 Config::load, from_toml, validation, env/secret resolution
toml_schema.rs 709 Toml* serde structs and default_* helpers
watcher.rs 553 spawn_file_watcher
providers.rs 329 Provider constants, default_provider_config, routing bootstrap
permissions.rs 335 Discord/Slack/Telegram/Twitch permission builders
onboarding.rs 316 run_onboarding
runtime.rs 225 RuntimeConfig struct + impl

Verification

  • just gate-pr passes (fmt, clippy, cargo check --all-targets, 301 lib tests, integration test compile)
  • No mod.rs files — uses src/config.rs as module root per repo conventions

Mechanical module extraction of src/config.rs (8,324 lines) into 8
focused submodules with no behavior changes. All 301 tests pass,
all public type names remain stable via re-exports from the module root.

New layout:
  src/config.rs              — module root, re-exports, tests
  src/config/types.rs        — domain types (Config, AgentConfig, Binding, etc.)
  src/config/toml_schema.rs  — Toml* serde structs and defaults
  src/config/load.rs         — Config::load, from_toml, env/secret resolution
  src/config/providers.rs    — provider constants and bootstrap helpers
  src/config/runtime.rs      — RuntimeConfig + reload logic
  src/config/watcher.rs      — spawn_file_watcher
  src/config/permissions.rs  — Discord/Slack/Telegram/Twitch permission builders
  src/config/onboarding.rs   — run_onboarding
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 228656c and 5dc20c1.

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

Walkthrough

Adds a full, file-scoped configuration subsystem: TOML schema and domain types, multi-source loading and validation (file/env/secrets), provider/routing inference, interactive onboarding, runtime hot-reload state, permission extraction, and a filesystem watcher that triggers selective live reloads.

Changes

Cohort / File(s) Summary
TOML schema & deserialization
src/config/toml_schema.rs
Adds comprehensive TOML types, defaults, helpers, and custom Deserialize logic for LLM/provider blocks, telemetry, API/metrics, messaging, MCP, cron, bindings, and per-instance overrides.
Domain types & validation
src/config/types.rs
Introduces resolved domain model (Config, Llm/Provider, Defaults, Agent/Binding, Messaging, Mcp, Telemetry), validation, timezone/MCP resolution, SystemSecrets trait impls, adapter validation and routing resolution.
Config loading & secret resolution
src/config/load.rs
Implements config loading pipeline (file/env), secret resolution (resolve_env_value, set_resolve_secrets_store), OTLP/MCP parsing/validation, from_toml and validate_toml flows, and env-overrides with detailed validation/warnings.
Interactive onboarding
src/config/onboarding.rs
Adds run_onboarding CLI/browser flow to collect provider/agent/messaging info, create instance dir and write initial config.toml, supports provider-specific credential flows (API key, Ollama URL, Anthropic OAuth placeholder).
Provider utilities & routing
src/config/providers.rs
Adds provider base-URL constants, default_provider_config/add_shorthand_provider, openrouter extra headers, infer_routing_from_providers and resolve_routing merge logic.
Runtime hot-reload state
src/config/runtime.rs
Adds RuntimeConfig with ArcSwap-wrapped live fields, constructor, setters for cron/settings/secrets, reload_config to reconcile runtime state (including OpenCode pool and MCP reconciliation), and reload_identity/reload_skills.
Permissions extraction
src/config/permissions.rs
Adds per-adapter permission structs (Discord/Slack/Telegram/Twitch) and builders that derive guild/channel/workspace/chat filters and DM allowlists from Bindings for hot-reload usage.
File watcher & live reload orchestration
src/config/watcher.rs
Adds spawn_file_watcher to debounce FS events, detect config/identity/skills changes, suppress no-op reloads by content hash, update bindings/permissions, and selectively restart/reconfigure messaging adapters and per-agent runtime state.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: extracting a large config.rs file into focused submodules, which is the primary objective of the PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing module layout, verification details, and context for the refactoring effort.

✏️ 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 config-refactor

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.

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.

Actionable comments posted: 5

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

287-494: Consolidate duplicated provider bootstrap logic in load_from_env.

This section repeats nearly identical provider insertion blocks twice. It works because of or_insert_with, but it’s brittle and easy to desynchronize.

♻️ Refactor direction
-        // Populate providers from env vars (same as from_toml does)
-        if let Some(anthropic_key) = llm.anthropic_key.clone() { ... }
-        ...
-        if let Some(minimax_cn_key) = llm.minimax_cn_key.clone() { ... }
+        populate_llm_providers_from_keys(&mut llm, anthropic_from_auth_token);

Then keep a single helper (shared by load_from_env and from_toml) for provider map population.

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

In `@src/config/load.rs` around lines 287 - 494, The code duplicates provider
insertion logic in load_from_env; remove the repeated blocks and consolidate
into a single provider-population helper (e.g., populate_providers) that fills
llm.providers using the existing add_shorthand_provider and the
entry/or_insert_with patterns; update load_from_env to call this helper (and
also make from_toml call it) so all provider keys (anthropic, openrouter, kilo,
zhipu, zai-coding-plan, opencode-zen, opencode-go, minimax, minimax-cn, openai)
are added exactly once to llm.providers; keep existing symbols like
add_shorthand_provider, load_from_env, from_toml and the ProviderConfig
construction logic but move them into the single helper to avoid duplication.
src/config/toml_schema.rs (1)

546-611: Consider extracting shared email fields for future refactoring.

TomlEmailConfig and TomlEmailInstanceConfig share nearly identical fields. This duplication pattern (also present in Discord, Slack, Telegram, Twitch configs) is valid for multi-instance inheritance but could be consolidated in a future phase.

Since the PR describes this as Phase 1 with no behavior changes, this is fine to defer.

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

In `@src/config/toml_schema.rs` around lines 546 - 611, TomlEmailConfig and
TomlEmailInstanceConfig duplicate nearly all fields; extract the shared fields
into a new struct (e.g., TomlEmailCommon or TomlEmailFields) and embed it into
both configs (use #[serde(flatten)] to preserve the TOML shape) so
TomlEmailConfig and TomlEmailInstanceConfig keep only unique fields like
instances and name; update references to the moved fields accordingly
(types/field names: enabled, imap_host, imap_port, imap_username, imap_password,
imap_use_tls, smtp_host, smtp_port, smtp_username, smtp_password,
smtp_use_starttls, from_address, from_name, poll_interval_secs, folders,
allowed_senders, max_body_bytes, max_attachment_bytes).
🤖 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/onboarding.rs`:
- Around line 245-296: The code appends raw user strings into TOML via
config_content.push_str(&format!(...)) (see uses of toml_key, provider_value,
routing.channel/branch/worker/compactor/cortex, agent_id, discord.token,
guild_id and the channel/dm id lists), which can produce invalid TOML if inputs
contain quotes/newlines; change those direct interpolations to produce
TOML-escaped string literals instead — for each value (toml_key/provider_value,
routing.* fields, agent_id, discord.token, guild_id and every
channel_ids/dm_user_ids element) create a toml::Value::String (or otherwise use
toml::to_string on the string) and append the serialized/escaped result so the
pushed lines use properly quoted and escaped TOML strings rather than raw
format! interpolation.

In `@src/config/runtime.rs`:
- Around line 177-203: The reload_config path currently updates many fields but
omits the RuntimeConfig.opencode field; modify reload_config to store the new
opencode state (e.g., call self.opencode.store(Arc::new(resolved.opencode))) so
changes to [defaults.opencode] are applied at runtime, and if
opencode_server_pool parameters (path, permissions, max_servers) are intended to
be hot-reloadable, add logic in the same reload sequence to rebuild or swap the
opencode_server_pool (identify and reconcile via the opencode_server_pool
manager or field name) when resolved.opencode or its pool subfields change.

In `@src/config/types.rs`:
- Around line 119-128: ApiConfig and WebhookConfig currently derive Debug while
exposing the auth_token field; implement a custom Debug for both types that
omits or redacts the auth_token (e.g., show "<redacted>" or
"Some(***REDACTED***)" instead of the real token) so that when Config (which
derives Debug) is printed it cannot leak credentials; update the impl Debug for
ApiConfig and WebhookConfig to format all other fields normally but replace
auth_token with the redacted placeholder.

In `@src/config/watcher.rs`:
- Around line 41-57: The callback currently ignores the Err variant of the
Result<Event, notify::Error>, which swallows backend watcher failures; update
the closure that matches on result (the move |result: std::result::Result<Event,
notify::Error>| { ... }) to handle the Err(e) case instead of dropping it—log
the error (using the project's logging/tracing facility) with context like
"watcher callback error" and the error value, or otherwise propagate it if
appropriate; keep the existing tx.send(event) behavior for Ok(event) and only
use let _ = on tx.send where receiver may be dropped.
- Around line 198-230: When a platform block is removed the existing ArcSwap
held by discord_permissions / slack_permissions / telegram_permissions /
twitch_permissions remains stale; add an else branch for each platform check
(matching the pattern used for Discord) that stores a cleared/default
permissions snapshot into perms (e.g., replace the Arc with an empty/default
platform permissions instance) so the ArcSwap is updated even when
config.messaging.<platform> is None; update the SlackPermissions::from_config,
TelegramPermissions::from_config and TwitchPermissions::from_config blocks to
mirror the Discord pattern and store a cleared/default Arc when the
corresponding config is absent.

---

Nitpick comments:
In `@src/config/load.rs`:
- Around line 287-494: The code duplicates provider insertion logic in
load_from_env; remove the repeated blocks and consolidate into a single
provider-population helper (e.g., populate_providers) that fills llm.providers
using the existing add_shorthand_provider and the entry/or_insert_with patterns;
update load_from_env to call this helper (and also make from_toml call it) so
all provider keys (anthropic, openrouter, kilo, zhipu, zai-coding-plan,
opencode-zen, opencode-go, minimax, minimax-cn, openai) are added exactly once
to llm.providers; keep existing symbols like add_shorthand_provider,
load_from_env, from_toml and the ProviderConfig construction logic but move them
into the single helper to avoid duplication.

In `@src/config/toml_schema.rs`:
- Around line 546-611: TomlEmailConfig and TomlEmailInstanceConfig duplicate
nearly all fields; extract the shared fields into a new struct (e.g.,
TomlEmailCommon or TomlEmailFields) and embed it into both configs (use
#[serde(flatten)] to preserve the TOML shape) so TomlEmailConfig and
TomlEmailInstanceConfig keep only unique fields like instances and name; update
references to the moved fields accordingly (types/field names: enabled,
imap_host, imap_port, imap_username, imap_password, imap_use_tls, smtp_host,
smtp_port, smtp_username, smtp_password, smtp_use_starttls, from_address,
from_name, poll_interval_secs, folders, allowed_senders, max_body_bytes,
max_attachment_bytes).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b5bb90 and 2388368.

📒 Files selected for processing (9)
  • src/config.rs
  • src/config/load.rs
  • src/config/onboarding.rs
  • src/config/permissions.rs
  • src/config/providers.rs
  • src/config/runtime.rs
  • src/config/toml_schema.rs
  • src/config/types.rs
  • src/config/watcher.rs

Incorporates changes from main into config submodules:
- providers.rs: add X-Title legacy header for OpenRouter attribution
- types.rs: derive PartialEq/Eq on OpenCodeConfig
- load.rs: add warn_unknown_config_keys for unrecognised TOML keys
- runtime.rs: ArcSwap opencode_server_pool + hot-reload logic
- config.rs (tests): parking_lot mutex, ANTHROPIC_BASE_URL env guard,
  test isolation fixes, new warn_unknown_config_keys tests
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