Claude Cowork's July update lets an agent run cross-device and around the clock: you start a task on your phone, the agent executes in the cloud, and you find the result on your desktop. This is convenient and introduces a new failure mode: the session state that connects the two devices can drift, race, or silently disappear.
This article gives you a two-terminal test that reproduces the handoff failure before it happens in production.
The handoff failure
When an agent runs across devices, three things must stay consistent:
- Task identity: both devices reference the same task ID
- Task state: both devices agree on whether the task is running, complete, or failed
- Result delivery: the output reaches the device that asked for it, not a stale session
If any of these drift, you get one of these failures:
| Failure | Symptom | Root cause |
|---|---|---|
| Stale session | Phone shows "running" but desktop shows "done" | State not propagated |
| Lost task | Desktop has no record of the phone's task | Task ID not persisted |
| Duplicate result | Both devices try to deliver the same output | No idempotency on delivery |
| Orphaned agent | Agent keeps running after both devices disconnect | No lease or heartbeat |
The two-terminal test
Open two terminals. Terminal A represents the phone (task initiator). Terminal B represents the desktop (result consumer).
# session_handoff_test.py
"""
Simulates a cross-device agent session handoff.
Run in two terminals to test state synchronization.
"""
import json
import time
import os
import sys
STATE_FILE = "/tmp/agent_session_state.json"
def init_state():
state = {
"task_id": f"task-{int(time.time())}",
"status": "initiated",
"initiator": "terminal-a",
"result": None,
"updated_at": time.time()
}
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
print(f"Terminal A: initiated task {state['task_id']}")
def check_state():
if not os.path.exists(STATE_FILE):
print("Terminal B: no session found - handoff FAILED")
return False
with open(STATE_FILE) as f:
state = json.load(f)
age = time.time() - state["updated_at"]
print(f"Terminal B: task={state['task_id']} status={state['status']} age={age:.1f}s")
if state["status"] == "completed" and state["result"]:
print(f"Terminal B: result received: {state['result']}")
return True
return False
def complete_task():
with open(STATE_FILE) as f:
state = json.load(f)
state["status"] = "completed"
state["result"] = {"files_changed": 3, "tests_passed": 12}
state["updated_at"] = time.time()
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
print(f"Agent: completed task {state['task_id']}")
if __name__ == "__main__":
mode = sys.argv[1] if len(sys.argv) > 1 else "check"
if mode == "init":
init_state()
elif mode == "complete":
complete_task()
elif mode == "check":
success = check_state()
sys.exit(0 if success else 1)
elif mode == "verify":
# Run full handoff sequence
init_state()
time.sleep(1)
complete_task()
time.sleep(1)
assert check_state(), "Handoff verification FAILED"
print("PASS: session handoff verified")
Test sequence
Terminal A (phone):
python3 session_handoff_test.py init
# Output: Terminal A: initiated task task-1784000000
Agent (simulated):
python3 session_handoff_test.py complete
# Output: Agent: completed task task-1784000000
Terminal B (desktop):
python3 session_handoff_test.py check
# Output: Terminal B: task=task-1784000000 status=completed age=2.0s
# Output: Terminal B: result received: {'files_changed': 3, 'tests_passed': 12}
Failure injection
# Delete the state file to simulate a lost session
rm /tmp/agent_session_state.json
python3 session_handoff_test.py check
# Expected: Terminal B: no session found - handoff FAILED
What the test verifies
| Invariant | How the test checks it |
|---|---|
| Task identity persists across devices | Both terminals read the same task_id from the state file |
| State propagation works | Terminal B sees "completed" after the agent writes it |
| Result delivery is reliable | Terminal B receives the result payload |
| Missing state is detected | The test reports FAILURE, not a silent empty result |
Limitations
- This test uses a local file for state. Real cross-device agents use a server-side store with network latency and partition risk.
- The test does not cover the case where both devices try to modify the task simultaneously (write-write conflict).
- A real handoff also needs a lease so the agent stops if no device connects within a timeout.
What to check
Before enabling a cross-device agent on your workflow, run this test with your actual session store. If the "check" command returns stale state, your handoff is broken, and the agent's results will silently disappear between devices.
Top comments (0)