feat(research): bake profile=research, port 5667 and a distinct bundle id into research builds - #1434
Conversation
…e id into research builds The Research Edition profile stack (ActivityWatch#1399) shipped, but the research build never consumed it: v0.14.0b5-research resolves profile=default, binds 5600 and shares activitywatch/ with a standard install. The profile is build identity, not a launch argument: a login-item --profile would miss double-click, Spotlight and the updater relaunch. New fail-closed patcher scripts/patch_research_edition_profile.py {qt|tauri} (same pattern as patch_research_edition_awqt.py), wired into all three build jobs for AW_RESEARCH_EDITION == 'true': - every no-flag/no-env profile fallback in aw-qt, aw-server, aw-client, aw-server-rust and aw-tauri resolves to BUILD_PROFILE = "research"; DEFAULT_PROFILE itself is left alone because it means the ordinary install in suffix/dir/lockfile/export_profile logic - built-in port 5600 -> 5667 in both servers, aw-client, aw-qt's tray and manager fallbacks, and aw-tauri's UserConfig::default - CFBundleIdentifier net.activitywatch.ActivityWatch-research (Tauri identifier net.activitywatch.tauri-research) and CFBundleName "ActivityWatch Research" in aw.spec, build_app_tauri.sh, tauri.conf.json and notarize.sh The bake runs after the module test suites (which assert the ordinary defaults) and before Package; Python modules are editable installs, the Rust binaries get an explicit rebuild step. Git-Session-Id: cb3d
Greptile SummaryThis PR adds a fail-closed build-time patcher that gives Research Edition builds isolated profile, port, bundle, installer, package, and autostart identities across supported platforms.
Confidence Score: 5/5The PR appears safe to merge; no actionable new defects or outstanding previous findings remain. The prior bundle-filename collision and CI-coverage findings are fully addressed, and the subsequent changes consistently separate installer and autostart identities while adding patched-tree build validation. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Standard source tree] --> B[Run ordinary tests]
B --> C{Research build?}
C -- No --> D[Package standard edition]
C -- Yes, Qt --> E[Apply Qt research patches]
C -- Yes, Tauri --> F[Apply Tauri research patches]
E --> G[Rebuild Rust server]
F --> H[Rebuild Tauri application]
G --> I[Package isolated Research Edition]
H --> I
I --> J[research profile and port 5667]
I --> K[distinct bundle or installer identity]
I --> L[distinct autostart identity]
Reviews (3): Last reviewed commit: "ci: research-edition build leg on PR/bra..." | Re-trigger Greptile |
…in CI The research macOS bundle still installed as ActivityWatch.app, so dragging it into Applications replaced a standard install. Bake the on-disk stem (ActivityWatch-Research.app / .dmg) through aw.spec, APP_NAME, Makefile, and notarize.sh, and parameterize the dmg job on APP_BUNDLE. Also run the fail-closed patcher tests on every release/PR job before patching, so table drift fails at PR time instead of tag time.
|
@greptileai review |
Watcher ActivityWatch#143 squash-merged at abd69a6. The two ActivityWatch#138 guards now skip when AW_RESEARCH_EDITION=true, which is what killed v0.14.0b5-research run 34155142876. Pin folded into this PR so the recut does not need a separate bump round-trip. Git-Session-Id: pm-aw143
|
Watcher #143 squash-merged at Remaining recut landing list here: 5.2 Windows install identity, 5.3 Linux package identity, 5.4 autostart labels, 6.2 research-edition PR-CI build leg. Will ping for review when those land — not asking for merge yet. |
|
Same job was green on Windows Qt/Tauri still running on https://github.com/ActivityWatch/activitywatch/actions/runs/34161729287 |
…ntity
Both Inno Setup scripts describe a single product each. A research build made
from them would share the standard AppId, so Windows treats it as the *same*
product: the research setup upgrades over an existing install and its
uninstaller removes both. That defeats the dual-run the research edition
exists for.
Patch, fail-closed, at release time:
- activitywatch-setup.iss (qt): AppId -> 32024B9B-..., MyAppName ->
"ActivityWatch Research", OutputBaseFilename -> activitywatch-research-setup
- aw-tauri.iss (tauri): AppId -> 70E2D4AB-..., MyAppName ->
"ActivityWatch Research (Tauri)", DefaultDirName ->
ActivityWatch-Research-Tauri, OutputBaseFilename ->
activitywatch-research-tauri-setup
- tauri.conf.json: pin bundle.windows.wix.upgradeCode
AppName, DefaultDirName (qt), the Start-Menu / desktop / {userstartup}
shortcuts and UninstallDisplayName all derive from `#define MyAppName`, so one
patched token cascades to every user-visible identity. The tests assert that
cascade against the *real* .iss files rather than a synthetic fixture, so
upstream hardcoding a shortcut name fails the research build instead of
silently shipping a colliding Start-Menu entry.
WixConfig exposes no product-code field (Tauri generates one per build); the
upgrade code is what defines the MSI product family, so that is the one pinned.
It was already distinct via the patched identifier, but only as a side effect
of an unrelated string.
Note both .iss files ship OutputBaseFilename=activitywatch-setup on master, so
the qt and tauri setups already overwrite each other in dist/. The research
names are distinct from each other as well as from standard.
Refs ActivityWatch#1434
Git-Session-Id: 26d3
|
Same macos-latest Qt is green in this run. Windows Qt/Tauri and macos-15-intel Tauri still running: https://github.com/ActivityWatch/activitywatch/actions/runs/34164551339 |
The research edition already had its own profile, port, macOS bundle and Windows installer identity. Linux was still a single product: the deb is `Package: activitywatch` installing `/opt/activitywatch`, and it drops `aw-qt.desktop` into both /etc/xdg/autostart and /usr/share/applications. Installing a research deb next to a standard one therefore replaces it -- dpkg treats a same-named package as an upgrade -- and its autostart entry overwrites the standard one. Bake a distinct Linux identity at release time: package `activitywatch-research`, `/opt/activitywatch-research`, `activitywatch-research.desktop` in both shared namespaces, a rebranded `Name=`, and a matching AppImage icon id / desktop-entry filename. Also split what aw-qt registers for itself on first run (config `autostart_on_first_run`): the Linux autostart filename, the macOS LaunchAgent label, and APP_NAME (which the Windows Run value and Startup shortcut derive from). Installer identity does not cover these -- without them the two editions still overwrite each other's autostart. The Linux autostart patch targets `_linux_desktop_path()` rather than `DESKTOP_FILENAME`, which also names the shipped resource the build reads out of the bundle; flipping the constant would send `_bundled_desktop_file()` looking for a file that is not there. Tauri's Linux bundles come from Tauri's own bundler, keyed on `productName`, which is coupled to the cargo binary name -- left for a change that can be verified against a real Tauri build. Tests patch the real packaging scripts, not a synthetic fixture, so upstream adding another /opt/activitywatch reference or a fourth desktop-entry copy fails the research build instead of shipping a collision. Git-Session-Id: e26e
|
macos-15-intel Qt flake on Pushed Research patcher steps were not involved in the flake ( |
Adds a dedicated `research-edition-checks` job that runs on every PR and push to master. It checks out with submodules and verifies that every patch target in the Qt and Tauri tables still exists exactly once in the real source files (`--check` mode). This is the fail-closed guard that previously only fired when Erik pushed the annotated release tag. The job is stdlib-only (no poetry/build step) so it completes in ~30s. It also runs the fixture-based profile patcher unit tests. Closes task step 6.2 of the research-edition-profile-wiring checklist. Git-Session-Id: f8ff
|
Step 6.2 landed ( The job checks out with submodules and runs the patcher in The job is stdlib-only (no poetry/build step) so it completes in ~30s. It also runs the fixture-based profile patcher unit tests. Remaining open: step 5.4 — Tauri autostart identity ( |
…tinct app_name
tauri_plugin_autostart uses productName ("aw-tauri") as the OS autostart entry
name by default. Standard and research Tauri builds sharing that name means
enabling autostart in one edition overwrites the other's entry on Windows
(registry Run key) and Linux (~/.config/autostart/).
Add AUTOSTART_PATCHES_TAURI that replaces tauri_plugin_autostart::init() with
a Builder::new() call. After the profile patches have also run, BUILD_PROFILE
is defined in profile.rs. When BUILD_PROFILE != DEFAULT_PROFILE the Builder
sets app_name("aw-tauri-research"), giving the research binary its own entry
name. MacosLauncher selection (AppleScript vs LaunchAgent) is preserved.
Tauri's Linux bundler derives package/desktop names from productName, so a
full Linux package identity split needs a separate investigation; the collision
that is fixed here is the runtime autostart entry written by the plugin itself.
Add test asserting the patcher inserts BUILD_PROFILE and that the lib.rs patch
produces Builder code with the conditional app_name override.
Git-Session-Id: 49c9
|
Correction on 6.2: That job ( 5.2–5.4 are in:
Original 6.2 (patched-tree PR-CI build) is still coming before a ready-ping. Not asking for merge yet. |
The --check smoke test (706ba75) only catches patch-target drift on the unpatched tree. A patched-tree compile/test/package failure -- the class that killed the first v0.14.0b5-research run -- still only surfaced at tag time. Add a `research` matrix dimension with one Linux include row per build job (Qt on ubuntu-22.04, Tauri on ubuntu-24.04) that sets AW_RESEARCH_EDITION=true, so every PR and branch push applies the full research patch set and builds, tests and packages the patched tree. On tag pushes and workflow dispatches the include row's `research` value resolves to false, so it merges into the existing Linux row instead of adding a duplicate leg: research tags already build the research edition in every row, and standard tags must not upload research artifacts to the standard release. Artifact names carry a -research suffix so the leg cannot collide with the standard row on the same runner. Git-Session-Id: 0283
|
6.2 pushed (
Run 34183065594 → 34183838348: both |
|
Ready for review. Both research build legs are green on run 34183838348:
Every other finished job is green; two standard rows (Qt macos-15-intel, Tauri windows) were still running at 04:05Z. Recut landing list items 1–3 are all in on this branch ( |
|
@greptileai review |
|
Full matrix on run 34183838348 is now green, including the two standard rows still running at the ready-ping (Qt macos-15-intel, Tauri windows). No remaining CI. Greptile re-triggered on |
Fix that, aw-qt should use isolated roots too. Also check aw-server which used to use config sections for testing mode in same file as prod conf. |
|
Fixed.
Submodule pins in this repo after those merge. Recut tag still yours. |
|
Follow-ups are merge-ready: ActivityWatch/aw-qt#132 and ActivityWatch/aw-server#170 (CI green; I cannot self-merge — pull-only). Research patcher targets still match those heads. Pins after those merge. Recut stays on #1436; baked 5667 still covers a fresh research profile. |
…e>.toml (#132) * fix(config): read isolated-root config.toml instead of config-<profile>.toml aw-server-rust writes bare config.toml under activitywatch-<profile>/. aw-qt still looked for config-<profile>.toml (and [server-<profile>]) in whatever dir AW_PROFILE currently pointed at, so a research instance never saw the rust server's port. Lookup now sets AW_PROFILE for the target profile, reads config.toml / [server] in the isolated root, and only uses the legacy suffixed names when testing still lives on the shared activitywatch/ root. Refs: ActivityWatch/activitywatch#1434 * fix(config): look up pre-isolation profile files in the shared root Isolated-root lookup set AW_PROFILE first, so the config-<profile>.toml / [server-<profile>] / [aw-qt-<profile>] fallbacks ran inside activitywatch-<profile>/ instead of the pre-isolation activitywatch/ root. Existing named profiles then lost their port and module list. Search the shared root for those suffixed files/sections after the isolated bare config misses.
The Research Edition patcher (#1434) rewrites OutputBaseFilename in both .iss files to activitywatch-research[-tauri]-setup, but package-all.sh still moved the hardcoded standard name — failing both Windows jobs on the v0.14.0b5-research tag build (mv: cannot stat). Glob the single produced setup exe instead, failing loudly if the count is ever not 1.
Why
The Research Edition profile stack (#1399) is done, but the research build never consumed it:
v0.14.0b5-researchresolvesprofile=default, binds 5600, and sharesactivitywatch/with a standard install. Erik promised Matthias (2026-09-04) that the research build runs in parallel with a personal install, so the recut needs isolation baked in.The profile is build identity, not a launch argument. A login item passing
--profile researchwould miss double-click, Spotlight, and the updater relaunch — each of those would silently startdefaultand pollute the participant's standard install. Autostart phase 3 (--profilein login items) is therefore not a research-edition gate and stays a generic multi-profile feature.What
New fail-closed patcher
scripts/patch_research_edition_profile.py {qt|tauri}(same pattern aspatch_research_edition_awqt.py), wired into all three build jobs forAW_RESEARCH_EDITION == 'true':research. Every "no--profile, noAW_PROFILE" fallback in aw-qt, aw-server, aw-client, aw-server-rust and aw-tauri resolves to a newBUILD_PROFILE = "research"constant instead ofDEFAULT_PROFILE. The launchers then setAW_PROFILE=researchfor their children, so dirs land inactivitywatch-research/and the lockfiles areaw-qt-research.lock/single_instance-research.lock.DEFAULT_PROFILEitself is deliberately not flipped: it means "the ordinary install" in suffix/dir/lockfile/export_profilelogic (aw-qtexport_profilepops the env for it; aw-tauridirs.rsmaps it to the bare root), so flipping it would collapse the isolation this PR exists to create.UserConfig::default, so a fresh research profile binds 5667 with no config file yet. (aw-tauri embeds aw-server-rust in-process and passes its own config port, so only the aw-tauri default matters there.)CFBundleIdentifiernet.activitywatch.ActivityWatch-research(Tauri identifiernet.activitywatch.tauri-research),CFBundleName"ActivityWatch Research" — inaw.spec,build_app_tauri.sh,tauri.conf.json,notarize.sh. Dual-run becomes a real LaunchServices identity with its own login item and TCC grant instead of a shared slot; Dock/Spotlight show two names.Every site is an exact, unique string match; a missing or ambiguous target aborts the build. 24 sites for the Qt bundles, 14 for Tauri;
--checkverifies without writing and currently passes against the pinned submodules.Step ordering (why the new step is after
make test)The module test suites assert the ordinary defaults (
resolve_profile(None) == "default", port 5600) and would fail on the patched tree. So the profile bake runs afterRun tests/Run integration testsand beforePackage. Python modules are editable installs (poetry install), so PyInstaller collects the patched source at package time; the Rust binaries get an explicit rebuild step (make --directory=aw-server-rust aw-serverin the Qt jobs,make --directory=aw-tauri buildin the Tauri job — incremental cargo,aw-webui/distis reused). The three existing research patch steps stay where they are; their targets aren't covered by unit assertions.Not in this PR
v0.14.0b5-researchor cuttingb6-research— recut is Erik's call after merge.BUILD_PROFILEinto the submodules themselves. The patcher inserts the constant at release time; if a submodule later grows the constant natively, the patcher fails closed and its table gets trimmed.config-<profile>.tomlfor the rust server's port while aw-server-rust writes bareconfig.tomlin isolated roots — a pre-existing aw-qt lookup mismatch. The baked 5667 fallback covers the research build; the mismatch is worth an aw-qt fix separately.Verification
python3 scripts/patch_research_edition_profile.py qt --check/tauri --check: all sites present and unique on the current pins.researchforresolve_profile(None, False),testingfor--testing, keepDEFAULT_PROFILE == "default", andexport_profilesetsAW_PROFILE=research;aw.specstill parses;tauri.conf.jsonstill valid JSON; shell scripts passbash -n.scripts/tests/test_patch_research_edition_profile.pycovers apply/check/fail-closed/no-double-apply plus a live check against the pinned submodules.profile=research, dashboard on 5667, dirs underactivitywatch-research/,CFBundleIdentifierends in-research, and a standard install keeps running alongside.Refs: #1399 (profile stack), ErikBjare/bob#1108 (Research Edition b5 tracking).
Companion: ActivityWatch/aw-watcher-window#143 merged at
abd69a6. This PR now pinsaw-watcher-windowthere (2868946) so the recut's patch→test path skips the two #138 guards underAW_RESEARCH_EDITION=true— the failure that killed run 34155142876.