Skip to content

feat(dirs): profile isolation via AW_PROFILE appname suffix - #149

Merged
ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:feat/instance-profiles
Aug 20, 2026
Merged

feat(dirs): profile isolation via AW_PROFILE appname suffix#149
ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:feat/instance-profiles

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Implements the aw-core layer of ActivityWatch/activitywatch#1399.

The mechanism

When AW_PROFILE is set, all platform directories (data, config, cache, log)
are rooted under activitywatch-<profile> instead of activitywatch.
This gives every profile a completely separate on-disk tree — the Chrome/Firefox
model suggested in the RFC thread by @ErikBjare.

profile      data dir root                           backward-compat
----------   ------------------------------------    ---------------
(unset)      ~/.local/share/activitywatch/          ✅ identical to today
testing      ~/.local/share/activitywatch-testing/   via AW_PROFILE=testing
research     ~/.local/share/activitywatch-research/  via AW_PROFILE=research

Cleanup is trivial: rm -rf ~/.local/share/activitywatch-research/ removes the
research profile completely, touching nothing else.

Changed

  • aw_core/dirs.py — adds _get_appname() (3 lines) that reads AW_PROFILE
    and returns activitywatch-<profile> or bare activitywatch. Every
    get_{data,config,cache,log}_dir call passes this name to platformdirs.
  • tests/test_dirs.py — 11 new tests: appname derivation + disjoint-dirs
    check for default/testing/research; all 189 existing tests still pass.

Why this is simpler than the prior approach

The earlier version of this PR threaded a profile= parameter through the
datastore and log layers. That was unnecessary: those layers already call
get_data_dir() and get_log_dir(), so making the dir functions
profile-aware gives full isolation with zero changes to datastore or log code.

Backwards compatibility

Unset or empty AW_PROFILE → bare activitywatch appname, identical to the
current layout. The existing -testing / -research file-level suffixes in
the datastore layer are redundant under full directory isolation but harmless.

What comes next (other PRs / other repos)

Per ActivityWatch/activitywatch#1399:

  • aw-server-rust: OnceLock<String> PROFILE + dirs.rs root suffix (Phase 3b)
  • aw-tauri: same dirs.rs change (hard-coded .join("activitywatch") → appname)
  • aw-qt / launchers: --profile NAME flag sets AW_PROFILE before spawning children
  • Research build: bakes AW_PROFILE=research into the launch script

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Greptile Summary

The PR derives the platform-directory application name from AW_PROFILE, isolating each profile's data, configuration, cache, and log roots while retaining the legacy paths when unset.

  • Adds centralized profile-aware app-name derivation.
  • Applies the derived name to every platform directory helper.
  • Adds tests for default, testing, and research profile separation.

Confidence Score: 4/5

The PR is not yet safe to merge because the previously reported logfile-boundary bug remains present and can return another service or mode's logfile.

The prior reply states that timestamp-boundary matching fixed digit-leading and prefix-extending collisions, but current HEAD still filters with name in filename and a global "testing" substring check, providing a concrete counterexample to that claim and leaving cross-instance logfile selection reachable.

Files Needing Attention: aw_core/log.py

Important Files Changed

Filename Overview
aw_core/dirs.py Centralizes profile-aware app-name selection and applies it consistently to all platform-directory roots.
tests/test_dirs.py Verifies app-name derivation and separation of representative data, configuration, and cache directories.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    E[AW_PROFILE] --> A[_get_appname]
    A -->|unset or empty| D[activitywatch]
    A -->|profile set| P[activitywatch-profile]
    D --> X[platformdirs roots]
    P --> X
    X --> R[data / config / cache / log]
Loading

Reviews (3): Last reviewed commit: "feat(dirs): profile isolation via AW_PRO..." | Re-trigger Greptile

Comment thread aw_core/log.py Outdated
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@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.

When AW_PROFILE is set, all platform directories (data, config, cache,
log) are rooted under activitywatch-<profile> instead of activitywatch.
This gives every profile a completely separate on-disk tree — identical
to the Chrome/Firefox model suggested in ActivityWatch/activitywatch#1399.

Mechanism: _get_appname() reads AW_PROFILE at call time and passes it
to every platformdirs call. No profile parameter needs to be threaded
through the datastore or log layers because those already use get_data_dir()
and get_log_dir() internally.

Backwards compatibility:
- Unset or empty AW_PROFILE → bare 'activitywatch' appname, identical to today.
- The existing -testing/-research file suffixes in the datastore layer are
  redundant under full directory isolation but harmless to leave in place.

Part of ActivityWatch/activitywatch#1399 (aw-core layer).
@TimeToBuildBob
TimeToBuildBob force-pushed the feat/instance-profiles branch from d126c03 to 6e785fc Compare August 20, 2026 15:12
@TimeToBuildBob TimeToBuildBob changed the title feat(datastore): generalise testing: bool into named instance profiles feat(dirs): profile isolation via AW_PROFILE appname suffix Aug 20, 2026
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Design pivot from the prior implementation:

The earlier version threaded a profile= parameter through the datastore and log layers — 666 additions. This revision does the same thing in 3 lines by making _get_appname() read AW_PROFILE and pass the suffixed name to platformdirs. The datastore and log layers don't need to change at all because they already call get_data_dir() / get_log_dir(), which now return the profile-rooted path automatically.

This is the approach @ErikBjare suggested in the RFC thread: per-profile appname (activitywatch-testing, activitywatch-research) rather than per-file suffixes within a shared directory. Full directory isolation; cleanup by rm -rf.

The force-push replaces the earlier commits entirely. All 189 existing tests still pass; 11 new tests cover the appname derivation and the disjoint-dirs invariant for default/testing/research.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@ErikBjare
ErikBjare merged commit 11f17e7 into ActivityWatch:master Aug 20, 2026
5 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/aw-server that referenced this pull request Aug 25, 2026
Profile-aware dirs from ActivityWatch/aw-core#149 are on git master but
not on PyPI — 0.5.17 was cut in 2024. AW_PROFILE export is a no-op until
the next aw-core release; this lock bump is just current PyPI.
ErikBjare pushed a commit that referenced this pull request Aug 25, 2026
* feat(cli): add --profile flag to aw-cli, remove testing-based log filename filter

--profile NAME selects a named instance profile, resolving to the
activitywatch-<profile> platform dirs introduced in #149. --testing
remains as an alias for --profile testing.

The qt subcommand now forwards --profile <name> to aw-qt (which gained
profile support in aw-qt#128). The logs subcommand relies on AW_PROFILE
being set in the environment (done by the group callback) so get_log_dir()
already returns the profile-specific directory; no filename-based filtering
is needed. find_oldest_log() drops the `testing` parameter accordingly.

Step 6 of ActivityWatch/activitywatch#1399.

* fix(cli): preserve inherited AW_PROFILE when no --profile flag given

Without this fix, running 'aw-cli <cmd>' with AW_PROFILE already set in the
environment would silently clear it, making directory/log lookups fall back
to the default profile instead of the inherited one.

Addresses Greptile's inherited-profile regression note on #151.
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