Skip to content

[settings-update-workflow]: Settings update workflow and runtime freshness#71

Open
archae0pteryx wants to merge 2 commits into
mainfrom
feat/settings-update-workflow
Open

[settings-update-workflow]: Settings update workflow and runtime freshness#71
archae0pteryx wants to merge 2 commits into
mainfrom
feat/settings-update-workflow

Conversation

@archae0pteryx

@archae0pteryx archae0pteryx commented May 22, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Added a settings workflow module that persists first, commits runtime settings, applies display/widget effects, and rebuilds the tray through one interface.
  • Changed Commands and CapEvaluator to read Settings through a live provider instead of startup-copied values.
  • Updated tray rebuild/badge logic to use current caps and marked issue 050 complete; issue 031 now blocks on it.

Issue

  • issues/050-settings-update-workflow.md

Completion promise

ui_bridge::update_settings delegates to a settings workflow module; settings persistence, runtime settings freshness, display effects, cap/notification behavior, and tray rebuild ordering are tested through one interface.

Validation

  • rtk task check

Summary by CodeRabbit

  • Bug Fixes
    • Settings updates now take immediate effect across the application, including notification toggles, display configuration, and tray menu refresh, eliminating delays in runtime configuration changes.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@archae0pteryx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 56 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e143cd4f-8d0e-4a90-be8c-10148aaf25bd

📥 Commits

Reviewing files that changed from the base of the PR and between b6eaf33 and e0413e9.

📒 Files selected for processing (4)
  • crates/commands/src/caps.rs
  • crates/commands/src/lib.rs
  • src-tauri/src/app/mod.rs
  • src-tauri/src/app/tray.rs
📝 Walkthrough

Walkthrough

This PR fixes runtime settings freshness by introducing a SettingsProvider closure pattern that reads settings dynamically rather than storing startup snapshots. It refactors settings updates through a composable SettingsWorkflow abstraction, ensuring notification toggles, caps, displays, and tray immediately reflect preference changes without requiring app restart.

Changes

Runtime Settings Freshness & Workflow Refactor

Layer / File(s) Summary
Problem context and scope
issues/031-notification-source-settings.md, issues/050-settings-update-workflow.md, issues/README.md
Issue 050 is elevated to priority queue, describing a runtime freshness bug where startup-cloned Settings in long-lived adapters become stale after preference changes. Acceptance criteria require immediate propagation: notification source toggles take effect on next evaluation, cap state and tray reflect current settings, and displays reapply only on relevant changes.
SettingsProvider type and Commands integration
crates/commands/src/lib.rs
SettingsProvider is defined as an Arc-wrapped closure returning Settings. Commands stores a SettingsProvider instead of static Settings, and settings() invokes the provider on each call. Tests verify caps() reflects updates to a mutex-backed settings source.
CapEvaluator and focus command provider integration
crates/commands/src/caps.rs, crates/commands/src/focus.rs
CapEvaluator stores and invokes SettingsProvider during evaluate() to fetch current settings for cap-state and notifier-callback gating. Commands::caps calls self.settings().caps instead of field access. Tests confirm notification source toggles read from the latest provider output.
Settings workflow abstraction and implementations
src-tauri/src/app/settings_workflow.rs
New module defines SettingsPersistence, SettingsRuntime, and SettingsEffects traits for composable update pipelines. SettingsWorkflow orchestrates: persist → commit to runtime → apply widget effects → conditionally reapply displays (only if changed) → refresh consumers → rebuild tray. Provides filesystem persistence, shared mutex-backed runtime, and Tauri side-effect implementations. Tests verify failure isolation and correct effect sequencing.
Tray rebuild handler provider integration
src-tauri/src/app/tray.rs
rebuild_handler accepts SettingsProvider and computes "over cap" tray state via fresh settings().caps. rebuild_tray_menu derives caps from CommandsState with safe fallback, ensuring tray icon reflects current settings.
App setup and provider wiring
src-tauri/src/app/mod.rs
Initializes shared settings_state mutex and builds a settings_provider closure returning current state. Wires provider into Commands and CapEvaluator constructors. Managed SettingsState references the shared source instead of a clone. Tray handler receives the provider to access fresh settings on each rebuild.
UI bridge delegation to workflow
src-tauri/src/ui_bridge/mod.rs
update_settings delegates entire flow (persist, apply effects, rebuild tray) to workflow_for_app(...).update(settings), removing 44 lines of inline orchestration and mapping errors as strings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • killallgit/adhd-ranch#53: Modifies CapEvaluator::evaluate notification-callback gating in parallel; main PR changes how settings are fetched (via SettingsProvider), while #53 changes which toggles gate each notification source.
  • killallgit/adhd-ranch#34: Both PRs modify the settings update path—main PR refactors runtime freshness via SettingsProvider and settings_workflow orchestration, while #34 introduces update_settings commands that trigger persistence and UI effects—creating a direct overlap in src-tauri/src/ui_bridge/mod.rs and tray rebuild wiring.

Poem

🐰 Settings now flow like fresh carrots, not stale hay—
Each call grabs the latest, no waiting for restart's day.
Workflow orchestrates effects in perfect sequence true,
Notification toggles dance, and the tray wears settings new.
No snapshots linger, fresh settings for me and you! 🥬

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.21% 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 clearly and specifically describes the main changes: introducing a settings update workflow and addressing runtime freshness. It directly corresponds to the PR's primary objective of adding the settings workflow module and ensuring settings reflect current state rather than startup-copied values.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/settings-update-workflow

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.

@archae0pteryx

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@archae0pteryx archae0pteryx marked this pull request as ready for review May 22, 2026 05:24
@coderabbitai

coderabbitai Bot commented May 22, 2026

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
crates/commands/src/lib.rs (1)

25-66: 🧹 Nitpick | 🔵 Trivial | 🏗️ Heavy lift

Use an explicit trait at the module boundary instead of a callable-based SettingsProvider alias.

SettingsProvider is currently Arc<dyn Fn() -> Settings + Send + Sync> and is stored/called in Commands and CapEvaluator, then passed across the src-tauri boundary (and called as settings().caps in src-tauri/src/app/tray.rs::rebuild_handler). Introduce a SettingsReader trait (fn get(&self) -> Settings) and depend on Arc<dyn SettingsReader> instead.

♻️ Suggested direction
+pub trait SettingsReader: Send + Sync {
+    fn get(&self) -> Settings;
+}
+
+impl<F> SettingsReader for F
+where
+    F: Fn() -> Settings + Send + Sync,
+{
+    fn get(&self) -> Settings {
+        self()
+    }
+}
-
-pub type SettingsProvider = Arc<dyn Fn() -> Settings + Send + Sync>;
+pub type SettingsProvider = Arc<dyn SettingsReader>;
...
-        (self.settings)()
+        self.settings.get()
  • Update remaining call sites like settings().caps in src-tauri/src/app/tray.rs to use settings.get().caps.
  • Keep the existing closure-based wiring from src-tauri/src/app/mod.rs—it will still coerce to dyn SettingsReader via the blanket impl.
🤖 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 `@crates/commands/src/lib.rs` around lines 25 - 66, Replace the callable alias
SettingsProvider with a small trait: introduce trait SettingsReader { fn
get(&self) -> Settings; } and change the stored type on Commands (and
CapEvaluator) from SettingsProvider to Arc<dyn SettingsReader + Send + Sync>;
update constructors new and new_with_settings_provider to accept Arc<dyn
SettingsReader> (or rename new_with_settings_reader) and wire existing call
sites to call settings.get() instead of invoking the closure (e.g. change
settings().caps to settings.get().caps); add a blanket impl so existing closure
wiring still works (impl<F: Fn() -> Settings + Send + Sync> SettingsReader for F
{ fn get(&self) -> Settings { (self)() } }) and update all references and
imports accordingly.
🤖 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-tauri/src/app/mod.rs`:
- Around line 95-103: The current settings_provider closure silently falls back
to Settings::default() when the settings_state mutex is poisoned (due to
unwrap_or_default), which can flip runtime caps/notifications/tray state; change
the lock handling in the closure used to build settings_provider (type
adhd_ranch_commands::SettingsProvider, capturing settings_state) to explicitly
handle a poisoned lock: on Ok return the cloned settings, on Err recover the
inner value via the poisoning error's into_inner() and return that recovered
settings while also logging a warning (use the project logger/tracing or
log::warn) that the mutex was poisoned and a recovered value is being used
instead of defaulting. Ensure you remove the unwrap_or_default and replace it
with this match/unwrap_on_poison logic so defaults are not silently substituted.

---

Outside diff comments:
In `@crates/commands/src/lib.rs`:
- Around line 25-66: Replace the callable alias SettingsProvider with a small
trait: introduce trait SettingsReader { fn get(&self) -> Settings; } and change
the stored type on Commands (and CapEvaluator) from SettingsProvider to Arc<dyn
SettingsReader + Send + Sync>; update constructors new and
new_with_settings_provider to accept Arc<dyn SettingsReader> (or rename
new_with_settings_reader) and wire existing call sites to call settings.get()
instead of invoking the closure (e.g. change settings().caps to
settings.get().caps); add a blanket impl so existing closure wiring still works
(impl<F: Fn() -> Settings + Send + Sync> SettingsReader for F { fn get(&self) ->
Settings { (self)() } }) and update all references and imports accordingly.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 30067aa5-36c5-4c9e-a193-d38fb746c611

📥 Commits

Reviewing files that changed from the base of the PR and between a974b9d and b6eaf33.

📒 Files selected for processing (10)
  • crates/commands/src/caps.rs
  • crates/commands/src/focus.rs
  • crates/commands/src/lib.rs
  • issues/031-notification-source-settings.md
  • issues/050-settings-update-workflow.md
  • issues/README.md
  • src-tauri/src/app/mod.rs
  • src-tauri/src/app/settings_workflow.rs
  • src-tauri/src/app/tray.rs
  • src-tauri/src/ui_bridge/mod.rs

Comment thread src-tauri/src/app/mod.rs
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