Conversation
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #678 +/- ##
==========================================
+ Coverage 70.81% 80.69% +9.87%
==========================================
Files 51 73 +22
Lines 2916 7205 +4289
==========================================
+ Hits 2065 5814 +3749
- Misses 851 1391 +540 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Addressed the two Greptile P1s in be1d36f: group-match same-identity siblings so a later source row cannot delete a dest sibling that still matches, and bound both reconcile fetches at |
|
@greptileai review |
|
Greptile 4/5 on be1d36f: no code change.
CI green, mergeable. Waiting for human review. |
|
@TimeToBuildBob Adding this to the v0.14.x queue as a stopgap — decision on ActivityWatch/activitywatch#1445. It fixes ActivityWatch/aw-android#253 for the format Android users run today, and the v2 sync format replaces Please rebase-check against #697 once that lands — both touch |
|
Acknowledged: v0.14.x stopgap for ActivityWatch/aw-android#253, then deleted when v2 replaces Preview rebase-check against current #697 head
Will rebase onto master after #697 merges and re-run |
|
@TimeToBuildBob Before this goes to Erik: rebase onto master. The branch is based on Content looks right from a structural pass: 7-day lookback constant, |
Resume-from-latest-end skipped title edits of already-synced events (ActivityWatch/aw-android#253). Match source and dest by timestamp in the 7 days before the cursor and replace dest rows whose data changed. Run that pass before the incremental copy so a latest-event title edit does not insert a start-clipped duplicate. Git-Session-Id: 0d71
A timestamp-only map collapsed two dest events that shared a start time. Identity is (timestamp, duration) so a sibling row is not replaced. Git-Session-Id: 0d71
Keep the lookback query unclipped so dest-latest identity stays intact, skip source events that end after the resume cursor, and cap both fetches at 20k events so a dense 7-day window cannot OOM. Git-Session-Id: 0d71
Filtering on event end dropped dest-latest title edits whose duration grew past the old resume cursor. Skip when timestamp >= resume instead. Git-Session-Id: 0d71
A crash between delete and insert would drop the event, and the next resume-cursor pass would not resurrect it. Insert first; a later pass skips insert when matching data is already present. Git-Session-Id: 0d71
A 20k newest-first cap skipped older edits still inside the 7-day lookback. The time window is the memory bound. Git-Session-Id: 0d71
end=None loaded the entire post-cursor source backlog when dest was behind. Fetch through resume instead. Match source and dest by identity as a group so a sibling with matching data is not deleted as stale. Git-Session-Id: 175f4625-aa96-5219-a447-7df0d9f1a988
be1d36f to
1379d77
Compare
🤖 AI code reviewSafe to merge — 2 findings disposed (accepted-tradeoff; rejected)Updated after inline dispositions on finding threads — this is the current state; the verdict below is frozen at review time and is kept as the historical record of that pass. Finding disposition
Adds a reconcile pass to aw-sync that, before the incremental copy, compares source and destination events in the 7 days before the resume cursor by (timestamp, duration) and replaces destination rows whose data changed, inserting the replacement before deleting the stale row. Adds a new AccessMethod::delete_events_by_id trait method with Datastore and AwClient implementations, updates the README limitation note, and adds an integration test file covering historical/latest edits, same-identity siblings, lookback bounds, and far-behind destinations. Not safe to merge — 2 P1 openConfidence 2/5 2 findings · ❌ 2 P1❌ P1 high — The reconcile pass uses How this was verified: Traced the reconcile loop: for each identity, ❌ P1 high — The reconcile pass fetches source events with end=resume, but the source bucket may contain events that start before resume and end after resume (the live last event). The comment says 'Do not use end>resume: the dest-latest event starts before resume and must still be title-reconciled', but the fetch itself uses end=resume, so a source event that starts before resume and ends after resume is excluded from source_events entirely. The dest-latest event is also fetched with end=resume, so it is excluded from dest_events too. This means a title edit on the live last event (which starts before resume and ends after resume) is never reconciled by this pass. The test latest_title_edit_reaches_staging uses t1 = now - 1h + 40min, duration 15min, so t1+duration is before now, meaning the event ends before the resume cursor (which is the dest's latest event end, also before now). The test does not cover an event that spans the resume cursor. The consequence is that a title edit on the currently-active event (which is the most common case for a user editing the current window title) will not be synced, and the incremental copy will re-fetch it as a start-clipped fragment, causing heartbeat to insert a duplicate as described in the PR's own cause section. How this was verified: Checked the fetch range in reconcile_updated_events: source_events and dest_events both use end=Some(resume). The comment at lines 513-515 explicitly discusses the dest-latest event starting before resume, but the fetch excludes it because end=resume clips events that start before resume and end after. The test latest_title_edit_reaches_staging uses t1 = now - 1h + 40min with duration 15min, so t1+duration is before now; the resume cursor is the dest's latest event end, which is also before now, so the event does not span the cursor. No test covers an event spanning the cursor. 1 advisory finding (summary-only, not scored)These P2 guard, heuristic, trade-off, or documentation claims are retained for judgment without opening review threads.
The How this was verified: Checked the AwClient implementation: it iterates and returns on first error. The Datastore implementation uses a single call. The reconcile pass calls delete_events_by_id after insert_events, so a failure here leaves the insert applied but some deletes not. Files changed (4) — the diff as I read it
Previous review passes
Reviewed Maintainer commands
|
|
@TimeToBuildBob Full read done now that it is on current master ( The logic is right. Bounded to a 7-day window ending at the cursor with One required change: no With that, merge on green. Queue position unchanged: after #700. |
reconcile_updated_events now returns Result and uses ? on get_events, insert_events, and delete_events_by_id. A datastore failure costs that bucket's reconcile, not a panic that aborts the pass (or the JNI frame on Android). force_commit in delete_events_by_id matches the other AccessMethod methods. Git-Session-Id: 82e7d9e5-0c8a-58c0-b129-a46f96ef6953
|
@ErikBjare Done on Local |
|
Verified on |
|
Not adjudicating remaining AI-review threads this cycle: last review saw CI still green. Holding for #700. |
|
Merge recommendation (Greptile convergence adjudication). Not merging — maintainer judgment. Fixed this session
Remaining (non-blocking) — classified on
Verified against: CI: all green (Android, coverage, clippy, format, macOS, ubuntu, windows). Domain risk: Android title-edit of a historical event and of dest-latest (the ActivityWatch/aw-android#253 path). No datastore panic path left in Convergence: Queue: #700 merged 14:07Z, so Erik's "ready after #700" gate is open. Branch is 1 commit behind master (that merge) but GitHub reports MERGEABLE. v0.14.x stopgap; deleted when v2 replaces |
|
Merge verified on Downstream note (not a request, just so the pin arithmetic is on the record): |
Rebased onto post-ActivityWatch#699/ActivityWatch#678 master (SyncReport/PeerReport world): open and pull failures per peer are now recorded on the report and skipped; only a total failure (every discovered peer failed) is Err, so one unreadable or failing peer no longer aborts the whole pass (ActivityWatch#688). Co-Authored-By: Bob Git-Session-Id: 2d23d1d2-e3df-5c27-97f3-ef94f342ad09
Rebased onto post-ActivityWatch#699/ActivityWatch#678 master (SyncReport/PeerReport world): open and pull failures per peer are now recorded on the report and skipped; only a total failure (every discovered peer failed) is Err, so one unreadable or failing peer no longer aborts the whole pass (ActivityWatch#688). Co-Authored-By: Bob Git-Session-Id: 2d23d1d2-e3df-5c27-97f3-ef94f342ad09
…ass (#703) * fix(aw-sync): skip broken peers in sync_run instead of aborting the pass Rebased onto post-#699/#678 master (SyncReport/PeerReport world): open and pull failures per peer are now recorded on the report and skipped; only a total failure (every discovered peer failed) is Err, so one unreadable or failing peer no longer aborts the whole pass (#688). Co-Authored-By: Bob Git-Session-Id: 2d23d1d2-e3df-5c27-97f3-ef94f342ad09 * test(aw-sync): make version-skip test hermetic against schema bumps Use NEWEST_DB_VERSION + 1000 instead of hardcoded 4 so the test cannot go stale when the supported schema version reaches the hardcoded value. Addresses our AI review P2 (fp 4bf1d7a1ac8b). Git-Session-Id: 515fe2f5-05bc-5268-82e6-bac7396140ff * fix(aw-sync): only open peer datastores for Pull/Both passes Push-only passes never read peer datastores, so opening them there was wasted work and made an all-unreadable-peer folder abort the pass before anything was pushed. Gate open_peer_datastores on Pull/Both; push mode now proceeds regardless of peer database state. Closes the Greptile P1 on 9b49d4a and the push-mode visibility P1 from the in-band review (with the gate, peer opens only ever run with record_peers=true, so incompatible peers are always on the report). Git-Session-Id: 63c563f9-1bf6-5fa9-a53b-c51caa2275d0
Why
ActivityWatch/aw-android#253: manual title edits show up in JSON export but often never reach the sync folder. Deleting the visible
test.dbdoes not help.Cause
sync_oneresumes at the destination's latest event end. WebUI/Android edits are delete+insert at the same timestamp, so they sit outside that window. The latest event is re-fetched as a start-clipped fragment;heartbeat(pulsetime=0)will not merge different data and inserts a duplicate instead.Android writes an internal staging db, then mirrors it outbound to SAF. Deleting the visible file only deletes the copy.
Change
Before the incremental copy, match source and dest events in the 7 days before the resume cursor by
(timestamp, duration)and replace dest rows whose data changed. Insert the replacement before deleting the stale row. Identity includes duration so two events that share a start time are not collapsed.Not in this PR: event deletion, edits older than 7 days, bucket-ID migration.
Tests
cargo test --package aw-sync --test historical_editOn
626af70those cases failed as described in the issue (historical title stuck, latest-event clipped duplicate, peer never updated). They pass here.Fixes the sync-side of ActivityWatch/aw-android#253. Leave that issue open until a release ships this.