# A Retry-Safe Handoff Contract for Long AI Chats

> Source: <https://dev.to/lamama/a-retry-safe-handoff-contract-for-long-ai-chats-28pe>
> Published: 2026-10-25 20:11:49+00:00

Long AI-assisted tasks often span more than one interaction. A response may be interrupted, a user may need to start a new conversation, or a tool call may finish while the interface is still waiting. The fragile point is not always the summary itself. It is the *transition*: what happens if a handoff is sent twice, acknowledged late, or resumed from incomplete context?

A useful design starts by treating a handoff as a small protocol, not as an unstructured paragraph that says “continue where we left off.”

A handoff packet should describe only the state needed to resume responsibly. For example:

```
{
  "schema_version": 1,
  "conversation_id": "example-session",
  "revision": 17,
  "goal": "Complete the deployment review",
  "confirmed_facts": ["Tests passed locally"],
  "decisions": ["No production change without approval"],
  "open_items": [{"id": "T-12", "task": "Verify the live service"}],
  "unknowns": ["Whether the new version is serving traffic"],
  "last_completed_step": "Recorded the dry-run results"
}
```

The distinction between `confirmed_facts` and `unknowns` matters. A newly opened conversation should not silently promote a guess into a fact. Likewise, decisions and restrictions should survive even if older conversational details do not.

Avoid putting passwords, API keys, session cookies, personal identifiers, or confidential source material into the packet. A handoff is still data being copied into another context; minimization and redaction should happen *before* transfer.

Retries are normal in distributed workflows. Suppose a sender transfers a packet, receives no acknowledgment, and retries. Without a stable ID, the receiver may treat the retry as a new instruction.

A simple demonstration uses a content-derived ID:

``` js
import { createHash } from 'node:crypto';

function makePacket(state) {
  const payload = JSON.stringify(state);
  const id = createHash('sha256').update(payload).digest('hex');
  return { id, payload };
}

function acceptPacket(packet, receiptStore) {
  if (receiptStore.has(packet.id)) {
    return { duplicate: true, receipt: receiptStore.get(packet.id) };
  }
  const receipt = { id: packet.id, accepted: true };
  receiptStore.set(packet.id, receipt);
  return { duplicate: false, receipt };
}

const state = { conversation_id: 'example-session', revision: 17 };
const packet = makePacket(state);
const receipts = new Map();
console.log(acceptPacket(packet, receipts).duplicate); // false
console.log(acceptPacket(packet, receipts).duplicate); // true
```

This is an **in-memory illustration**, not production-grade exactly-once delivery. A real system needs durable storage, a unique constraint on the handoff ID, and an atomic relationship between recording the receipt and applying any consequential operation. Cross-language systems also need canonical serialization before hashing, because equivalent JSON objects can have different byte representations. A hash is an identifier here, not an authorization token.

A handoff can move through a few named states:

| State | Meaning | 
|---|---|
| `PREPARED` | A packet exists, but the destination has not confirmed receipt. | 
| `ACKNOWLEDGED` | The destination accepted a specific packet ID. | 
| `RESUMED` | Work continued from that acknowledged packet. | 
| `CANCELLED` | The user or system stopped the transition. | 

A timeout after `PREPARED` is not evidence of failure; the receiver might have accepted the packet before the acknowledgement was lost. The safe retry uses the **same packet ID** and first asks the receiver whether it has already been accepted.

A separate state transition should be required before any action with external effects: sending email, updating a database, deploying software, or publishing content. Resuming context is not permission to perform those actions.

Before continuing, the destination should answer five questions:

If the answer to an important question is unknown, the continuation should say so. “Needs verification” is a valid state; invented certainty is not.

At minimum, test duplicate delivery, dropped acknowledgments, stale revisions, malformed packets, missing restrictions, cancellation, and re-opening a conversation after the sender is gone. It is also worth checking that no hidden background action starts solely because a handoff arrives.

For browser-based tools, the user should be able to pause, inspect, copy a backup, and stop a transition. Visibility is not a substitute for permissions, but it makes failures easier to detect and recover from.

A summary helps another conversation *understand* the work. A handoff protocol helps it *resume safely*. The difference is an explicit record of what is known, what is not known, what has already happened, and which steps still require authorization.

**AI disclosure:** This article was drafted with generative AI. It must be checked and substantially reviewed by a human who understands the technical material before publication. It is not a report of firsthand testing or production experience.
