How to send a hand-off
Use this guide when a record needs another persona’s work or decision: a closed notebook entry ready for an
SOP check, a supply slip the lead must know about, a question reassigned to a scientist. You start with a
record; you end with a handoff_requests row sent that appears in the
addressee’s inbox and on the program board, and later accepted with a
program-visible thread pinned to the record. Sending needs program.handoffs.manage, which every program
persona except the viewer holds. Every surface here carries the status word
designed until the R3 gates pass.
Hand-offs are the only channel between twins, and they are records. A twin may propose one
(console.propose_handoff), but the row exists only after you approve it; the receiving twin may prepare
only after the receiving person accepts. There is no agent-to-agent channel and no messaging tool.
Prerequisites
- A record in Evidence, Plans, Review or Progress that you can read, and
program.handoffs.manage. - Knowing the persona you address (
to_persona_key); naming a person (to_user_id) is optional.
Choose the kind and the addressee
From the record’s actions choose Hand off (the phase-2 record action “Send to QA” is the same thing with
the quality reviewer pre-selected). Kinds: review (please check this), data (please supply or link data),
decision (please decide this), information (for your awareness). Address a persona; optionally a person.
Write a short message; it passes redaction and never carries retrieved source text.
Send, or approve your twin’s proposal
Send calls POST /programs/{program_id}/handoffs with HandoffCreate {to_persona_key, to_user_id?, kind, linked_type, linked_id, message_md}. The API creates the row sent, writes one inbox item per current holder
of the addressed persona (one item when a person is named), notifies a named addressee with
handoff_received (a persona-wide hand-off goes to the digest), and audits handoff.send. When your twin
proposed the hand-off (“EXP-0421 ready for SOP check”), the proposal waits in your inbox like any other write
and Approve creates the same row; the record then reads “Proposed by Maya N.’s twin (agent); decided by
Maya N.” (illustrative).
What the addressee sees
The item sits under Hand-offs in their inbox with your name, the kind, the linked record and “Why now”. The
first holder to Accept (POST .../handoffs/{handoff_id}/accept) becomes to_user_id; the sibling items
of the other holders are withdrawn in the same transaction; a program-visible thread opens
(visibility = program, scope_type = handoff, pinned to the record) so people, not twins, resolve questions
in the open; and the addressee’s twin, if active, runs twin_handoff to prepare (for QA: the check bound to
the SOP version in force, filed as a proposal for QA to approve). Decline needs a reason. The board shows
the card in the addressee persona’s column.
Complete or withdraw
The person who accepted marks it Complete when the work is in the record (handoff.complete). You can
Withdraw a hand-off that is still sent (handoff.withdraw). Each route is scoped to its party: withdraw
to the sender, accept and decline to the addressee or a current persona holder, complete to the acceptor.
Read what changed
The hand-off page (/p/<slug>/handoffs/[id]) shows the status chip, both parties, the linked record, the
thread and the decision reason. Setup › Audit shows handoff.send, handoff.accept and
handoff.complete with actor_kind = human; a twin-proposed hand-off additionally carries interrupt_id,
twin_id and the two hashes on the handoff.send row.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
409 handoff_exists | a hand-off of the same kind to the same persona on the same record is already sent or accepted | open the existing one; a different kind (for example data after review) is allowed |
409 self_handoff | you would be the addressee | address another persona or person |
| Accept is missing | you are neither the named addressee nor a current holder of the addressed persona | ask the sender to re-address it |
403 bound_user_only on the proposal | you opened a colleague’s twin proposal | only the bound person approves a twin’s proposal |
| No e-mail arrived | the hand-off was addressed to a persona, not a person | persona-wide hand-offs are summarised in the daily digest |
Related
- How to decide from your inbox.
- How to read the program board.
- About stage playbooks for the hand-offs each stage expects.
Source: PRODUCT-PLAN.md Part 7 sections 2 (hand-off duties), 4 (twin-to-twin requests are records) and 7.4; PRODUCT-CONTRACT.md sections 11.4 and 11.7; api/app/db/models/handoff.py (HANDOFF_KINDS, HANDOFF_STATUSES, HANDOFF_LIVE_INDEX); api/app/tools/catalogue.py (console.propose_handoff); api/app/services/audit_actions.py (handoff.*); api/scripts/designed-routes.json (/programs/{}/handoffs routes); web/lib/api/types.ts (HandoffCreate, HandoffDecision, HandoffOut); decisions/0023-handoffs-and-event-triggers.md