Skip to content

fix(tray): persist module start/stop to [autostart] modules - #285

Merged
ErikBjare merged 4 commits into
masterfrom
fix/persist-tray-module-toggles
Oct 10, 2026
Merged

ErikBjare merged 4 commits into
masterfrom
fix/persist-tray-module-toggles

Conversation

@ErikBjare

@ErikBjare ErikBjare commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Addresses a user report with two halves:

  1. Turning watchers off in the tray doesn't stick across restarts.
  2. "after removing aw-watcher-window and aw-watcher-afk from the config files, they're still automatically enabled anytime activitywatch starts".

1. Tray toggles now persist (fixed here)

handle_system_click only changed the running state, so a tray toggle lasted for the session and the module came back on the next launch. Now, in both the GUI and mini mode, a tray start or stop adds the module to, or removes it from, [autostart] modules in config.toml (new src-tauri/src/config_edit.rs).

  • The file is re-read from disk and edited in place with toml_edit. Comments, layout and hand edits made while the app runs are kept. Comments on and around entries stay exactly once when adding or removing, with or without a trailing comma.
  • Both forms the config accepts are edited: inline modules = [...] (names and { name, args }) and [[autostart.modules]] tables.
  • A file that doesn't parse is left untouched (the failure is only logged).
  • Removing an entry with inline args moves them to [module_args], replacing any existing value there, since the inline args were the ones in effect. A later manual start still uses them.
  • Writes are atomic (temp file in the same dir, then rename; permissions kept, symlinks written through). This also applies to the autostart toggle and write_formatted_config. Writes share autostart::PERSIST_LOCK.
  • Tray clicks on names that aren't discovered modules are ignored.

aw-notify

aw-notify's autostart is governed by the shared settings.aw-notify.enabled opt-in flag (ActivityWatch/activitywatch#1435: single source of truth, managers start it if and only if it is set). So a Modules-menu click on aw-notify now goes through the same path as the "Enable notifications" toggle: write the flag, then start or stop it. It is never written to [autostart]. At launch aw-notify now follows the flag alone: it is started when the flag is on, even if unlisted, and skipped when it is off, even if listed. Previously it also had to be listed, so the dedicated toggle didn't survive a restart either.

2. Hand-edited removals reverting to defaults (root cause; not changed here)

This isn't caused by the config path or the per-profile path. Default and testing profiles still use the legacy activitywatch/aw-tauri/config.toml root, and the defaults are only written when the file is missing. The cause is the parse-failure fallback in get_config() (src-tauri/src/lib.rs:603-615 on master):

  • Any deserialize error makes it silently use UserConfig::default() for the whole session, which on macOS/Windows (and on Linux without aw-awatcher) is ["aw-watcher-afk", "aw-watcher-window"] (lib.rs:535, 568).
  • It never writes that back, so the file still looks edited.
  • In GUI mode it shows a one-off "Malformed config file" dialog that's easy to miss on a login autostart. In daemon or mini mode it only logs a warning.

AutostartConfig.modules has no #[serde(default)] (lib.rs:482), so the most natural ways to "remove" the watchers trigger that fallback. I checked with toml::from_str::<UserConfig>:

Edit Result
modules = [\n] OK, nothing autostarts
modules = [\n "aw-awatcher",\n] (trailing comma) OK
modules line deleted or commented out missing field modules, so defaults are used
a comma left behind ([ , ]) parse error, so defaults are used
a missing comma between entries parse error, so defaults are used
[autostart] section deleted missing field autostart, so defaults are used

Follow-up fix (separate PR): add #[serde(default)] on AutostartConfig.modules (and on autostart itself) so a missing list means "start nothing". On a real syntax error, don't autostart the default watchers: keep the defaults for port and paths but start no modules, and say so in the tray or notification instead of a one-off dialog.

With this PR, users no longer need to hand-edit the file to disable watchers. Also, the tray never writes over a malformed file.

Tests

  • config_edit::tests: one-per-line, single-line and array-of-tables edits; comments before, inside and after entries (with and without a trailing comma); inline args winning over [module_args]; a malformed config left alone; write_atomic (replace, create, no temp leftovers, symlink write-through). All of these use scratch dirs under the system temp dir and never the real config dir.
  • manager::tests: autostart_names_follow_notify_opt_in and tray_click_on_unknown_module_is_ignored.
  • cargo fmt --check, cargo clippy --all-targets -D warnings and cargo test --lib (60 tests) pass locally.

@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T12:45:05.411850Z c9af666 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 623b849bbd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lib.rs Outdated
@greptile-apps

greptile-apps Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Adds file-write library and refactors config persistence.

The PR appears safe to merge; all previous findings are addressed and no new actionable issues remain.

What we checked:

  • Config locks do not deadlock: Module clicks take the manager lock before PERSIST_LOCK. The other config-writing path does not take the manager lock, so there is no reverse waiting path.

Summary

Tray module choices now survive restarts in GUI and mini mode. Config edits preserve layout and comments, support both list forms, and retain module arguments.

  • aw-notify uses its shared notification flag instead of the autostart list.
  • Config writes replace files atomically and follow symlinks.
  • The latest change keeps toggles and saves in the same order and adds a duplicate-arguments regression test.
  • All five previous findings are addressed. No new actionable issues were found.
  • ErikBjare explicitly defers changes to the malformed-config fallback to a separate PR.
  • Tests were inspected, not run during this review.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Tray module click] --> B{aw-notify?}
  B -->|Yes| C[Save notification flag]
  C -->|Saved| D[Start or stop aw-notify]
  B -->|No| E[Start or stop discovered module]
  E --> F[Edit autostart modules while holding manager lock]
  F --> G[Write config atomically]
Loading

Reviews (4) · Last reviewed commit: "fix(tray): save module toggles under the..." · Reviewed by Greptile

Comment thread src-tauri/src/lib.rs Outdated
Comment thread src-tauri/src/lib.rs Outdated
Comment thread src-tauri/src/lib.rs Outdated
@TimeToBuildBob

TimeToBuildBob commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AI code review

This PR adds a new config_edit module that edits [autostart] modules in the user's config.toml in place using toml_edit, so tray toggles of modules persist across restarts. It also routes aw-notify clicks through the settings flag rather than the modules list, makes all config writes atomic via a new write_atomic function (temp file + rename with symlink resolution), and refactors handle_system_click to return an Option<bool> so unknown modules are ignored and module changes are persisted. GUI and mini mode now share a single handle_module_menu_click entry point.

Safe to merge — no P0/P1 findings

Confidence 5/5

⚠️ 1 generated file excluded and NOT reviewed: src-tauri/Cargo.lock (+2/-0 lines, 0KB). Machine-generated output is removed from the diff before review — from what the model sees and from the size gate alike — so nothing below says anything about it.

✅ No thread-worthy findings. Advisory notes follow; they are retained without opening review threads.

1 advisory finding (summary-only, not scored)

These P2 guard, heuristic, trade-off, or documentation claims are retained for judgment without opening review threads.

⚠️ P2 medium — src-tauri/src/config_edit.rs

This is a fix(...) PR but no test files are included in the diff. Erik's feedback: 'where is the repro & fixes they are supposed to catch' (gptme#3441), 'that measurement should come with a regression test' (gptme#3446). Add a test that would have caught this bug. (Advisory: Erik merged all such PRs but consistently requested tests.)

Add a test file that reproduces the bug before the fix and passes after it.

How this was verified: static preflight: fix-commit + touched-files scan (rule 7)

Files changed (6) — the diff as I read it
  • src-tauri/Cargo.toml — Adds the toml_edit dependency (0.25).
  • src-tauri/src/autostart.rs — Makes PERSIST_LOCK pub(crate), updates its doc comment, and switches persist_enabled to use config_edit::write_atomic.
  • src-tauri/src/config_edit.rs — New module: write_atomic (atomic replace with symlink resolution), persist_module_autostart/click, and set_module_autostart for inline arrays and arrays of tables, plus extensive tests.
  • src-tauri/src/lib.rs — Registers config_edit module, uses write_atomic in write_formatted_config, and routes tray module clicks to manager::handle_module_menu_click.
  • src-tauri/src/manager.rs — handle_system_click now returns Option<bool> and rejects unknown modules; adds NOTIFY_MODULE, autostart_names, and handle_module_menu_click; set_notify_enabled drops the unused AppHandle parameter.
  • src-tauri/src/mini.rs — Routes mini-mode module menu clicks through manager::handle_module_menu_click instead of calling handle_system_click directly.
Previous review passes
commit score findings engine when
623b849bbd7b 4/5 1 llm 2026-10-07 10:19 UTC
6a39182251c5 3/5 2 llm 2026-10-07 12:52 UTC
d39dd4b20c87 5/5 0 llm 2026-10-07 13:04 UTC

Reviewed d39dd4b20c87 · openrouter/deepseek/deepseek-v4-flash-0731 · llm engine · 591s · about this reviewer

Maintainer commands

@TimeToBuildBob review (own line) — fresh review · @TimeToBuildBob fix — a worker acts on the findings. Once per comment; 👀 = received.

Comment thread src-tauri/src/lib.rs Outdated
ErikBjare added a commit to ActivityWatch/aw-qt that referenced this pull request Oct 7, 2026
* test: isolate platform dirs in all tests, not just XDG on Linux

The xdg_tmp fixture only sets XDG_*_HOME, which platformdirs ignores on
macOS and Windows. There, test_profile_config.py wrote its fixture files
into the real ActivityWatch config dirs (aw-qt.toml, aw-server.toml,
aw-server-rust/config.toml, ...), overwriting the user's config.

An autouse fixture now points platformdirs' user dirs into tmp_path on
every platform, laid out like the XDG dirs so existing tests are unchanged.

* feat(tray): persist module start/stop to autostart_modules

A module started or stopped from the tray now stays that way after a
restart: the click adds the module to, or removes it from,
autostart_modules in aw-qt.toml. Mirrors ActivityWatch/aw-tauri#285.

- Only explicit menu clicks persist. Crash auto-restarts, the restart
  dialog and shutdown call Module.start/stop directly and never write.
- aw-server and aw-server-rust are never written, so stopping the server
  from the tray can't leave the next start without one.
- With --autostart-modules the run isn't using the configured list, so
  toggles only change what is running.
- The file is re-read right before each write and edited in place with
  tomlkit (comments and layout kept), in the same file and section
  AwQtSettings reads for the profile. A file that fails to parse is left
  untouched and the failure logged. Writes are atomic and follow symlinks.
- Enable appends, disable removes; order is kept and no duplicates are
  added. A section without the key (the commented-out first-run file)
  gets the effective default list with the change applied.

* fix(tray): address review: UTF-8 I/O, shadowed system copies, slot safety

- Read and write aw-qt.toml as UTF-8 with newline="" (keeps line
  endings); an undecodable file is logged and left untouched.
- Don't persist a click on a system module that a bundled copy of the
  same name shadows: autostart_modules holds names, and autostart runs
  the bundled copy. Manager.resolve() is the shared lookup.
- Catch any error from persisting so it can't escape the Qt slot.
- Skip the symlink test where symlinks can't be created (Windows).
@ErikBjare
ErikBjare force-pushed the fix/persist-tray-module-toggles branch from 623b849 to df4b4ba Compare October 7, 2026 12:15
@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@TimeToBuildBob review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df4b4ba6fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/config_edit.rs Outdated
Comment thread src-tauri/src/config_edit.rs
Comment thread src-tauri/src/config_edit.rs Outdated
@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@TimeToBuildBob review

Comment thread src-tauri/src/config_edit.rs
Comment thread src-tauri/src/manager.rs
@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@TimeToBuildBob review

Starting or stopping a module from the tray (GUI and mini mode) now adds
it to or removes it from `[autostart] modules` in config.toml, so the
choice survives a restart. Previously a tray toggle only lasted for the
session, and removed watchers came back on the next launch.

The file is re-read and edited in place with toml_edit, so comments and
hand edits are preserved; a malformed file is left untouched. Removing an
entry with inline args moves them to `[module_args]`. Writes share the
autostart PERSIST_LOCK. Tray clicks on undiscovered modules are ignored.
- Edit `[[autostart.modules]]` tables as well as inline `modules = [...]`
  arrays; an emptied array of tables is written back as `modules = []`.
- Keep comments on and around entries exactly once when adding or
  removing, with or without a trailing comma, and in single-line arrays.
- When removing an entry with inline args, its args replace any existing
  `[module_args]` value (they were the ones in effect).
- aw-notify clicks in the Modules menu (GUI and mini) now go through the
  `settings.aw-notify.enabled` flag like the "Enable notifications" toggle,
  and are never written to `[autostart]`. At launch aw-notify follows the
  flag alone: started when it is on (even if unlisted), skipped when off.
- Write the config atomically (temp file + rename, keeping permissions and
  writing through symlinks), for tray edits, the autostart toggle and
  write_formatted_config.
- Move the config-editing code into config_edit.rs.
…ling config symlinks

When a module is listed more than once, removing it saves the last
entry's args (the effective ones, per configured_modules_args) to
[module_args], not the first's, for both list forms.

write_atomic resolves symlinks with read_link instead of canonicalize, so
a config.toml that links to a not-yet-created file keeps the link and
gets its target created on first run.
… args case

Keep the manager state lock held while saving a Modules-menu toggle, so
saves can never be reordered relative to the toggles that caused them.

Add a test for a module listed twice where only the earlier entry has
args: removal saves those args over the [module_args] value.
@ErikBjare
ErikBjare force-pushed the fix/persist-tray-module-toggles branch from d39dd4b to c9af666 Compare October 10, 2026 12:41
@ErikBjare
ErikBjare merged commit 9460d90 into master Oct 10, 2026
7 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.

2 participants