A Human Accepted the Handoff. Then the Assignment Changed. A new analysis of AI assistant handoff evaluation identifies a gap where acceptance evidence can be stale by the time an assistant authorizes an outgoing message, even though every cited event is real. The article, developed with AI assistance using synthetic examples and Python tests rather than production logs, proposes a revision-based contract in which each outgoing claim cites a revision and is checked against a fence — the authoritative revision captured at the release decision — returning 'stale' when the cited revision does not match the fence and 'unknown' when the snapshot prefix is incomplete. The author argues that teams must decide whether ownership transfers only when the new assignee accepts or immediately on reassignment, since a pending transfer and a completed reassignment must not share one ambiguous event label. Alice accepts a support request. A minute later, the request moves to Bob, who has not accepted it yet. The assistant still has Alice’s acknowledgment and tells the customer, “Alice is your assigned support contact.” Every event in that acknowledgment is real. The message is still wrong about the current assignment. This article was developed with AI assistance. The example and Python tests are synthetic, not production logs. They explore a gap in handoff evaluation: evidence that once supported a claim can stop supporting it before the assistant speaks. A previous article tested whether acceptance evidence existed before an assistant made a promise. That catches a premature claim. It does not catch a stale one: an acceptance can precede the message and still belong to an assignment that has ended. The new question is whether the acceptance belongs to the current assignment at the point where the system authorizes the outgoing message. That requires more than a timestamp or an “ever accepted” flag. It requires a definition of when responsibility begins and ends. Consider this fictional sequence, recorded by one assignment authority for one request lifecycle: Under this contract, Alice’s acceptance is valid from revision 1 until revision 2. Bob’s is valid from revision 3 until revision 4. Between revisions 2 and 3, someone is assigned but nobody has accepted the current assignment. Those are different customer-facing states. Some teams deliberately keep Alice responsible until Bob accepts. That is a different contract, and often a reasonable one. In that design, requesting a transfer must not remove Alice as owner; the ownership change happens only when the transfer completes. Decide which contract your workflow follows before writing the test. A pending transfer and a completed reassignment must not share one ambiguous event label. The example uses monotonically increasing revisions issued by a single authority. Each row is a committed snapshot of the current owner and whether that owner accepted. Revisions belong to the request lifecycle, not to a person. If Alice receives the request again later, that is a new revision; her earlier acceptance cannot silently reactivate. An outgoing claim records the revision it cites. The checker also receives a fence: the authoritative revision against which that message was checked for release. This is a captured value from the release decision, not the newest revision found when an analyst opens the logs tomorrow. Suppose the model drafts its sentence using revision 1. Before release, the assignment service is at revision 2. The sentence is stale even if the old acknowledgment is delivered again after the reassignment. Arrival order does not make an old revision current. This setup separates two questions that are easy to blur: did the draft cite the current revision, and does that revision actually support the named owner’s acceptance? A fresh revision can still describe an unaccepted assignment. Save the following code as check owner.py and run python3 check owner.py. It needs no packages. All rows must be normalized snapshots from the same request lifecycle and authority. The small fixture starts at revision 1 and requires a complete prefix through the fence; it intentionally refuses to infer missing state. Python 3. Snapshots: revision, owner, accepted . All rows belong to ONE request lifecycle and authority.def check rows, fence, cited revision, named owner : versions = {} for row in rows: rev, owner, accepted = row if rev fence: continue Later changes cannot rewrite this decision. if rev in versions and versions rev = row: raise ValueError 'conflicting revision' versions rev = row if set versions = set range 1, fence + 1 or fence < 1: return 'unknown' Capture gap, not proof of no owner. , owner, accepted = versions fence if cited revision = fence: return 'stale' if owner is None or not accepted: return 'unaccepted' if owner = named owner: return 'wrong owner' return 'supported at fence' A = 1, 'Alice', True B = 2, 'Bob', False Reassignment ends Alice's ownership.C = 3, 'Bob', True D = 4, None, False Release ends Bob's ownership.cases = 'before move', A, B , 1, 1, 'Alice', 'supported at fence' , 'old acceptance', A, B , 2, 1, 'Alice', 'stale' , 'assigned not accepted', A, B , 2, 2, 'Bob', 'unaccepted' , 'new acceptance', A, B, C , 3, 3, 'Bob', 'supported at fence' , 'wrong person', A, B, C , 3, 3, 'Alice', 'wrong owner' , 'released', A, B, C, D , 4, 4, 'Bob', 'unaccepted' , 'late duplicate', A, B, C, A , 3, 3, 'Bob', 'supported at fence' , 'shuffled capture', C, A, B , 3, 3, 'Bob', 'supported at fence' , 'capture gap', A, C , 3, 3, 'Bob', 'unknown' , 'empty capture', , 1, 1, 'Alice', 'unknown' , if name == ' main ': for name, rows, fence, cited, owner, expected in cases: assert check rows, fence, cited, owner == expected, name print name, 'PASS' try: check A, 1, 'Bob', True , 1, 1, 'Alice' except ValueError: print 'conflicting revision PASS' else: raise AssertionError 'conflict was accepted' All eleven assertions passed locally. This verifies the checker against these fixtures, not a routing product. In particular, old acceptance returns stale, while assigned not accepted returns unaccepted. Both prevent the sentence from passing, but they need different fixes. For a stale draft, regenerate or suppress the message using the current state. For an unaccepted assignment, the language needs to describe waiting rather than acceptance; the operations team may also need to investigate why Bob has not taken it. Rewriting a sentence cannot supply that acceptance. The before move case is equally important. Revision 2 does not retroactively invalidate a message released against revision 1. The audit must preserve what was true at the decision point. Conversely, the released case shows why an old successful acceptance cannot justify a new message after ownership has ended. Duplicates and shuffled capture order do not change the answer. Conflicting content under one revision raises an error. A missing revision returns unknown, even when the latest captured row looks reassuring. This is a conservative fixture contract, not a requirement that every production system replay its entire history; an authenticated complete snapshot could support a different checker. A separate “get current owner” request followed by “send message” leaves a race. Ownership can change between them. Calling the result a fence does not close that gap. One implementation option is to let the authority conditionally append an outgoing message intent only if the expected assignment revision still matches. That check and append need a shared serialization boundary. If another change wins first, the append fails and the assistant must reconsider the message. This is a proposed integration design, not something this Python function implements. It also establishes only the state at authorization. A message can sit in an outbox while another transfer happens. If your promise must remain current at delivery, add a delivery-stage version check and a defined cancellation or replacement policy. Even then, distinguish provider acceptance from what the customer actually sees. The checker’s supported at fence result deliberately makes no claim about later delivery. For customer wording, “Bob accepted your request at 10:04” is a historical statement. “Bob is your assigned support contact” claims current ownership. “Bob is working on it now” claims activity too. Choose the sentence you can support, and keep its claim type with the release record. A timestamp should not be used to disguise an assignment change the system already knows about. A staging test can hold the outgoing draft, change the owner, and then attempt release with the old revision. The expected result is rejection or regeneration, with the attempted revision and release outcome recorded. Next, delay Bob’s acceptance: a fresh assignment must not be promoted to an accepted one. Finally, replay Alice’s old acknowledgment after Bob accepts and verify that it cannot restore Alice’s ownership. Keep request-lifecycle ID, assignment revision, named owner, exact draft, release decision and customer-visible message together. Use an authoritative ordering within that lifecycle; independent database counters or unsynchronized service clocks do not automatically provide one. If the capture cannot connect the release to the state it checked, report the claim as unverified. The follow-up service check stays separate. Who eventually answered, whether the customer had to repeat themselves, and how long they waited still matter. A perfectly versioned ownership statement can accompany a poor support experience. The useful change is small but consequential: acceptance becomes a state with a lifetime. Once a request moves, the next sentence needs evidence for the new assignment. Keeping the old acknowledgment is good audit history. Treating it as a permanent permission to promise is the bug. Related: An AI Handoff Can Finish Successfully and Still Promise Too Early https://pub.towardsai.net/an-ai-handoff-can-finish-successfully-and-still-promise-too-early-5bb13465abae . A Human Accepted the Handoff. Then the Assignment Changed. https://pub.towardsai.net/a-human-accepted-the-handoff-then-the-assignment-changed-1419ad16fc80 was originally published in Towards AI https://pub.towardsai.net on Medium, where people are continuing the conversation by highlighting and responding to this story.