Skip to content

fix(transform): sort union_no_overlap inputs by timestamp - #185

Merged
ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:fix/union-no-overlap-sort-inputs
Oct 26, 2026
Merged

ErikBjare merged 1 commit into
ActivityWatch:masterfrom
TimeToBuildBob:fix/union-no-overlap-sort-inputs

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

union_no_overlap walks both lists in ascending timestamp order, but query_bucket returns events newest-first, and the query function passes its arguments through unsorted. The result then contains overlapping events and durations are double-counted.

Repro against a v0.14.1 aw-server-rust --testing server (this module ports the same algorithm): window and AFK buckets that each fully cover the same 40 minutes.

union_no_overlap(query_bucket("w"), query_bucket("afk"))                                  -> sum_durations 4200.0
union_no_overlap(sort_by_timestamp(query_bucket("w")), sort_by_timestamp(query_bucket("afk"))) -> sum_durations 2400.0

aw-webui hits this path with raw bucket output:

  • canonicalEvents: stopwatch_events = query_bucket(...) then union_no_overlap(stopwatch_events, events)
  • activityQuery: not_afk_curr = filter_keyvals(query_bucket(...), ...) then union_no_overlap(not_afk, not_afk_curr), once per AFK bucket. With two or more devices the total is inflated.

The fix sorts both inputs inside the transform, as period_union and filter_period_intersect already do. Inputs that are already sorted produce the same output as before.

Test: test_newest_first_inputs_are_sorted_before_merging feeds reversed lists. It fails without the sort and passes with it. The full pytest tests suite passes (359 passed, 5 skipped) and ruff check is clean.

Counterpart of ActivityWatch/aw-server-rust#838. The Python server is what aw-qt autostarts in the v0.14.1 Qt bundle, so this is the path most desktop users hit.

Also reproduced live on the released Python aw-server v0.14.1 (--testing, isolated home), using the same buckets: unsorted 4200.0, sorted 2400.0. A chained activityQuery-style union (a = []; a = union_no_overlap(a, afk); a = union_no_overlap(a, window)) returns 3600.0 where the correct coverage is 2400.0.

query_bucket returns events newest-first, but the merge walks both lists
in ascending order. Unsorted input emitted overlapping events: a 40-minute
span with full coverage in both lists summed to 70 minutes. period_union
and filter_period_intersect already sort their inputs.

Counterpart of ActivityWatch/aw-server-rust#838.

Git-Session-Id: f67bd883-b10a-49dd-99de-2938d082f464
@greptile-apps

greptile-apps Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] This PR appears safe to merge.

Summary

union_no_overlap now sorts copied inputs by timestamp before merging, so newest-first events no longer cause double-counting.

  • Event unions sort both inputs before removing overlaps.

Reviews (1) · Last reviewed commit: "fix(transform): sort union_no_overlap in..." · Reviewed by Greptile

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable — 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 26, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 AI code review

The PR changes union_no_overlap to sort deep copies of both input lists by Event.timestamp before merging and updates its docstring to describe this behavior. It adds a regression test comparing ascending and newest-first inputs, checking union invariants and a total duration of 40 minutes.

Safe to merge — no P0/P1 findings

Confidence 5/5

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

Files changed (2) — the diff as I read it
  • aw_transform/union_no_overlap.py — Sorts both copied input lists by timestamp before merging and updates the input-order documentation.
  • tests/test_union_no_overlap.py — Adds test_newest_first_inputs_are_sorted_before_merging to compare reversed inputs with ascending inputs and check invariants and total duration.

Reviewed 581136b9a06e · panel: codex:gpt-6.1-sol · subscription engine · 21s · about this reviewer

Maintainer commands

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

Outage coverage: 1-pass, not merge evidence. Subscription review supplied findings while full consensus capacity was unavailable. The consensus merge gate remains unsatisfied.

@ErikBjare
ErikBjare merged commit 4f5df9d into ActivityWatch:master Oct 26, 2026
7 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.

2 participants