- Fail the run when the connection drops while a sync is running, and clear
the active conflict, so the queue is not sent on a dead client and a
re-login as another account never receives this profile's uploads.
- Backfill last_modified from upload_time in the 37 -> 38 migration and drop
the "last_modified is null" arm from the sync-check query, so rows that
predate the column stop reporting as changed on every poll.
- Skip conflicts during auto runs and report how many were skipped, instead
of opening the modal conflict dialog from a timer or a file-watcher event.
- Remove the duplicated download loop and the stray '<' plus extra brace
that left the head unbuildable.
- Skip the fallback upload under DownloadMode so a local-only deck is not
pushed when the user asked for downloads only.
- Treat a server row without a hash as unknown rather than changed, so
pre-migration decks are not re-downloaded on every sync.
Sync keyed local files to server rows by path, but the server names a row
after the deck, not after the file, so any deck whose file name and deck name
differ uploaded again on every run. A second pass then brought the server copy
back as an extra local file.
- Match by the sidecar's row id, then guid, then content, and only then by the
server path, so renaming either side no longer duplicates a deck.
- Key the server tree by row id: the server allows several rows with the same
name in one folder, and keying by path silently dropped all but one.
- Stop discarding rows whose name contains '/' ("U/W Control"). They stay
matchable, and only the download file name is sanitised.
- Build the download path from the folder plus a sanitised row name, including
every folder segment, so a hostile server name cannot escape the deck folder.
- Download a colliding server row under a unique name and count it, instead of
skipping it without a trace.
- Hash the whole deck instead of the card hash: SHA-1 over the native deck
string covers name, printings, banner, tokens and sideboard plans. The client
and the server hash the exact bytes they exchange, so both agree.
Adds deck_content_hash_test for the client/server hashing invariant and for the
name-only change the card hash cannot see.
- Emit SyncSummary from syncFinished() instead of four positional ints, and
build it from a single currentSummary() helper, so the counts cannot drift
between the status message and the signal.
- Use a designated initializer for LocalFile, like the other records.
- Drop setClient() and setColorIdentityProvider(): both fields are already
constructor parameters and the setters had no callers. setProfile() and
setDeckPath() stay, since the tab re-reads them before every run.
- Fix the include paths in tab_deck_storage.h and the narrowing conversion of
the tree item id, so the branch builds again.