While investigating #194, I read through the cloud sync code and found three additional bugs. Grouping them here to keep noise low.
1. Gist search doesn't paginate — users with 100+ gists get duplicate sync gists
File: src-tauri/src/services/gist_sync.rs, line 225
let url = format!("{}/gists?per_page=100", self.api_base);
GitHub's Gist list API caps at per_page=100 with no pagination. If a user has more than 100 gists, find_or_create_gist will never find the existing sync gist and creates a new one every time "Connect" is pressed. Subsequent pushes go to an ephemeral gist that's abandoned on next connect.
Fix: Paginate using the Link header, or store the gist ID on first creation and skip the search entirely on subsequent connects.
2. disconnect_cloud_sync sets gist ID to empty string instead of deleting it
File: src-tauri/src/commands/cloud_sync.rs, lines 129–138
let _ = db.set_setting("sync_gist_id", "");
get_sync_auth_status (line 119) checks gist_id.is_some(). If get_setting returns Some("") for empty strings, is_authenticated stays true after disconnect. The GitHub token is also left in the DB, so a re-connect reuses it without re-validating — which fails silently if the token has expired.
Fix: Delete both keys instead of setting them to empty strings, or normalize empty strings to None in the auth check.
3. apply_pulled_payload uses wrong backup extension strategy
File: src-tauri/src/services/gist_sync.rs, line 551
let backup = path.with_extension("md.bak");
Path::with_extension replaces the existing extension rather than appending. For .md files this produces the correct result by coincidence (CLAUDE.md → CLAUDE.md.bak), but for .json files it would produce settings.md.bak instead of settings.json.bak.
The project's own utils/backup.rs handles this correctly using format!("{}.bak", file_name) with with_file_name. This code path should use the same approach.
4. gh auth token doesn't verify gist scope
File: src-tauri/src/commands/cloud_sync.rs, line 17
gh auth token returns whatever token is stored regardless of scopes. If a user authenticated gh for Git operations only (no gist scope), the connect step succeeds but all Gist API calls return 403. The error surfaces as a generic failure message.
Fix: Use gh auth token --scopes gist or check the token's scopes against the GitHub API before storing it.
While investigating #194, I read through the cloud sync code and found three additional bugs. Grouping them here to keep noise low.
1. Gist search doesn't paginate — users with 100+ gists get duplicate sync gists
File:
src-tauri/src/services/gist_sync.rs, line 225GitHub's Gist list API caps at
per_page=100with no pagination. If a user has more than 100 gists,find_or_create_gistwill never find the existing sync gist and creates a new one every time "Connect" is pressed. Subsequent pushes go to an ephemeral gist that's abandoned on next connect.Fix: Paginate using the
Linkheader, or store the gist ID on first creation and skip the search entirely on subsequent connects.2.
disconnect_cloud_syncsets gist ID to empty string instead of deleting itFile:
src-tauri/src/commands/cloud_sync.rs, lines 129–138get_sync_auth_status(line 119) checksgist_id.is_some(). Ifget_settingreturnsSome("")for empty strings,is_authenticatedstaystrueafter disconnect. The GitHub token is also left in the DB, so a re-connect reuses it without re-validating — which fails silently if the token has expired.Fix: Delete both keys instead of setting them to empty strings, or normalize empty strings to
Nonein the auth check.3.
apply_pulled_payloaduses wrong backup extension strategyFile:
src-tauri/src/services/gist_sync.rs, line 551Path::with_extensionreplaces the existing extension rather than appending. For.mdfiles this produces the correct result by coincidence (CLAUDE.md→CLAUDE.md.bak), but for.jsonfiles it would producesettings.md.bakinstead ofsettings.json.bak.The project's own
utils/backup.rshandles this correctly usingformat!("{}.bak", file_name)withwith_file_name. This code path should use the same approach.4.
gh auth tokendoesn't verify gist scopeFile:
src-tauri/src/commands/cloud_sync.rs, line 17gh auth tokenreturns whatever token is stored regardless of scopes. If a user authenticatedghfor Git operations only (nogistscope), the connect step succeeds but all Gist API calls return 403. The error surfaces as a generic failure message.Fix: Use
gh auth token --scopes gistor check the token's scopes against the GitHub API before storing it.