Agent Handoff Protocols

How agent A signals completion to agent B: issue status transitions as signals, metadata JSONB as a message payload, and the activity_log as a shared event bus for handoffs.

Handoff Protocols Between Agents

What you'll learn

Design clean handoff interfaces between agents, standardise the data format that passes from one agent to the next, and handle failed handoffs.

The Handoff Problem

When Agent A finishes work and Agent B continues from where A left off, B needs A's output in a predictable format. If A writes free-form prose, B has to parse it — fragile and error-prone.

The solution: define a handoff schema for each agent's output.

Defining a Handoff Schema

Add to the agent's system prompt:

## Output Format
You MUST return a JSON object with this exact schema. No prose, no markdown fencing — raw JSON only.

{
  "summary": "string (2-3 sentences)",
  "key_facts": ["string", ...],  // 3-5 items
  "sources": [{"title": "string", "url": "string"}],  // 1-5 items
  "confidence": "high" | "medium" | "low",
  "next_steps": ["string", ...]  // optional
}

Paperclip stores this in issues.result_structured (JSONB) when the result is valid JSON:

// Heartbeat post-processes the result
if (isValidJson(result.output)) {
  await db.issues.update(issueId, {
    result: result.output,
    result_structured: JSON.parse(result.output),
  });
}

Consuming the Handoff in the Next Agent

When creating the writer's issue, extract fields from the researcher's result_structured:

-- Writer issue description built from researcher's structured output
INSERT INTO issues (company_id, title, description, assigned_agent_id, parent_issue_id)
SELECT
  i.company_id,
  'Write article from research: ' || topic,
  'Summary: ' || (i.result_structured->>'summary') || E'\n\n' ||
  'Key facts: ' || (i.result_structured->'key_facts')::text || E'\n\n' ||
  'Write a 700-word article covering the key facts above.',
  writer_id,
  i.parent_issue_id
FROM issues i
WHERE i.title = 'Research: ' || topic
  AND i.status = 'resolved';

Validating Handoff Data

Add a validation step between agents:

function validateResearchHandoff(result: string): ResearchOutput {
  const data = JSON.parse(result);
  if (!data.summary || !Array.isArray(data.key_facts)) {
    throw new Error('Invalid research handoff: missing required fields');
  }
  return data as ResearchOutput;
}

If validation fails, the issue is marked escalated and a human reviews the raw output before the next agent proceeds.

Handoff via Issue Metadata

For structured data that doesn't need to be in the description prose:

UPDATE issues
SET metadata = jsonb_set(
  COALESCE(metadata, '{}'),
  '{handoff}',
  result_structured
)
WHERE id = 'researcher-issue-id';

The next agent's issue can reference metadata->'handoff' directly.

Checkpoint ✓

Sign in for progress tracking & Q&A →