Skip to content

fix(macos): fall back to jxa when exclude_title/exclude_titles configured#120

Open
TimeToBuildBob wants to merge 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/exclude-title-swift-macos-1141
Open

fix(macos): fall back to jxa when exclude_title/exclude_titles configured#120
TimeToBuildBob wants to merge 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/exclude-title-swift-macos-1141

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Contributor

@TimeToBuildBob TimeToBuildBob commented Mar 4, 2026

Problem

On macOS, the default strategy is swift, which spawns a native binary subprocess that sends heartbeats directly to the AW server. This subprocess bypasses all Python filtering, so exclude_title and exclude_titles settings are silently ignored.

Reported in: ActivityWatch/activitywatch#1141

Root Cause

In main.py:

if sys.platform == 'darwin' and args.strategy == 'swift':
    # Swift binary runs and sends heartbeats directly — no Python filtering
    p = subprocess.Popen([binpath, server_address, bucket_id, ...])
    p.wait()
else:
    # Python heartbeat_loop handles exclude_title/exclude_titles
    heartbeat_loop(..., exclude_title=args.exclude_title, ...)

The Swift binary accepts only positional args (server address, bucket ID, hostname, client name), with no support for exclusion flags.

Fix

Before spawning the Swift binary, check if title exclusion is configured. If so, log a warning and fall back to the jxa strategy, which runs through heartbeat_loop and properly applies the filtering.

This is a minimal, safe fix — no Swift code changes required. Users who rely on exclude_title get working behavior with a clear log message. Users who don't configure exclusions continue using swift unchanged.

Testing

  • Verified Python syntax with ast.parse
  • The logic change is straightforward: the early-exit condition prevents the swift path when exclusions are active

Alternative Considered

Adding native --exclude-title support to the Swift binary — more work, deferred.


Important

Add fallback to jxa strategy in main.py for macOS when exclude_title or exclude_titles is configured, bypassing unsupported swift strategy.

  • Behavior:
    • In main.py, added a check to fall back to jxa strategy if exclude_title or exclude_titles is configured on macOS, instead of using swift strategy.
    • Logs a warning when falling back to jxa strategy due to unsupported exclusions in swift.
  • Misc:
    • No changes to Swift binary; the fix is entirely in Python.
    • Verified logic with ast.parse for syntax correctness.

This description was created by Ellipsis for 2a4b330. You can customize this summary. It will automatically update as commits are pushed.

The swift strategy uses a native binary subprocess that sends heartbeats
directly to the AW server, bypassing all Python filtering. When a user
sets exclude_title=true or exclude_titles=[...] in the config, the swift
strategy silently ignores these settings.

Fix: detect title exclusion config before starting, and fall back to the
jxa strategy with a warning message. The jxa strategy runs through
Python's heartbeat_loop which properly applies exclude_title filtering.

Fixes ActivityWatch/activitywatch#1141

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 2a4b330 in 8 seconds. Click for details.
  • Reviewed 24 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_zBqyCHtf2dFKw3cB

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR fixes a long-standing macOS-only bug where exclude_title/exclude_titles settings were silently ignored when using the default swift strategy, because the Swift binary bypasses the Python heartbeat_loop entirely. The fix is minimal: it adds an early check before the swift path and mutates args.strategy to "jxa" when any title-exclusion is active, causing execution to fall through to heartbeat_loop which properly applies the filters.

Key changes:

  • Adds a pre-flight guard in main() (inside with client:) that detects the swift+macOS+exclusion combination and silently switches strategy to jxa with a WARNING-level log.
  • No changes to the Swift binary, config schema, or heartbeat_loop — existing users not using exclusions are unaffected.

Finding:

  • The args.exclude_titles truthiness check does not filter None entries, whereas heartbeat_loop does (via if title is not None at lines 119–120). If a user's TOML config produces exclude_titles = [null], the fallback would be triggered unnecessarily even though no real patterns are active. Using any(t is not None for t in args.exclude_titles) would align the guard with actual runtime behaviour and prevent spurious fallbacks.

Confidence Score: 4/5

  • Safe to merge — the core fallback logic is correct; only a minor edge case around None-valued exclude_titles entries was identified.
  • The change is small, well-scoped, and targets a clear bug. The fix correctly guards the swift path and the mutation of args.strategy is safe because the subsequent if-block reads it immediately. The identified concern — the None-filtering mismatch — is an edge case unlikely to affect real users (requires a TOML config with explicit null entries), but the suggested fix is straightforward and improves precision of the guard condition.
  • aw_watcher_window/main.py — apply the suggested fix to align the exclude_titles guard condition with actual filtering behavior in heartbeat_loop.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[main starts] --> B{sys.platform == darwin<br/>and strategy == swift?}
    B -- No --> E[heartbeat_loop<br/>with current strategy]
    B -- Yes --> C{exclude_title or<br/>exclude_titles set?}
    C -- No --> D[spawn Swift binary<br/>subprocess.Popen<br/>bypasses Python filtering]
    C -- Yes --> F[log WARNING:<br/>falling back to jxa]
    F --> G[args.strategy = jxa]
    G --> E
    D --> H[p.wait — Swift binary<br/>sends heartbeats directly<br/>to AW server]
    E --> I[Python filtering applied<br/>exclude_title / exclude_titles<br/>work correctly]
Loading

Last reviewed commit: 2a4b330

Comment on lines +77 to +81
if (
sys.platform == "darwin"
and args.strategy == "swift"
and (args.exclude_title or args.exclude_titles)
):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The fallback condition at line 80 checks args.exclude_titles with a plain truthiness test, but the actual filtering at lines 119–120 excludes None entries with if title is not None. This means if a user's TOML config contains exclude_titles = [null] (or produces a list of only None values), the fallback will trigger unnecessarily — the list evaluates as truthy even though no actual patterns will be active after filtering.

To align the fallback guard with the actual runtime behavior in heartbeat_loop, filter out None entries before testing truthiness:

Suggested change
if (
sys.platform == "darwin"
and args.strategy == "swift"
and (args.exclude_title or args.exclude_titles)
):
if (
sys.platform == "darwin"
and args.strategy == "swift"
and (args.exclude_title or any(t is not None for t in args.exclude_titles))
):

@ErikBjare
Copy link
Copy Markdown
Member

Please create a follow-up issue to actually fix this for the swift-part of the watcher (which is the more stable default).

@TimeToBuildBob
Copy link
Copy Markdown
Contributor Author

Created #121 as follow-up for native Swift exclude_title support, per your request.

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