Skip to content

[codex] Stabilize web settings LLM hot reload - #2765

Merged
henrypark133 merged 5 commits into
stagingfrom
e2e/web-settings
Apr 21, 2026
Merged

henrypark133 merged 5 commits into
stagingfrom
e2e/web-settings

Conversation

@henrypark133

Copy link
Copy Markdown
Collaborator

What changed

  • refactored the web settings hot-reload path to resolve only LlmConfig while preserving the same owner/admin DB merge semantics used at startup
  • restored dotenv/bootstrap env refresh inside the LLM-only resolver so hot reload still sees fresh ./.env and ~/.ironclaw/.env state
  • hardened the hot-reload tests to isolate ambient env and TOML state by locking env access and injecting an empty temp TOML config

Why

The failing web settings tests were exercising the LLM provider hot-reload path. The fix needed to keep owner-scope layering intact without rebuilding unrelated config sections, while also preserving the previous behavior where a settings-triggered reload would re-read env-file state.

User impact

LLM settings changes in the web UI now rebuild the provider chain against the same effective configuration that startup uses, including owner overlays and refreshed env-backed credentials/base URLs.

Root cause

The original hot-reload path rebuilt full config from the owner scope. The refactor to narrow that down to LLM-only resolution fixed the owner/admin layering issue, but initially dropped the dotenv/bootstrap refresh step. That caused hot reload to resolve against stale startup env when env files had changed.

Validation

  • cargo fmt --all
  • git diff --check
  • CARGO_TARGET_DIR=/tmp/ironclaw-settings-test-target cargo test re_resolve_llm_keeps_admin_only_keys_for_operator -- --nocapture
  • CARGO_TARGET_DIR=/tmp/ironclaw-settings-test-target cargo test settings_set_handler_triggers_llm_provider_hot_reload -- --nocapture
  • CARGO_TARGET_DIR=/tmp/ironclaw-settings-test-target cargo test --lib settings_set_handler_owner_scope_triggers_reload -- --nocapture
  • CARGO_TARGET_DIR=/tmp/ironclaw-settings-test-target cargo test --lib reload_rebuilds_from_owner_scope_not_effective_scope -- --nocapture
  • CARGO_TARGET_DIR=/tmp/ironclaw-settings-test-target cargo test --lib settings_set_handler_rolls_back_on_reload_failure -- --nocapture

@github-actions github-actions Bot added scope: channel/web Web gateway channel size: M 50-199 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Apr 20, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the LLM hot-reload mechanism to resolve only the LLM configuration instead of the full application config, centralizing logic in new resolve_llm_with_secrets and load_db_backed_settings methods. Feedback indicates that environment-loading calls within the hot-reload path should be removed to ensure thread safety and adhere to the requirement that hot-reloads only process database-persisted settings.

Comment thread src/config/mod.rs Outdated
@henrypark133
henrypark133 marked this pull request as ready for review April 20, 2026 22:38
Copilot AI review requested due to automatic review settings April 20, 2026 22:38

Copilot AI 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.

Pull request overview

This PR stabilizes the web-settings-triggered LLM provider hot-reload by re-resolving only LlmConfig while preserving the same DB/TOML layering semantics used at startup, and by hardening the associated tests against ambient env/TOML state.

Changes:

  • Refactors DB-backed settings merge logic into a shared helper and reuses it for both full config loads and LLM-only resolution.
  • Updates the web settings reload path to resolve LlmConfig directly (including secrets hydration) instead of rebuilding full Config.
  • Hardens hot-reload tests by locking env access and ensuring an explicit (empty) TOML config file exists when a TOML path is injected.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/config/mod.rs Extracts shared DB/TOML/admin/user settings layering and introduces an LLM-only resolver used by hot reload.
src/channels/web/features/settings/mod.rs Switches reload path to use the LLM-only resolver; strengthens tests with env locking and explicit empty TOML injection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/config/mod.rs
Comment thread src/config/mod.rs Outdated
Copilot AI review requested due to automatic review settings April 20, 2026 22:55
@github-actions github-actions Bot added size: L 200-499 changed lines and removed size: M 50-199 changed lines labels Apr 20, 2026

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/config/mod.rs
Comment thread src/channels/web/features/settings/mod.rs
Comment thread src/config/mod.rs
Copilot AI review requested due to automatic review settings April 20, 2026 23:42

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/config/mod.rs
Comment on lines +415 to +418
Err(e) if strict_db_reads => {
return Err(ConfigError::ParseError(format!(
"Failed to load admin-scope settings from DB: {e}"
)));

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

In strict_db_reads mode, DB read failures are surfaced as ConfigError::ParseError. Because ConfigError::ParseError formats as "Failed to parse configuration: ...", callers (and the settings UI via 422 body) will see a misleading parse error for what is actually a DB connectivity/query failure. Consider introducing a dedicated ConfigError variant for config-source read failures (or otherwise returning an error that doesn’t prefix with parse semantics) so user-facing error strings are accurate.

Copilot uses AI. Check for mistakes.
Comment thread src/config/mod.rs

/// Build the settings overlay used for DB-backed config reads.
///
/// Resolution order is profile -> TOML -> admin DB -> per-user DB.

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

The doc comment for load_db_backed_settings says resolution order starts at "profile", but the implementation begins from Settings::default() and then applies the profile and overlays. Update the comment to include the defaults layer so it matches the actual merge stack.

Suggested change
/// Resolution order is profile -> TOML -> admin DB -> per-user DB.
/// Resolution order is defaults -> profile -> TOML -> admin DB -> per-user DB.

Copilot uses AI. Check for mistakes.

@henrypark133 henrypark133 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What looks good:

  • The refactor narrows hot reload to LlmConfig instead of rebuilding unrelated config.
  • The owner/admin layering is now centralized in one resolver path instead of being duplicated between full-config and hot-reload flows.
  • The hot-reload path now fails closed on DB read errors, which matches the review concern and preserves rollback behavior.

No verified findings.

Low-priority notes:

  • src/config/mod.rs plus src/channels/web/features/settings/mod.rs: the DB-read failure behavior looks fixed in code, but there still is not one end-to-end handler test that injects get_all_settings failure and proves 422 + rollback through settings_set_handler.

Summary:

  • Recommended verdict: Approve
  • Prior feedback status: partially unresolved
  • Residual risk: reviewed diff-only because sandboxed git fetch could not update .git/FETCH_HEAD, and the targeted reload-path tests were inspected but not executed locally.

@nickpismenkov nickpismenkov 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.

lgtm

@henrypark133

Copy link
Copy Markdown
Collaborator Author

already got an approval (look at comment above just not an approval click overriding and merging)

@henrypark133
henrypark133 merged commit 6d4935a into staging Apr 21, 2026
21 checks passed
@henrypark133
henrypark133 deleted the e2e/web-settings branch April 21, 2026 02:08
theredspoon pushed a commit to theredspoon/ironclaw that referenced this pull request Jun 21, 2026
* Stabilize web settings LLM hot reload

* Keep web settings hot reload DB-scoped

* fix(config): preserve TOML overlay in llm re-resolve

* test(config): allow env lock in async toml re-resolve test

* fix(web): fail closed on hot reload db read errors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: medium Business logic, config, or moderate-risk modules scope: channel/web Web gateway channel size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants