@@ -454,17 +454,19 @@ function bookmarkNeedsUpdate(cloudBm, localBm) {
454454 * @param {Array } tombstones - Merged tombstones
455455 * @returns {{toAdd: Array, toUpdate: Array, skippedByTombstone: Array} } - Categorized bookmarks
456456 */
457- function categorizeCloudBookmarks ( cloudBookmarks , localBookmarks , tombstones , modifiedLocalIds ) {
457+ function categorizeCloudBookmarks ( cloudBookmarks , localBookmarks , tombstones ) {
458458 const localByUrl = new Map ( localBookmarks . filter ( ( b ) => b . url ) . map ( ( b ) => [ b . url , b ] ) ) ;
459459
460460 const toAdd = [ ] ;
461461 const toUpdate = [ ] ;
462462 const skippedByTombstone = [ ] ;
463- const skippedByLocalModification = [ ] ;
463+ // NOTE: locallyModifiedBookmarkIds is intentionally NOT used here.
464+ // Per design: cloud always wins on pull. The pushing browser's order is truth.
465+ // Since sync happens immediately, there's no real conflict scenario.
464466 const alreadyExistsUnchanged = [ ] ;
465467
466468 console . log (
467- `[MarkSyncr] categorizeCloudBookmarks: ${ cloudBookmarks . length } cloud, ${ localBookmarks . length } local, ${ tombstones . length } tombstones, ${ modifiedLocalIds ?. size || 0 } locally modified `
469+ `[MarkSyncr] categorizeCloudBookmarks: ${ cloudBookmarks . length } cloud, ${ localBookmarks . length } local, ${ tombstones . length } tombstones`
468470 ) ;
469471
470472 for ( const cloudBm of cloudBookmarks ) {
@@ -491,9 +493,6 @@ function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones, mo
491493 const localBm = localByUrl . get ( cloudBm . url ) ;
492494 if ( ! localBm ) {
493495 toAdd . push ( cloudBm ) ;
494- } else if ( modifiedLocalIds ?. has ( localBm . id ) ) {
495- // Local bookmark was modified by the user since last sync - local wins
496- skippedByLocalModification . push ( cloudBm . url ) ;
497496 } else if ( bookmarkNeedsUpdate ( cloudBm , localBm ) ) {
498497 toUpdate . push ( { cloud : cloudBm , local : localBm } ) ;
499498 } else {
@@ -511,23 +510,14 @@ function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones, mo
511510 }
512511 }
513512
514- if ( skippedByLocalModification . length > 0 ) {
515- console . log (
516- `[MarkSyncr] 🏠 Skipped ${ skippedByLocalModification . length } cloud→local updates (locally modified, local wins):`
517- ) ;
518- for ( const url of skippedByLocalModification . slice ( 0 , 5 ) ) {
519- console . log ( ` - ${ url } ` ) ;
520- }
521- }
522-
523513 if ( alreadyExistsUnchanged . length > 0 ) {
524514 console . log (
525515 `[MarkSyncr] ✓ ${ alreadyExistsUnchanged . length } cloud bookmarks already exist locally (unchanged)`
526516 ) ;
527517 }
528518
529519 console . log (
530- `[MarkSyncr] Categorization result: toAdd=${ toAdd . length } , toUpdate=${ toUpdate . length } , skipped=${ skippedByTombstone . length } , localWins= ${ skippedByLocalModification . length } , unchanged=${ alreadyExistsUnchanged . length } `
520+ `[MarkSyncr] Categorization result: toAdd=${ toAdd . length } , toUpdate=${ toUpdate . length } , skipped=${ skippedByTombstone . length } , unchanged=${ alreadyExistsUnchanged . length } `
531521 ) ;
532522
533523 return { toAdd, toUpdate, skippedByTombstone } ;
@@ -1282,9 +1272,11 @@ function setupBookmarkListeners() {
12821272 return ;
12831273 }
12841274
1285- // Always track locally modified bookmarks, even during sync
1286- locallyModifiedBookmarkIds . add ( id ) ;
1287- debouncedSaveLocallyModifiedIds ( ) ;
1275+ // Only track user-initiated removals, not sync-driven ones
1276+ if ( ! isSyncDrivenChange ) {
1277+ locallyModifiedBookmarkIds . add ( id ) ;
1278+ debouncedSaveLocallyModifiedIds ( ) ;
1279+ }
12881280
12891281 // ALWAYS create tombstones for deleted bookmarks, even during sync.
12901282 // Without a tombstone, the next sync will re-add the bookmark from cloud.
@@ -1644,8 +1636,7 @@ async function performSync(sourceId) {
16441636 const { toAdd : newFromCloud , toUpdate : bookmarksToUpdate } = categorizeCloudBookmarks (
16451637 cloudBookmarks ,
16461638 updatedLocalFlat ,
1647- mergedTombstones ,
1648- locallyModifiedBookmarkIds
1639+ mergedTombstones
16491640 ) ;
16501641
16511642 console . log ( `[MarkSyncr] 🔍 New bookmarks from cloud: ${ newFromCloud . length } ` ) ;
@@ -1741,15 +1732,11 @@ async function performSync(sourceId) {
17411732 console . log ( `[MarkSyncr] Local checksum: ${ localChecksum } ` ) ;
17421733 console . log ( `[MarkSyncr] Cloud checksum: ${ cloudChecksum } ` ) ;
17431734
1744- // Determine if we have LOCAL (user-initiated) changes to push to cloud
1745- // NOTE: bookmarksToUpdate are cloud→local updates (received FROM cloud), NOT local changes.
1746- // Including them here was causing the receiving browser to re-push cloud data back,
1747- // overwriting the original browser's ordering changes.
1748- const hasLocalChangesToPush =
1749- localAdditions . length > 0 ||
1750- locallyModifiedBookmarkIds . size > 0 ||
1751- localTombstones . length > cloudTombstones . length ||
1752- deletedLocally > 0 ;
1735+ // Simplified push logic: push if local state differs from cloud.
1736+ // The pushing browser's order is truth. After pull+reorder, if our local
1737+ // state differs from cloud (new local bookmarks, tombstone deletions, etc.),
1738+ // push the current state. Checksum comparison handles this cleanly.
1739+ const hasLocalChangesToPush = localChecksum !== cloudChecksum ;
17531740
17541741 // Check if there were any changes during this sync
17551742 const hasChanges =
0 commit comments