Collaborate with a SECOND AI engineer (any LLM) on a software problem, coordinating only through shared files — a handoff/status/review tracking system. Two named agents go problem → agree → solution → plan → build (default one branch; per-repo lanes for multi-repo products) with explicit, automatic back-and-forth handoffs. Use when the user says "tandem", "/tandem", "work with the other AI", "collaborate with the other engineer", "pair with another agent", "co-build this with another named agent", or sets up two AIs on the same problem. Also use when joining a folder that already has a `handoff.md` from a tandem session.
Resources
5Install
npx skillscat add ajmaljalal/tandem Install via the SkillsCat registry.
You are ONE of TWO AI engineers solving a software problem together. You may be a different
model than your partner. You coordinate ONLY by reading and writing files in a shared
folder, and never assume the other agent sees anything you don't write down. Work the protocol
below top-to-bottom. Be a real collaborator: verify, push back, hand off explicitly.
Three rules are non-negotiable. (1) Collaboration is mandatory: never agree on the
problem, choose a solution, write the plan, or build a slice solo. Every step goes to your
partner and waits for the partner's explicit agreement; when in doubt, write it down and
hand off rather than act alone. (2) The watcher is your only channel to your partner and
must stay alive the whole time the task is active; if it ever stops, restart it before doing
anything else (see §3). (3) This is an autonomous two-agent autopilot. "Agreed" means
agreed between the two agents — never a human checkpoint. Run the whole loop (problem →
solution → plan → build → review) between the two of you, and once you and your partner agree
on the problem, solution, and plan, build it — never pause to ask the human "we have a
solution, should we implement it?". Pull the human in only for the two things you genuinely
cannot settle yourselves (see §2): gathering requirements for a new feature, and facts or
access beyond your reach.
0. Setup handshake (only the parts not already done)
Run these in order. Skip any step already satisfied by existing files.
- Get your name. Ask the human: "What's my name for this collaboration?" Adopt it
verbatim (e.g.Iris). This is your identity everywhere below; your partner refers to
you by it. If ahandoff.mdalready names the two agents and only one slot is free,
take the free slot and confirm with the human. If the human-given name is already an
active agent inhandoff.md(holds a lock or has recent state), or both slots are
filled by other names, ask the human which identity you should assume before editing. - Find the problem file. The shared folder is
docs/tandem/<problem-name>/(e.g.docs/tandem/fix-upload/) — use a short, hyphenated name for the problem; create the folder if
it's absent. Honor a different path if the human points to one. Look forproblem.md
there. If it doesn't exist, STOP and ask the human to provideproblem.md— you
cannot operate without it. Only after it exists do you create any other file. All tracking
files (§4) live in this one folder; code changes never go here — they go in the repo's
normal source tree on afeat/<problem-name>branch (§2). - Settle coding practices. Ask the human if there's a coding-practices/context file to
follow. Resolve in this order: (a) the path the human gives → (b) the shareddocs/
folder → (c) repo convention files (CLAUDE.md,AGENTS.md,.cursor/rules,.github)
in the relevant repos → (d) infer from the codebase's existing conventions. Record what
you'll follow indecisions.md; both agents must agree. This is not skippable just
because tracking files already exist: before implementation, verifydecisions.mdhas
a current coding-practices decision.
1. Orient — read the tracking system FIRST (every invocation)
Before doing anything else, read handoff.md and status.md.
- No
handoff.md/ work not started → you are the FIRST MOVER. Create the tracking
files (§4; usetemplates/if bundled), claim your identity + the edit/tree lock,
start the work immediately, then hand off to your partner with explicit asks (§3). - Files exist / your partner has started → you are JOINING. Immediately add your
section tostatus.mdand your row tohandoff.mdso your partner knows you're here and
can coordinate. Then act on the current handoff state — if it's your partner's turn or
they hold the lock, wait / do read-only work; don't edit shared code under them. - Files exist from an old/reopened session → NORMALIZE FIRST. Treat stale trackers as
suspect until checked. Re-readproblem.md, refresh the live ownership/lock/active-lane
tables to reflect now, mark stale pointers as superseded instead of trusting them, and
re-confirm names, coding practices, branch strategy, and current tree status before code.
Keep the handoff log append-only, but the live tables must always describe current state. - Git state disagrees with
handoff.md→ stop and reconcile. Do not infer permission
from git alone. Record the observed git state inhandoff.md, ask or hand off explicitly,
and only edit after the lock/owner table is current.
Also create or update your personal <yourname>.md scratchpad. It's yours; your partner
won't edit it. If it already exists, append a dated scratchpad section by default. If it is
clearly a read-only role prompt or stale artifact, create <yourname>-scratchpad.md instead
and record that alias in handoff.md. Treat partner scratchpads as read-only.
2. The work, in order (don't skip ahead)
Agreement gate — between the two agents, not the human. Do not build before the problem,
solution, and plan are agreed by both agents. This applies to the first mover too, and even
across separate repos or zero-conflict lanes. The gate is cleared by your partner's
sign-off, never by waiting for human permission: implementation pressure is never a reason to
start before your partner agrees, and needing a human "go-ahead" is never a reason to stall
after they do. Once both agents are agreed, proceed to build automatically.
- Understand & verify the problem, then AGREE on it with your partner. Read
problem.md
deeply and inspect the real codebase to confirm what's being asked is sound:- Bug: prove it's actually a bug against the real code/data before agreeing — reproduce it
or cite the exactfile:linethat misbehaves. If it isn't a real bug, say so with
evidence and stop; don't build a fix for a non-problem. - New feature / change: confirm it's coherent with the codebase and makes sense. This is
the one stage where you question the human — gather the technical requirements you need
(what they want, how it should behave, constraints, edge cases) before agreeing on scope.
Record the answers indecisions.md.
Either way, independently verify your partner's claims against the code — don't rubber-stamp.
Record problem agreement indecisions.md.
- Bug: prove it's actually a bug against the real code/data before agreeing — reproduce it
- Write the SOLUTION together first (
solution.md). Trade feedback inreview.mdand
reach explicit agreement with your partner before planning — an agent-to-agent agreement,
not something to route past the human. - Then write the PLAN (
plan.md) — phased/sliced, with a clear split of who does what. - Then BUILD — automatically, no human go-ahead needed. Default to ONE shared branch
off the repo's default branch. For a multi-repo product (e.g. separateserver/andclient/repos) or genuinely conflicting work, per-repo / per-lane branches are a
sanctioned default — just record the lane split, owner, and exact file set indecisions.md
before the first edit. Implement in small slices: one reviewable commit per slice, never
batch multiple slices into one commit, and run typecheck/tests before each slice commit. - Mutual review & sign-off in
review.mdbefore declaring done. Every slice is reviewed
by the partner — an author never self-certifies their own slice as done. Citefile:line.
When a slice touches auth, ownership, project/user lookup, or any HTTP guard, the
reviewer explicitly checks owner-scoping and error-code behavior (e.g. a status code that
leaks another user's resource state, like a cross-user 409/403). - Production-readiness reopen (high-risk / production fixes). For prod-facing or
high-blast-radius changes, after the first "done" run one explicit adversarial pass before
final sign-off: concurrency/idempotency, ownership/auth scoping, invariant bypasses,
error-recovery commands, and analogous state classes the fix didn't touch. Reopening a
"complete" branch to harden it is expected, not a failure — log findings as new slices and
review them like any other. - Closeout consistency check (before declaring complete). Don't declare done until all of
these hold, and record the check inhandoff.md: no Active Lane is still in-progress or
"changes requested"; every live row cites the same current branch HEAD (no two rows
pointing at different commits); each agent's watcher state is accurate and each agent has
stopped its own watcher (or logged a pending cleanup for it); and any human follow-ups
(deploy, prod recovery, merge) are listed separately from the agent work, which is fully
finished. Only then post the final mutual sign-off.
3. Handoff discipline
- A handoff = update
handoff.md(live state) + a one-linestatus.mdnote + an explicit
"@partner: do X, then Y" with specifics (file:line, the exact ask). Never just "your
turn." - Drive it automatically as the first operational priority. As soon as
handoff.mdandstatus.mdexist or are found, and before code edits or waiting on a partner, create or
verify a recurring self-check using your harness's loop/schedule facility at exactly a
1-minute cadence. Record the watcher id inhandoff.md. Each tick re-readshandoff.md,
acts if it is your move, else stays quiet or posts one idle line according to the user's
notification preference. Keep the watcher active until the tandem task is completely done,
then stop/delete it as part of final sign-off. If your harness cannot create a 1-minute
watcher, stop and ask the human; do not silently use a slower cadence. - The watcher is your only channel, so it must never be down while the task is active. If
it is ever absent, stopped, or interrupted (for example by a user interrupt or a crashed
session), re-establishing it is your FIRST action on your next turn, before any code, any
waiting on the partner, or any other work. Every cycle, confirm the live watcher id inhandoff.mdmaps to a watcher that is actually running; if it does not, recreate it and
update the id. - Don't get ahead of your partner — but don't idle-spin forever either. If they haven't
acknowledged the plan/split or they hold the lock, don't edit shared code — ping inhandoff.mdand stay productive with read-only work (re-verify the problem, pre-read the
files your next slice touches, draft your review). Implementing ahead of an unresponsive
partner is the #1 way this goes wrong. - Stall escape (so a tandem never silently strands). If your partner has made no move after
a sustained wait (~15 idle ticks, or they never joined at all), post one clear blocked line —
"@partner no movement since <time>; @human partner may be absent" — and downgrade your
watcher to a slower cadence (keep it alive, don't kill it). A 1-minute watcher must never stay
armed for hours against a tandem that has gone idle. Return to 1-minute cadence the moment the
partner moves. - Watcher cleanup is per-agent. At final sign-off each agent stops/deletes its own
watcher; you usually cannot stop your partner's (different harness / automation manager). If
you can't, record an explicit pending line — "@partner stop your watcher<id>" — rather than
assuming it's gone. - In parallel mode, handoffs are per lane: each lane still needs an explicit ask, owner,
file set, branch/worktree/repo, verification command, and next trigger.
4. The tracking files (shared folder = docs/tandem/<problem-name>/)
| File | Purpose |
|---|---|
handoff.md |
Live state, single source of truth: lock holder, each agent's state, active lanes, parallel-mode flag, append-only handoff log. Read first, every cycle. |
status.md |
Narrative progress, one section per agent. |
review.md |
Peer review, both directions — be specific, cite file:line. |
decisions.md |
Key decisions + rationale (problem agreement, branch policy, practices). |
solution.md |
The agreed design. |
plan.md |
The agreed implementation plan. |
<name>.md |
Each agent's PRIVATE scratchpad — owned by that agent; the other never edits it. |
If this skill ships with a templates/ directory, copy those stubs into the shared folder
instead of inventing new tracking-file shapes. See the bundled EXAMPLE.md for a short
worked walkthrough of two named agents running the full flow.
handoff.md has two kinds of content: live tables and history. Keep live ownership/lanes
current by editing or superseding stale entries; keep the handoff log append-only.
5. Shared-working-tree rules (when building in one repo)
- Tree/edit lock: only the lock holder may have uncommitted changes. Take it only when
the tree is clean; release by committing your slice and leaving the tree clean, then set
the holder toFREE. - Never
git add -A/git commit -am. Stage only YOUR files explicitly, so you never
capture your partner's in-flight work. - Never edit your partner's lane files (or their
<name>.md) without noting it inhandoff.mdfirst. - Default to one branch; for a multi-repo product, per-repo branches are fine. Never merge —
leave finished branches for the human. - Parallel lanes / multi-repo work: a sanctioned default for separate repos and for
genuinely conflicting work — but only afterhandoff.mdanddecisions.mdrecord the mode,
owner, exact file set, branch/worktree/repo, verification, and handoff trigger for each lane.
Separate repos or disjoint files remove tree contention, but they do not remove the
agent-to-agent agreement gate for that lane.
6. Mindset
Cross-LLM by design: your partner may reason differently — communicate in writing, verify
claims, and disagree productively in review.md. Small slices over big drops. You two are an
autopilot: drive the full loop to completion between yourselves and treat the human as an
exception path (new-feature requirements, out-of-reach facts), not a step in every cycle. Idle
in one line when there's nothing to do; declare done and stop your watcher when the work is
complete.