feat(research): inject APP_TO_CATEGORY map for non-browser app privacy - #1397
Conversation
…ild time Adds APP_CATEGORY_MAP (72 entries, faithfully derived from Matthias Lehner's APP_TO_CATEGORY dict) to the CI patch script. When [aw-watcher-window.research_app_category_map] is present in the submodule's config.py (requires ActivityWatch/aw-watcher-window#136 in the pin), the patch script now injects the app map alongside the existing URL/title category map. Non-browser apps are replaced by a broad study category (e.g. 'Microsoft Outlook' → 'Email', 'Spotify' → 'Music & Audio') instead of keeping the raw app name. Unmapped apps become 'Excluded'. The injection is non-fatal when the section is absent so this commit stays compatible with older submodule pins before ActivityWatch#136 merges.
Greptile SummaryThe PR extends the Research Edition configuration patcher with a non-browser application-category map while retaining compatibility with older watcher configurations.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "fix(tests): unpack tuple return from pat..." | Re-trigger Greptile |
|
|
||
|
|
||
| def patch_config(text: str) -> str: | ||
| def patch_config(text: str) -> tuple[str, bool]: |
There was a problem hiding this comment.
Tuple return breaks existing test
When scripts/tests/test_patch_research_edition_config.py runs, it treats the result of patch_config as a string, but this change returns a tuple, causing its substring assertions to fail.
Knowledge Base Used: Packaging and release pipeline
There was a problem hiding this comment.
Fixed in 011ff9f — unpacked the tuple return value in the test: result, app_map_injected = patcher.patch_config(source). Also added an assertion that app_map_injected is False for the test fixture (which has no research_app_category_map section).
|
@greptileai review |
… closed on stale pins The submodule was pinned at 624823a7, exactly one commit behind the merged APP_TO_CATEGORY support (a297914c, ActivityWatch#136). That pin has no [aw-watcher-window.research_app_category_map] section, and the injection step treated a missing section as non-fatal — so the build went green, printed a note into the CI log, and produced an artifact where non-browser apps keep their raw names. That is the exact symptom the map exists to fix. - bump aw-watcher-window 624823a7 -> a297914c (merged ActivityWatch#136) - patch_config() now raises when the app-map section is absent - regression test for the pre-ActivityWatch#136 pin Verified by symptom against both pins' real config.py: new pin injects 570 patterns + 72 app entries (exit 0); old pin aborts (exit 1).
|
Pushed The problem: the branch pinned Fixed:
Verified by symptom, running the real script against each pin's actual
Merging this now bumps the submodule too, so it's sufficient on its own — no follow-up bump PR needed before cutting the build. |
…edition ready) (#1) * fix(release): consistent release asset naming across editions (ActivityWatch#1393) Unify all asset filenames to: activitywatch[-tauri][-research]-<version>-<os>-<arch>[-setup].<ext> Previously research-edition assets mixed three conventions (edition token before the version in zips, after the arch in dmg/deb/rpm, and one AppImage without a version), and said 'research' twice since the tag already carries a -research suffix. - Strip the -research tag suffix from the version part of filenames (and from the Debian control version, where it would parse as a revision); the edition lives in its own token after the product name. - Rename the edition token from '-research-edition' to '-research'. - Move edition naming for AppImage/deb into the package scripts, dropping the post-hoc mv hacks in the workflow. - Document why the Qt AppImage filename is unversioned (stable releases/latest/download URL). * ci(build-tauri): fail fast if aw-server-rust submodule is on an older release line (ActivityWatch#1391) * ci(activitywatch): verify aw-server-rust submodule matches release tag in build-tauri Adds a fast-fail step in build-tauri that reads the bundled aw-server version from aw-server-rust/aw-server/Cargo.toml and compares the major.minor prefix against the AW release tag. If the submodule is pinned to an older release line, the build fails immediately rather than producing a 30-min Tauri bundle with the wrong backend (the Windows 0.14 release shipped with aw-server v0.13.1 due to this). Fixes ActivityWatch#1380 * ci(build-tauri): add missing aw-server-rust version guard The version-freshness check landed in build-qt but not build-tauri — the job that actually shipped the mismatched bundle in ActivityWatch#1380. Add the same fail-fast step to build-tauri so all five Tauri matrix jobs are guarded, not just the independent Qt build. --------- Co-authored-by: Bob <bob@bob.local> * feat(research): inject APP_TO_CATEGORY map for non-browser app privacy (ActivityWatch#1397) * feat(research): inject APP_TO_CATEGORY map into research config at build time Adds APP_CATEGORY_MAP (72 entries, faithfully derived from Matthias Lehner's APP_TO_CATEGORY dict) to the CI patch script. When [aw-watcher-window.research_app_category_map] is present in the submodule's config.py (requires ActivityWatch/aw-watcher-window#136 in the pin), the patch script now injects the app map alongside the existing URL/title category map. Non-browser apps are replaced by a broad study category (e.g. 'Microsoft Outlook' → 'Email', 'Spotify' → 'Music & Audio') instead of keeping the raw app name. Unmapped apps become 'Excluded'. The injection is non-fatal when the section is absent so this commit stays compatible with older submodule pins before ActivityWatch#136 merges. * fix(tests): unpack tuple return from patch_config in test assertions * fix(research): bump aw-watcher-window past ActivityWatch#136 and fail closed on stale pins The submodule was pinned at 624823a7, exactly one commit behind the merged APP_TO_CATEGORY support (a297914c, ActivityWatch#136). That pin has no [aw-watcher-window.research_app_category_map] section, and the injection step treated a missing section as non-fatal — so the build went green, printed a note into the CI log, and produced an artifact where non-browser apps keep their raw names. That is the exact symptom the map exists to fix. - bump aw-watcher-window 624823a7 -> a297914c (merged ActivityWatch#136) - patch_config() now raises when the app-map section is absent - regression test for the pre-ActivityWatch#136 pin Verified by symptom against both pins' real config.py: new pin injects 570 patterns + 72 app entries (exit 0); old pin aborts (exit 1). * fix(research): survive the ActivityWatch#137 config layout, and anchor the flag rewrite aw-watcher-window#137 moved the research knobs out of `default_config` into a separate `research_defaults` template, so they stop being persisted into every fresh install's config file. That is the right fix, but it removes both TOML table headers the Research Edition patch script anchors on. Two defects follow. 1. Injection breaks on the next submodule bump. `patch_config()` requires exactly one `[aw-watcher-window.research_category_map]` and one `[aw-watcher-window.research_app_category_map]` header. Neither exists past ActivityWatch#137, so the script aborts and the Research Edition build fails. The pin is currently a297914 (the ActivityWatch#136 merge), where both headers still exist -- so the build works today and breaks the moment anything, including a dependabot submodule bump, moves it forward. The fail-closed message would also misdiagnose it as 'submodule predates ActivityWatch#136' when the real cause is the opposite. 2. The flag rewrite could silently disable the Research Edition. ActivityWatch#137 documents the release-time rewrite in a comment containing the literal text `sed -i 's/^research_enabled = false$/research_enabled = true/'`, and that comment sits *above* the real flag. The unanchored `text.replace(..., 1)` therefore patches the comment and leaves `research_enabled = false`, producing a green build with research disabled and no error anywhere. Both layouts are now handled: post-ActivityWatch#137 the maps are injected into `research_defaults` with unprefixed headers (that template is parsed standalone and merged into the section key by key, so a prefixed header would create a nested key nothing reads); pre-ActivityWatch#137 the existing prefixed headers are used. The flag rewrite is line-anchored via regex, matching the contract ActivityWatch#137's own comment documents, and refuses to guess if it finds anything other than exactly one match. The pre-ActivityWatch#136 guard now keys on the runtime lookup `config.get("research_app_category_map"` rather than a table header, so it tests the capability that actually matters and survives further reshuffling of the config templates. Verified against both real config.py revisions: 570 category patterns and 72 app entries inject correctly, and the output parses as Python and as TOML in each. * build(deps): bump aw-watcher-window past ActivityWatch#137 Advance the aw-watcher-window submodule from a297914 to a7690ac, picking up ActivityWatch/aw-watcher-window#137 (fix(config): keep research options out of the first-run config). This is the prerequisite for the post-ActivityWatch#137 config layout the patch_research_edition_config.py script (c328757, in fix/re-patch- config-shape) was rewritten to handle. Without this bump, the Research Edition build would still pin to the pre-ActivityWatch#137 layout and patch in category maps that no longer exist after ActivityWatch#137 moved research knobs into a separate research_defaults template. Verified: scripts/tests/test_patch_research_edition_config.py (7 tests, all pass); live patch run against aw-watcher-window/aw_watcher_window/ config.py injects 570 unique URL/title patterns across 17 categories and 72 app-name entries, and flips research_enabled from false to true. Refs: ActivityWatch/aw-watcher-window#137, ErikBjare/bob#599, tasks/aw-research-edition-study-ops-goal-arc --------- Co-authored-by: Erik Bjäreholt <erik@bjareho.lt> Co-authored-by: Bob <bob@bob.local>
* feat(research): ship the study category set to the web UI The Research Edition watcher rewrites `app` to a study category before the event is stored. aw-webui, however, categorises client-side using its own default regexes and never sees the watcher's map -- so the Categories panel reads "Uncategorized" while Top Applications shows the correct categories. The data is right and the UI disagrees with it. That is exactly what the Lund study reported on v0.14.0b3-research, twice. The app-map fix in #1397 makes the stored data correct; it does not make the Categories panel agree, because that panel never consults the watcher config. aw-webui#936 adds the missing half: a build can supply a preset category set via AW_PRESET_CATEGORY_SETS, activated by default only when the user has no stored categorization. This wires the producer. scripts/emit_research_category_preset.py derives the preset from the same CATEGORY_MAP and APP_CATEGORY_MAP the watcher patch uses, so the UI's categories cannot drift from the watcher's. Rules match the category name anchored end to end, because by the time aw-webui sees an event, `app` IS the category name. Names are regex-escaped -- several contain '&', '/' or '-'. The release workflow emits it next to the existing patch step, for both the Qt and Tauri research paths, and exports it as a single-line value into $GITHUB_ENV for the build. Depends on ActivityWatch/aw-webui#936 and the aw-webui submodule bump that carries it; until then the variable is simply unread and builds are unchanged. * fix(research): escape category regexes portably across Python and JS The preset regexes were built with Python's `re.escape`, which escapes space as `\ ` and ampersand as `\&`. Both are valid in Python and both are *invalid identity escapes* in JavaScript unicode-mode regex: `new RegExp(r, "u")` throws `Invalid escape` on 14 of the 18 study categories, including every name containing " & ". They compile today only because aw-webui builds the regex without the `u` flag. Depending on that leniency is the wrong bet -- adding the flag later would break all 14 at once, and the symptom would be the Categories panel showing nothing, which is exactly the bug this preset exists to fix and therefore the hardest kind to attribute. Escape only the metacharacters both engines agree on outside a character class. For the current 18 names that is a no-op (none contain a metacharacter), so the emitted patterns become plain readable literals like `^Sensitive / Excluded$`, while a future name containing `.` or `(` is still escaped rather than silently becoming a wildcard. Verified in node: 18/18 compile and match with no flag and with the `u` flag, and still reject superstrings. Mirrors the cross-engine class Greptile caught on the consumer side in ActivityWatch/aw-webui#936, where JS-only regexes were reaching the Python query engine.
Summary
Updates the Research Edition CI patch script to inject Matthias Lehner's 72-entry app-name → category map into
[aw-watcher-window.research_app_category_map], closing the last spec gap before Matthias's September study.Depends on ActivityWatch/aw-watcher-window#136 being merged first (that PR adds the
research_app_category_mapconfig section and the runtime lookup).What changed
The CI patch script (
scripts/patch_research_edition_config.py) now:APP_CATEGORY_MAP— 72 entries, faithfully derived from Matthias'sAPP_TO_CATEGORYdict (classifier 2026-07-06)[aw-watcher-window.research_app_category_map]when the section is present in the patched config.pyEffect at runtime
Once aw-watcher-window#136 merges and the submodule is bumped:
Microsoft Outlook → Email,Spotify → Music & Audio,steam → GamesExcluded(strictly more private than keeping the raw name)This is what Matthias's classifier specifies and what he expects from the study build. It is also what was shown in his test screenshot:
Messages,Finder,Microsoft Outlook,Microsoft Teamsappearing as raw app names instead of study categories.Sequence to ship
--research-app-categorysupportv0.14.0b4-researchRef: ErikBjare/bob#1108