SCREEN / STATE INVENTORY
Settings › Sync — Off · Create · Join (code, waiting) · Approve on existing device · Expired/failed · Recovery (phrase) · Locked key · Relay unreachable · Removed · Devices (name, role, last contact, this device) · Queue (summary, full list, per-row action) · Remove device · Remove runner (affected work + recovery).
Project › Runs on — Can run · Board only with reasons (repo, runtime, account) · Link a checkout · partially cleared · Local setup details (paths).
Board — owner row on tiles (▸ here / ▸ Studio Mac / ○ offline) · ⧉ conflict flag · stale (synced 41m ago).
Card — owner chip + last confirmed · Start here / Start on Studio Mac / Starting… / View session / Move execution · question answered → relay chip ◔ ✓ ✕ ⌛ — ? · review current vs rejected old approval · persistent conflict entry.
Conflict — entry · compare (side by side / stacked) · base on/off · incoming edit · edit vs delete · combined editor · resolved with undo · survives stage move.
Move execution — clean · confirm · done · dirty work · local-only repo · missing runtime/account · old host offline. Race — both pressed · one owner. Phone — two additions to Grid Phone.
COMPONENT CHANGES
OwnerChip (new) — glyph + words: “▸ Runs here”, “▸ Runs on Studio Mac”, “○ Runs on Studio Mac · offline 41m”, “Runs on Studio Mac (removed)”. Tile (muted row), card header (bordered), phone. Never color-only.
RelayChip (new) — six states with fixed glyphs; note is a prop. Relay acceptance is “◔ Queued”, never ✓.
Tile — one optional owner row above the footer, sharing the row with the ⧉ flag. Session header — OwnerChip, then Start here / Start on Studio Mac (exclusive), View session (always), Move execution….
Question / Review blocks — footer with RelayChip and Cancel; review gains the rejected-old-approval insert above the current diff.
ConflictEntry + ConflictResolver (new dialog) — two version groups with author/time, base toggle, incoming banner, combined textarea. Under 640px it stacks; each group keeps its own pick button.
MoveExecution dialog (new) — from/to, checks (repo, work, runtime, account, host), blocked insert or what-happens + stop confirmation. Device row gains a role chip; Queue joins the Devices card in Settings › Sync; Runs on group joins project settings.
INTERACTION NOTES · BACKEND FACTS NEEDED
Owner chip needs: owning device, last confirmation time from that device, reachability now. “Last confirmed” is the runner’s own heartbeat, not relay receipt.
Start needs one authoritative claim per card. Both devices show Starting… until it returns; the loser gets the toast and View session. No timer, no takeover.
Relay states need: queued (here), delivered, applied (runner ack + result), rejected (reason: review changed, already answered, card moved), expired (runner TTL), cancelled (here), uncertain (delivered, no ack within N min → “check the session”, never ✓).
Review approval carries the diff identity it approved; the runner rejects on mismatch and returns the current diff summary.
Conflict needs per-field versions with device, author, time and optional base; both kept until a resolution write. Stage moves are separate writes. An incoming version during resolution never replaces the one on screen.
Move execution needs target readiness (repo remote match, runtime, account), source work state (dirty files, unpushed commits), whether the provider can resume the conversation, and a runner ack that the agent stopped. Old host offline ⇒ no stop ack ⇒ no move; only ownership recovery from Devices.
Keyboard — Tab follows reading order; dialogs trap focus and return it to the opener; Esc = cancel / keep both; ⌘1 / ⌘2 pick a conflict side; Enter on a focused option answers. Every status chip has words; glyphs are decorative.
Pairing — three-word code on both devices; approval is a tap on the existing device. Scan-to-approve from the iPhone camera is proposed and needs backend work.
UNRESOLVED PRODUCT CHOICES
1 · How long a queued answer waits on the runner before it expires (24h shown; not decided).
2 · Whether a card whose runner was removed can be claimed by any prepared device, or only after an explicit release.
3 · Whether uncertain relay results retry on their own or wait for the user.
4 · Whether Move execution is offered while the target is online but locked.
5 · Whether the losing conflict version stays in History for a window (30 days shown) or forever.
6 · A second runner per workspace — out of scope; the UI never assumes it.
7 · A pairing gesture beyond the three-word code — needs backend work before it is promised.