DEV Community

Kate Johnson
Kate Johnson

Posted on

A small handoff contract for multiple coding agents

Two capable coding agents can still waste an afternoon. Both inspect the same bug, both edit the
same file, and each assumes the other will run the final test. The code may be sound. The missing
piece is coordination.

I use a small handoff contract whenever more than one agent shares a repository or an external
account. It gives each agent enough state to continue the work without replaying the full session.

Give each agent a bounded objective

An assignment should name one result and one ownership boundary.

objective: Add request validation to the webhook endpoint
owns:
  - src/webhooks/webhook.controller.ts
  - src/webhooks/webhook.schema.ts
does_not_own:
  - database migrations
  - deployment configuration
Enter fullscreen mode Exit fullscreen mode

The does_not_own list helps when two tasks sit close together. An agent working on validation may
notice a schema issue, but it should record that issue instead of editing a migration owned by
another task.

File ownership alone may be too narrow. One agent can own the API contract while another owns the
consumer, even if both need to read the same files. Write access needs a clear owner. Read access
does not.

Record state as a small state machine

Free-form status notes become hard to compare. A short set of states gives the coordinator a
stable signal:

  • reading: gathering evidence, no edits yet
  • editing: changing owned files
  • verifying: running tests or checking the live flow
  • waiting: blocked on approval or an external system
  • done: objective met and evidence recorded

Store one timestamp with the state. The timestamp distinguishes a live task from an abandoned one
without guessing from a long transcript.

{
  "state": "verifying",
  "updatedAt": "2026-08-25T14:20:00Z"
}
Enter fullscreen mode Exit fullscreen mode

Separate observations from mutations

Agents should log facts they read and actions they took in different fields.

observed:
  - POST /webhooks accepts an empty event type
changed:
  - Added an enum check for event type
  - Added a 400 response test
Enter fullscreen mode Exit fullscreen mode

That split prevents a coordinator from treating a proposed fix as completed work. It also makes
external actions easier to audit. A draft message, a sent message, and an approved message are
three different states.

For actions that spend money, publish content, deploy code, or contact another person, record the
approval gate next to the action:

external_action:
  destination: production deployment
  status: waiting_for_approval
  prepared: true
Enter fullscreen mode Exit fullscreen mode

Attach evidence to the result

Tests pass is too vague for a handoff. Record the command, result, and scope.

verification:
  - command: pnpm test webhook.controller.spec.ts
    result: 8 passed
  - command: pnpm lint src/webhooks
    result: passed
Enter fullscreen mode Exit fullscreen mode

Live checks need the same treatment. Name the route, account, environment, or browser flow that
you checked. Another agent should know whether the result came from a unit test, a local service,
or the production interface.

Evidence also prevents duplicate verification. The next agent can trust a fresh, scoped test and
focus on the missing layer.

Use cursors for monitoring

Reading the full transcript on each poll costs time and hides new events inside old output. Store
the last line, event ID, or timestamp that the coordinator consumed.

monitor:
  session_id: 7d4c...
  last_line: 1842
Enter fullscreen mode Exit fullscreen mode

The next poll starts at line 1843. If the agent changes state, submits an external action, or
finishes verification, the coordinator reports that change and advances the cursor.

This pattern works outside coding sessions too. Proposal queues, support inboxes, and deployment
logs all benefit from a durable boundary between reviewed and new items.

Define the final handoff

A completed task should leave five fields:

result: Empty webhook event types now return 400
files_changed:
  - src/webhooks/webhook.controller.ts
  - src/webhooks/webhook.schema.ts
verification:
  - unit tests: 8 passed
open_risks:
  - Existing clients may send the legacy "unknown" event type
next_action:
  - Confirm legacy client behavior before deployment
Enter fullscreen mode Exit fullscreen mode

The next_action field should contain work that remains. Leave it empty when the task is complete.
That rule keeps agents from inventing a follow-up to avoid saying they are done.

Handle stale ownership

An agent can crash after claiming a task. Add a lease or stale threshold to each ownership record.
The coordinator may reassign the work after the threshold, but it should inspect the worktree and
handoff first. Uncommitted edits may still contain useful work.

Shared repositories need one more rule: agents preserve changes they do not own. They should not
reset a branch, remove a worktree, or revert an unfamiliar edit to make their task easier.

A compact contract is enough

The contract does not need a new service. A JSON file, a database row, or a session handoff can
carry the same fields:

  • objective and ownership
  • state and timestamp
  • observations and mutations
  • approval gates
  • verification evidence
  • monitor cursor
  • open risks and next action

These fields turn parallel agents into a coordinated team. Each agent can read the current state,
claim a bounded slice, leave proof, and stop without colliding with the next one.

Top comments (0)