Skip to content

Device groups: secondaries keep every feature and stand in, phones follow, no more Google logouts - #218

Merged
danielehrhardt merged 22 commits into
mainfrom
fix/device-groups-v2
Oct 21, 2026
Merged

danielehrhardt merged 22 commits into
mainfrom
fix/device-groups-v2

Conversation

@danielehrhardt

Copy link
Copy Markdown
Contributor

Why

On the second Mac a simple "download these two files to the desktop" chat was stopped twice by the watchdog ("no sign of life for 60 minutes"). The cause, from that Mac's own log: macOS asked whether Godmode may open the Desktop folder (12:16) and nobody was there to click Allow until 18:39. Every command that touched ~/Desktop waited, including Claude Code itself. The chat also ran on an old runner of that Mac, not on the Godmode app of the device group (the same computer was paired twice).

Beyond that bug, a device group only half worked: while the primary was away a secondary was read only, schedules stopped, the phone could not connect anywhere else, and copying browser sessions to the other computer signed you out of Google.

What changes

A secondary keeps every feature while the primary is away, and stands in for it

  • Every computer of the group keeps a full copy of what is shared (setup, automations, task board, My tasks, goals, passkeys, cards, messaging, paired phones). Triggers note each change. Each side pulls the other's changes: the newer change wins, and on a tie a deletion wins, then the primary.
  • While the primary is away nothing is read only anymore. After 2 minutes (or at once when the primary quits) the secondary stands in. It runs the automations, the board, messaging and the phone app, continues the chats that were working on the primary, and runs automations that came due meanwhile.
  • When the primary comes back, the hand-back runs in this order: its changes come over, the chats taken over stay with the secondary, everything changed there goes back, standing in ends, and the app shows the primary again. A starting or waking primary waits until it has heard from the group, so nothing runs twice.

Phone app

  • A phone paired with one computer works with all of them. When the primary is out of reach the app connects to another computer of the group, and only the computer it was paired with can unpair it.

macOS permission prompts

  • Godmode watches for macOS privacy prompts while agents work. It labels the waiting step ("Waiting for you on MacBook Pro von m: macOS asks whether Godmode may open your Desktop folder"), sends one notification and doesn't let the watchdog stop the run.
  • Agents are told when Full Disk Access is missing, and each computer's Full Disk Access shows in Settings → Devices.

Browser sign-ins

  • Google, Microsoft, Apple, PayPal, Meta, LinkedIn, X and similar sessions are never copied to another computer anymore. By default each computer of the group signs in with the saved logins, 2FA and passkeys.

UI

  • A calm device popover (aligned, a quiet hand-off button instead of the white slab).
  • States in words ("Away since 19:40", "Takes over in 0:07", "Stands in").
  • An away banner with countdown.
  • Settings for standing in and browser sign-ins.
  • Full Disk Access warnings and a "Use only the app" fix for a computer that is also paired as an old runner.

Screenshots

Real two-core sandbox (both computers are this Mac, hence the same name):

Primary popover Secondary, primary away
Standing in Settings → Devices while standing in

More states, light and dark, are in docs/screenshots/device-groups-v2/.

Tests

  • devices-e2e covers two real cores. The secondary works offline and stands in, continues the primary's running chat, hands everything back, and the primary does not pick the chat up again.
  • The new suites:
    • replica.test.ts: last writer wins, ties, deletions, deferred rows, delete cascades, unsafe folder names.
    • mac-prompts.test.ts: the parser was checked against the second Mac's real log.
    • browser-sessions.test.ts.
    • apps/mobile/src/lib/peers.test.ts.
  • An independent review (two rounds) found and got fixed, among others: the change-log triggers breaking secrets writes (keys now come from the schema), the replica tables in backups (left out now, and restoring a backup inside a group is refused), late automation ticks that a stand-in already ran, missed automations, duplicate My-tasks for questions, the hand-back when the primary drops again, and a returning primary resuming a chat the secondary continued.
  • Known limit, documented: two computers that can't reach each other at all (different networks, no Tailscale) both do the shared work until they meet again. Settings says to use Tailscale on each computer.
  • The full core suite passes except the two mods tests that also fail on main on this Mac. pnpm -r typecheck and the mobile typecheck pass.

Google, Microsoft, Apple, Amazon, PayPal, Meta and others sign a session
out everywhere when its cookies show up on a second computer. Runners now
get every cookie except those sites'; devices of a group get none unless
the group seeds them, and then only for sites they have no cookies for
yet. The receiving computer drops session-bound cookies itself, so an
older controller can't sign it out either.
… Full Disk Access and runner-twin fixes

- Sidebar popover: one inset for header, rows and footer; roles as quiet tags; state in words (Away since, Takes over in, Stands in, slow connection); Full Disk Access chip and runner-twin hint; hand-off as a two-line button that is only solid while work runs and hides when there is nowhere to go; no monospace in the sidebar row
- Settings > Devices: While the primary is away (switch, minutes, live stand-in state with pending changes), Browser sign-ins (own / seed), Full Disk Access note with how-to, Use only the app for a runner twin (POST /api/devices/retire-runner), copy without the old read-only wording
- Away banner on secondaries: countdown to taking over, standing-in state
- Places: This computer name from THIS_COMPUTER, runner twins after their device and named as runners; Runner tag in the Run on chip and the chat pill
- Screenshots in docs/screenshots/device-groups-v2
… link

fullDiskAccess() answers from the last probe and asks again in the
background when that is older than ten minutes. runnerInfo() reports it
as fullDiskAccess.
macOS asks the person at the computer before a program may open the
Desktop and other folders, and whatever asked waits until someone
answers. On a runner nobody may be there for hours: two runs waited six
hours for a Desktop prompt until the watchdog stopped them.

While runs work on a Mac, services/macPrompts.ts follows tccd's log
(log stream, or log show while a step is silent where it can't stream)
for prompts of this program or Godmode's app. A pending prompt labels
the silent runs it holds, sends one warning notification, and keeps the
watchdog from stopping a silent run; a run stopped after the prompt was
given up (2 hours) names it. Without Full Disk Access the system prompt
tells agents to keep out of those folders or say so first.
The phone knows the computers of its pairing's device group (from GET /api/mobile/me) and moves on to another one when the computer it uses is out of reach: the one that answered last first, then the primary, then the rest. Only the paired computer or the primary can unpair it with a refused key; another computer of the group may not have the key yet. Home, the live accessory and Settings show which computer it goes through.
The group copies pairings and removals between its computers within seconds, so the paired computer is the one reliable judge. A refused key from the primary, like from any other computer of the group, means it doesn't have the key yet: the phone tries the next one.
…rors

Right after macOS asks, every run still looks silent: who a prompt holds
is decided once the runs at work wrote again, and only runs in a step
of their own count. The watchdog spares a silent run only for as long
as a prompt is waited for, and names a dialog only for the runs it held
and nobody answered. With the stream, only its answer ends a prompt;
without it, runs going on again do. A line that can't be handled no
longer ends the reader, a stream that ended is started again, the
watch ends when runs vanish without an event, and the human hears
about the same dialog at most every half hour.

Full Disk Access is decided by what Godmode starts, and an app known
only by its bundle id is recognised in the prompt line.
# Conflicts:
#	packages/core/src/remote/linkServer.ts
…e slots, missed automations from their schedules
…rn, missed windows, failed joins, settle wait
@danielehrhardt
danielehrhardt merged commit 9cf9314 into main Oct 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant