Skip to content

fix(categories): treat blank regex rules as matching nothing everywhere - #1026

Merged
ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:fix/blank-regex-category-382
Oct 6, 2026
Merged

ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:fix/blank-regex-category-382

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Fixes #382.

Root cause

The views don't agree on what a blank regex rule means:

  • Server-categorized views (Category Sunburst, Timeline barchart, Top Categories) get $category from categorize() in the query. aw-core's Rule deliberately never matches an empty regex (aw_transform/classify.py: "also checks that the regex isn't an empty string (which would erroneously match everything)"), so those views ignore the category.
  • Client-side views (Top Applications, Top Window Titles) color rows with matchString(), which compiles RegExp(''). That matches every string, so every app or title not claimed by a more specific rule lands in the blank-regex category.

aw-server-rust differs from aw-core here as well: its RegexRule::new("") compiles to a match-all regex.

The editor has refused empty patterns since 9f37315 (validateRegex('') === false). Blank rules still reach users through older stored settings and imported category files.

Fix

  • hasMatchableRegex(c) in util/classes.ts is used by both matchString and classifyEvents, so client-side matching skips blank regexes the same way aw-core does.
  • classes_for_query sends blank-regex rules as { type: 'none' }, so aw-server-rust and aw-server give the same result. The category still exists as a container, and its children match as before.

Tests

  • classes.test.node.ts: with a blank-regex Work parent and a Work > Programming (vim) child, matchString/classifyEvents return Uncategorized for unrelated strings (master returned Work) and still return the child for vim.
  • store/categories.test.node.ts: classes_for_query sends the blank rule as none and leaves real regex rules unchanged.

Ran locally: jest on the classes, categories store, categorization, queries and Sunburst suites (42 passed), tsc --noEmit is clean, and prettier passes. No .vue files changed.

aw-core's server-side categorize() ignores a rule whose regex is empty,
because it would otherwise match every event. The client-side matchers
compiled RegExp(''), which matches everything. So Top Applications and
Top Window Titles put every event in a blank-regex category, while
Sunburst, Timeline and Top Categories ignored it.

Skip blank regexes in matchString/classifyEvents, and send them to the
server as 'none' so aw-server-rust (which compiles '' as match-all)
agrees too.

Fixes ActivityWatch#382

Git-Session-Id: 1bfb
@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Changes how blank regex rules are categorized.

The PR appears safe to merge.

Summary

The PR makes empty category regexes match nothing in client-side classification and sends them to server queries as none, while retaining child category rules.

  • Adds regression tests for client matching and the query payload.
  • No actionable issues were identified.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Category rules] --> B{Empty regex?}
  B -- Yes --> C[Client: skip matching]
  B -- Yes --> D[Server query: send type none]
  B -- No --> E[Keep regex rule]
  E --> F[Client and server classification]
Loading

Reviews (1) · Last reviewed commit: "fix(categories): treat blank regex rules..."

@codecov

codecov Bot commented Oct 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.37%. Comparing base (45e9c41) to head (ad8139b).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1026      +/-   ##
==========================================
+ Coverage   63.30%   63.37%   +0.07%     
==========================================
  Files          52       52              
  Lines        3608     3607       -1     
  Branches      840      894      +54     
==========================================
+ Hits         2284     2286       +2     
+ Misses       1309     1306       -3     
  Partials       15       15              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

@TimeToBuildBob

TimeToBuildBob commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 AI code review

Adds hasMatchableRegex() to util/classes.ts, which returns false for regex rules with an empty regex string. matchString and classifyEvents now filter categories through it so blank regexes never match client-side. classes_for_query converts blank-regex rules to { type: 'none' } before sending them to the server. Adds unit tests for both matching and the query getter.

Safe to merge — no P0/P1 findings

Confidence 5/5

✅ No findings. The diff looks correct to me on this pass.

Files changed (4) — the diff as I read it
  • src/util/classes.ts — Adds hasMatchableRegex() and filters regex categories in matchString and classifyEvents so empty regexes are not compiled.
  • src/stores/categories.ts — Imports hasMatchableRegex; classes_for_query now maps blank-regex rules to { type: 'none' }.
  • test/unit/classes.test.node.ts — Adds test verifying a blank-regex parent does not match while its regex child still does.
  • test/unit/store/categories.test.node.ts — Adds test for classes_for_query converting a blank-regex rule to type none.

Reviewed ad8139bd31d4 · openrouter/deepseek/deepseek-v4-flash-0731 · llm engine · 1031s · about this reviewer

Maintainer commands

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

ErikBjare pushed a commit to ActivityWatch/aw-server-rust that referenced this pull request Oct 2, 2026
* fix(aw-transform): empty regex rules never match

RegexRule::new("") compiled to a match-all regex in fancy_regex, so a
blank category rule matched every event. aw-core's Python
aw_transform.classify.Rule deliberately never matches an empty regex
("would erroneously match everything") — aw-server-rust silently
diverged from it. ActivityWatch/aw-webui#1026 hides this for the web UI
by sending blank rules as {type: 'none'}, but other clients (aw-client
categorize queries, scripts, the CLI) still see the divergence.

Add a never_matches flag set when the pattern is empty, checked before
any regex evaluation, so an empty-regex rule behaves like Rule::None
instead of a wildcard. Covered by a new regression test that fails on
master.

Also carries the aw-query clippy::block_scrutinee allow from #771/#772
(unrelated Rust 1.99+ toolchain drift) so this branch's own CI/clippy
passes independently of that PR's merge.

Git-Session-Id: 1d99

* fix(aw-transform): derive never_matches in From<Regex> conversion

Greptile P1: Rule::from(Regex::new("").unwrap()) hard-coded
never_matches: false, so the public conversion path still let an empty
regex match every event — the exact bug this PR fixes, reachable past
the new guard. Derive the flag from the source pattern and cover the
From path in the regression test.

Git-Session-Id: 2b7710f0-a6ec-5fa0-b895-bf715c0f8ab4
@ErikBjare
ErikBjare merged commit 4e8789c into ActivityWatch:master Oct 6, 2026
9 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.

Ignored rules with blank RegEx

2 participants