How it works / Leases and carriers
Leases and carriers
Only one carrier holds a piece of work at a time. It holds the work under a lease, which runs out if the carrier goes quiet.
Before you start
The examples use the workspace billing, the handles build-agent and review-agent, and the file baton.md from your first relay. Any baton file will do. Each section opens its own piece of work.
One carrier at a time
A handle is a name that carries work. It belongs to a person or to an agent. The carrier is the handle that holds the work now.
Only the carrier can save the baton, offer the work, or end its stretch on it. Other handles in the workspace can read the work and add notes. They can catch it only when it is offered to them or when it drops. Offers, catches and drops explains both.
The service lets one carrier hold a piece of work at a time. If two handles try to catch it at once, one wins and the other is refused.
Legs
A leg is one carrier's stretch on the work. A leg starts when a handle does one of these:
- opens the work with
handsoff open - catches it with
handsoff continue, from an offer or a drop - takes it over from its own earlier session with
handsoff continue --take-over
A leg ends when the carrier hands the work over, ends its leg with handsoff end, lets its lease run out, is taken over, or closes the work.
open and continue print the leg's number on the service, such as Leg 103. handsoff history counts the legs of one piece of work from 1 instead. History explains its lines.
The lease
The lease says how long the carrier keeps the work. Each save, offer, retract, renew, block or unblock by the carrier starts it again. A note does not. Each workspace sets a default, which starts at 30 minutes. To choose another length for one leg, add --lease to open or continue. It must be from 1 minute to 12 hours.
Here build-agent first asks for a 30-second lease. The service refuses it, because a lease must be at least 1 minute (60 seconds), and opens nothing:
handsoff open BILL-53 --title "Reconcile the March payments" --as build-agent --lease 30s --baton baton.md
This request cannot be used; check its fields and send it again.
invalid: lease_seconds must be from 60 to 43200 seconds; fix: check the documented request fields and send it again
With a one-minute lease, the work opens:
handsoff open BILL-53 --title "Reconcile the March payments" --as build-agent --lease 1m --baton baton.md
Opened BILL-53 at r1 (work 111)
Leg 199; lease expires 2026-10-05T04:49:43.030528+00:00
Durations are a number of seconds, or a number with s, m, h or d, such as 30m.
Keep the lease alive
Every save, offer, retract, renew, block and unblock by the carrier renews the lease. A note does not, because a note is not tied to the carrier's leg. To renew it with no other change, run handsoff renew:
handsoff save BILL-53 --as build-agent --baton baton.md
accepted as r2
handsoff renew BILL-53 --as build-agent
Renewed BILL-53; lease expires 2026-10-05T04:49:44.54693+00:00
The new expiry is now plus the leg's lease length. Here the lease is one minute.
An agent with an adapter does not need to renew by hand. The adapter renews the lease while the agent works, at most once a minute. See Claude Code and Codex.
If a carrier holds the work for a long time without saving, listings show the work as stalled. A workspace starts with a stall threshold of one hour.
When a lease lapses
When the lease runs out, the leg ends and the work drops. Any handle in the workspace can then catch it. If the carrier had offered the work and not yet ended its leg, the offer lapses too.
To see it, wait one minute after the renew above, and do nothing with BILL-53. Its lease runs out, and the work drops:
handsoff list
…
DROPPED BILL-53: Reconcile the March payments — dropped
Last carrier build-agent; ended: lease lapsed at 2026-10-05T04:49:44.54693+00:00
Last accepted r2 at 2026-10-05T04:48:43.723441+00:00
dropped: lease lapsed at 2026-10-05T04:49:44.54693+00:00; held by build-agent; last accepted r2 at 2026-10-05T04:48:43.723441+00:00
Next: catch it
…
The service says lease lapsed and nothing more. It does not know why the carrier went quiet, so it never guesses.
A lapsed carrier can no longer write. Its saves are refused, and the baton stays as it was:
handsoff save BILL-53 --as build-agent --baton baton.md
The baton is no longer this leg's: lease lapsed at 2026-10-05T04:49:44.54693+00:00; the write was not applied. Run handsoff continue 111 to take it back if allowed, or give the notes to the current carrier.
not_holder: leg 199; current leg 199; lease lapsed at 2026-10-05T04:49:44.54693+00:00; fix: run handsoff continue 111 if allowed, or give the notes to the current carrier
The 111 in that line is the work's id. handsoff continue 111 and handsoff continue BILL-53 do the same thing.
The service keeps the refused save in the history, marked late, not accepted. The next carrier sees it too. After an interruption shows what that carrier gets.
Take over in a new session
A session is one run of an agent, or one terminal you work in. The tool tells sessions apart only by the session id you give it with --session or HANDSOFF_SESSION. Adapters set it for you. Two shells with no session id look like the same session.
Your handle may hold work in one session while you start another, for example after a restart. Here review-agent opens BILL-54 in a terminal with no session id:
handsoff open BILL-54 --title "Close the February books" --as review-agent --baton baton.md
Opened BILL-54 at r1 (work 113)
Leg 201; lease expires 2026-10-05T05:19:05.540341+00:00
While that leg is live, continue in a new session, window-2, refuses and names the fix:
handsoff continue BILL-54 --as review-agent --session window-2
Your handle holds this work in another session; use --take-over.
invalid: the requested operation needs correction; fix: use handsoff continue --take-over, or wait for the lease to lapse
Add --take-over to move the hold into the new session:
handsoff continue BILL-54 --as review-agent --session window-2 --take-over
Caught BILL-54 at r1 from review-agent via take-over
Attention
Contact with review-agent was lost: taken over at 2026-10-05T04:49:06.614933+00:00. Its last accepted save was r1 at 2026-10-05T04:49:05.541156+00:00. It may have done more than one step after that save, so every step of the next action, and any step the baton names as in flight, may already be done, not only the first. Before you do each of those steps, check its outcome through the references, even a step that looks safe to repeat: skip it if it was done, and do only the rest if it was partly done. If you cannot tell for a step, stop and block the work.
…
The old leg ends as taken over, and a new leg starts at the current baton. You get the same warning as after a drop, because the old session may have done more after its last save. Only the same handle can take over. It cannot take over while its own offer is waiting.
The old session, the terminal with no session id, can no longer write:
handsoff renew BILL-54 --as review-agent
The baton is no longer this leg's: taken over by review-agent at 2026-10-05T04:49:06.620896+00:00; the write was not applied. Run handsoff continue 113 to take it back if allowed, or give the notes to the current carrier.
not_holder: leg 201; current leg 202; taken over by review-agent at 2026-10-05T04:49:06.620896+00:00; fix: run handsoff continue 113 if allowed, or give the notes to the current carrier
The workspace's lease setting
handsoff workspace settings prints the workspace's default lease, its default offer deadline and its stall threshold:
handsoff workspace settings
Workspace billing (29)
default lease 30m (1800 seconds)
default offer deadline 1d (86400 seconds)
stall threshold 1h (3600 seconds)
The workspace's owner can change the default lease with --default-lease, from 1 minute to 12 hours. Workspaces covers the other settings.
Next, read offers, catches and drops to pass the work on.