Repository navigation
Releases: siyuanj/local-tasks-bridge
Release list
Local Tasks Bridge 1.0.0
The first release as Local Tasks Bridge, the maintained fork of
syncweave-labs/reminders-task-bridge.
Added
- Menu bar app
Local Tasks Bridge.app, which owns the Reminders
permission, supervises the sync engine, and shows status, notifications and
held changes. It starts at login through the LaunchAgent
io.github.siyuanj.local-tasks-bridge. - Setup assistant: Reminders access, choice of sign-in method, Google
sign-in, list selection (including creating a matching Reminders list for an
existing Google list such as My Tasks), options, and a safe first sync — a
dry run, then a sync without deletions or completions. Earlier installations
are offered Import My Existing Setup. - English and Simplified Chinese in the app, engine messages,
ltb
output, notifications, the approval dialog, the installer and the
documentation (languagesetting,LTB_LANG). - Compiled EventKit helpers (
ltb-reminders-export,
ltb-reminders-apply) inside the app, instead of interpreting the Swift
sources withswifton every cycle. - Two Google sign-in methods: the shared “Local Tasks Bridge” client
embedded in release builds, or your own Google Cloud “Desktop app” client
(ltb client import); Web clients are refused with an explanation
(oauth_clientsetting). - Account binding by Google account ID (OpenID subject): signing in again
to the same account keeps the sync map, a different account stops the sync.
Bindings from earlier versions are upgraded in place. - Proxy setting (
proxy): the system default,none, or an explicit HTTP
proxy — for networks such as mainland China, where Google is reachable only
through a proxy. - Instant sync of local edits: the app starts a cycle as soon as Reminders
reports a change (sync-now,trigger_min_interval_seconds). - Quota protection for the shared client: while signed in through it, the
engine polls Google at most every 5 minutes and spaces triggered syncs at
least 30 seconds apart; local edits still upload within seconds. Cycles that
wrote nothing skip the verification re-read. - Review Pending Changes window: when the app hosts the engine, large
batches of deletions and completions are reviewed there (Apply N Changes
or Keep On Hold); without the app, the engine asks with anosascript
dialog. - Reset Settings… when
config.jsoncan't be read, and Pair Accounts
Again… for account changes (with Use a Different Google Account when
the change was a mistake). - A proxy setting on the sign-in step of the setup assistant, so the proxy is
in place before signing in. - Pause and resume (
ltb pause,ltb resume); the paused state survives
restarts. - JSON command-line interface for the app —
version,status,lists,
config show|init|merge|validate,client import|status,auth,
account,signout,sync,approvals show|apply|hold,pause,
resume,sync-now,doctor --json,migrate,agent,uninstall—
with documented exit codes (contract). ltb rebuild: recovery from account binding required after you
confirm the account pair — it backs up, holds the background loop and the
sync lock, and builds a new sync map without deleting anything.- The
ltbcommand, linked into~/.local/binby the installer. - One-line installer
install.sh: installs and updates from GitHub
releases with checksum verification, installs a downloaded zip (--zip) or
a source build (--from-source), and uninstalls (--uninstall). It never
usessudo. - Release automation: Apple silicon and Intel zips with an embedded
Python,SHA256SUMSand a release manifest; prepared for Developer ID
signing and notarization. - Simulated end-to-end tests: the real engine CLI against a fake Google
OAuth/Tasks server and fake Reminders helpers; hermetic installer tests. - Migration from the 2026-09 private trial and from upstream installations
(ltb migrate, Import My Existing Setup), keeping the Google sign-in and
the sync map. - Check for Updates…, Copy Diagnostics, Open Logs and
Open Data Folder in the menu. - Documentation in English and Chinese: README, Google Cloud setup,
configuration and command-line reference, troubleshooting and FAQ,
architecture, migration, releasing, roadmap and the privacy policy.
Changed
- Name and locations. The product is now Local Tasks Bridge (formerly
“iCloud Reminders ↔ Google Tasks Sync”,icloud-reminders-google-sync):
bundle identifierio.github.siyuanj.LocalTasksBridge; LaunchAgent
io.github.siyuanj.local-tasks-bridge(was
com.icloud-reminders-google-sync); settings in
~/.config/local-tasks-bridge/(was~/.config/icloud-reminders-google-sync/);
logs in~/Library/Logs/LocalTasksBridge/; program files inside the app
bundle (was~/.local/share/icloud-reminders-google-sync/releases/…). - Defaults for new setups: two-way sync; completions and deletions within
the safety limits (25 items / 25 %); reminders without a due date included;
existing Google tasks imported; a 1-minute interval (5 minutes with the
shared client); approval prompts on. - Google sign-in requests only the Google Tasks scope plus
openidand
email; upstream also requested Google Calendar access. - The Reminders write helper receives the same list boundary as the exporter,
so reads and writes cover only the selected lists. - Private files default to the folder that contains
config.json. - Repository layout:
engine/,macos/,tests/,scripts/,docs/. - The Chinese interface uses Apple's term 列表 for lists.
Fixed
From the 2026-09 trial:
- A task completed in Google could be misread as a deletion in the same cycle
and deleted; completed reminders are now re-read after inbound changes. - Completing the last remaining item of a list now propagates, while the
empty-source guard still blocks deletions. - Completing a Google task after an unknown network outcome is retried only
once a read confirms that the task is still open; other writes are never
repeated blindly. - Google Tasks reads are retried a bounded number of times after transient
network errors, such as TLS failures through a local proxy. - Scheduled cycles keep a fixed 60-second start-to-start cadence instead of
waiting a full interval after each cycle (previously 66–104 s between
starts). - A sign-in that left the Google Tasks permission unticked is rejected
immediately instead of failing on every cycle. - A stray request to the loopback sign-in port, such as a browser's favicon
request, no longer breaks the sign-in.
From the simulated end-to-end tests and reviews before release:
- Unselecting a list, renaming or removing a list on either side, or a Google
list title that now points to a different list is never read as deletions;
tasks of lists outside the sync are left alone. A list renamed on one side is
treated as a new list. - Reminders due more than 365 days ahead are synced and no longer look
deleted; the lookahead window applies only to the inherited calendar mode. - A Google edit keeps the reminder's time of day; a date changed in Google
moves the reminder to the new date at the same time. - A due date changed or cleared in Google is applied on the Mac instead of
being reverted. - Editing a reminder on the Mac after its Google task was deleted no longer
loses the edit: if the edit is newer, the task is created again. status.jsonand notifications keep only the first line of an error, so
titles stay in the private log.- Reads get up to 3 attempts after dropped connections, timeouts, HTTP 429
and 5xx. - The sync map is saved before a conflict (with
conflict_policy: skip) or a
failed verification ends a cycle. - A failed manual sync is recorded as failed instead of staying “running”, and
a failed sync is reported before “background sync isn't running”. - A refused consent or an unticked Tasks permission is reported as a sign-in
error (exit code 3). - Signing out stops using a migrated gcloud credential, and an empty success
response from Google's revoke endpoint counts as revoked. ltb uninstallandltb agent uninstallrun from Terminal stop the running
login item; the app marks its own calls withLTB_CALLER=app.- A background loop hosted by the app stops when the app is gone.
From the second review round:
-
A list with no entries in the sync map yet — first sync, rebuilt or lost map,
newly selected list — syncs without deletions or completions on that cycle. -
A deleted Google task is recreated only when the reminder was edited after
the deletion or the list doesn't sync deletions; while deletions are held,
nothing is recreated or written. -
Pairing an existing Google task with a reminder keeps the Google notes on
both sides. -
Pausing keeps the last sync state visible.
-
uninstall --delete-dataremoves only the product's own files, works with
an unreadableconfig.json, and reports a failed revocation. -
migratemoves legacy/tmplogs into the backup folder;migrate --force
also backs up the replacedconfig.json. -
A network failure while refreshing the sign-in is reported as a network
problem (exit code 9), not as “sign in again”. -
Write commands finish before honouring SIGTERM, and the app never cancels
them; Quit waits.config init --forceworks on an unreadable config. -
The app refuses to turn on Start at login, import, finish setup or install
the command-line tool while it runs from a temporary location. -
The installer updates an existing copy where it is, refuses a
--dest
ending in.app, and explains thehttps_proxysetting when a download
fails. -
Settings changed with
ltb config mergewhile the sync loop runs now take
effect on ...