Repair an Event Pipeline with Teams

Coordinate Claude Code agents to repair a broken order-event contract.

Introduction

30 Second Summary

When different people own different parts of a system, each part can look correct on its own. The real risk appears where their assumptions meet.

In this project, you will use Claude Code agent teams to rescue a broken order-event pipeline. Your team will replace conflicting money-unit assumptions with one owned contract that makes every core test pass.

What You'll Build

Picture demonstrating an event pipeline that charges exactly 2500 cents after four named teammates coordinate their work through one canonical contract.

By the end of this project, you'll have:

  • A deterministic release gate that proves passing component tests can still hide a 250000-cent integration failure.
  • A live agent team with named ownership that you can inspect through its shared task list.
  • A repaired event pipeline that charges exactly 2500 cents after direct handoffs establish totalCents as the canonical field.
  • Secret Mission: Add a backward-compatible currency field that defaults legacy events to credits while preserving explicit values such as tokens.

Are there any prerequisites?

You need Claude Code 2.1.178 or later plus Node.js 20.0.0 or later on macOS. Git and Terminal must also be available.

This project assumes prior experience with Claude Code subagents and parallel Git worktrees.

Before We Start

This checkpoint locks in the coordination failure you are about to investigate before any hands-on work begins. Your answer will frame the evidence you capture across the broken event pipeline.

Set Up the Team Workspace

Agent teams in Claude Code are experimental. The feature starts disabled.

You will verify the required tool versions. The result is a Git-backed interactive session with agent teams enabled.

In this step, get ready to:
  • Verify the required tool versions.
  • Enable agent teams in your user settings.
  • Launch a Git-backed Claude Code workspace.
Verify Claude Code and Node.js

This project requires Claude Code 2.1.178 or later. It also requires Node.js 20.0.0 or later.

  • Press Cmd+Space to open Spotlight on your Mac.
  • Type Terminal.
  • Press Enter to open Terminal.
  • Check the Claude Code version and installation diagnostics by running:
claude --version
claude doctor

What do these commands check?

  • The first command prints the installed Claude Code version.
  • The second command checks the installation plus its settings without changing either one.

✔️ I see version 2.1.178 or later

Your Claude Code installation meets the project requirement. The diagnostics should also report a valid installation.

ⓧ I see an older version

The diagnostics identify how Claude Code was installed. Choose the matching update method below.

Native installation

  • Update the native installation by running:
claude update
What does this command do?

This command applies the available Claude Code update to a native installation.

  • Verify the updated version by running:
claude --version
What should this print?

The command prints the version from the updated Claude Code installation.

Homebrew claude-code

  • Update the claude-code cask by running:
brew upgrade claude-code
What does this command do?

Homebrew upgrades the installed claude-code cask.

  • Verify the updated version by running:
claude --version
What should this print?

The command prints the version from the upgraded Homebrew installation.

Homebrew claude-code@latest

  • Update the claude-code@latest cask by running:
brew upgrade claude-code@latest
What does this command do?

Homebrew upgrades the installed claude-code@latest cask.

  • Verify the updated version by running:
claude --version
What should this print?

The command prints the version from the upgraded Homebrew installation.

ⓧ Command not found

Claude Code needs to be installed before this workspace can launch an interactive session.

  • Follow the official Claude Code setup guide to install Claude Code.
  • Return to Terminal after the installation completes.
  • Verify the installed version by running:
claude --version

What should this print?

The command prints the version from the new Claude Code installation.

Claude Code check still failing?

Confirm that you used the update method reported by the diagnostics. A Homebrew installation needs its matching Homebrew cask command.

Still stuck? Help me diagnose my Claude Code installation.

The project tests use the built-in Node.js test runner. Version 20.0.0 is the minimum baseline for this project.

  • Check your Node.js version by running:
node --version

What does this command do?

The command prints the Node.js version available in this Terminal session.

✔️ I see version 20.0.0 or later

Your Node.js version supports the test runner used later in this project.

ⓧ I see an older version

Your current Node.js version is below the project baseline. The current LTS release is v24.20.0 LTS.

  • Visit the official Node.js download page.
  • Download the v24.20.0 LTS installer for macOS.
  • Run the downloaded installer.
  • Quit Terminal after the installer finishes.
  • Press Cmd+Space to reopen Spotlight.
  • Type Terminal.
  • Press Enter to reopen Terminal.
  • Verify the updated version by running:
node --version

What should this print?

The command prints the Node.js version provided by the new installation.

ⓧ Command not found

Node.js is not available in your current Terminal environment. The current LTS release is v24.20.0 LTS.

  • Visit the official Node.js download page.
  • Download the v24.20.0 LTS installer for macOS.
  • Run the downloaded installer.
  • Quit Terminal after the installer finishes.
  • Press Cmd+Space to reopen Spotlight.
  • Type Terminal.
  • Press Enter to reopen Terminal.
  • Verify the installation by running:
node --version

What should this print?

The command prints the Node.js version provided by the new installation.

Node.js version still unchanged?

Quit every open Terminal window after the installer completes. A newly opened Terminal session reloads the command path.

Need help? Help me fix my Node.js version check.

Enable agent teams

Claude Code reads user settings from ~/.claude/settings.json. This JSON file can hold several unrelated settings, so the existing content must remain intact.

  • Open ~/.claude/settings.json in the terminal editor by running:
nano ~/.claude/settings.json

What does this command do?

The command opens the Claude Code user settings file in nano. Nano creates the file when it does not already exist.

  • Inspect the existing top-level settings before making a change.
  • Add the env object below if the file is empty.
  • Merge the env object into the existing top-level object if other settings are present.
  • Add CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS inside the existing env object if that object already exists.
  • Leave teammateMode unset so Claude Code uses the default in-process mode.
  • Use this exact setting:
{
  "env": {
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
  }
}

What does this setting do?

The env object gives Claude Code the feature flag when a session starts. The value 1 enables experimental agent teams.

Existing top-level settings stay alongside this object. Existing properties inside env stay alongside the new property.

  • Press Ctrl+O to write the updated settings to disk.
  • Press Enter to confirm the filename.
  • Press Ctrl+X to exit nano.
  • Check the saved installation plus its settings by running:
claude doctor

What does this check prove?

The diagnostics read the saved Claude Code settings without changing them. A successful result confirms that the installation can parse the updated file.

That is the setup hurdle cleared. Claude Code can now read the agent-team setting without losing your unrelated preferences.

Settings diagnostics failing?

  • Check that every opening brace in ~/.claude/settings.json has a matching closing brace.
  • Check that sibling JSON properties are separated by commas.
  • Confirm that the value 1 remains inside double quotes.

Still blocked? Help me repair my Claude Code settings file.

✔️ Awesome, I've got everything!

Your saved settings contain the agent-team environment entry. Any unrelated settings remain in the same file.

ⓧ I'd like to double check the full code

Your full file can contain unrelated settings. Its required env section must contain this exact entry.

{
  "env": {
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
  }
}

How should I compare this?

Compare the environment key plus its quoted value. Keep every unrelated property already present in your file.

Create and launch the workspace

The team needs one shared working directory. Initializing that directory as a repository gives every later owner the same project boundary.

  • Move to your Desktop by running:
cd ~/Desktop

What does this command do?

The command changes Terminal's current directory to your Desktop.

  • Create the contract-collision folder and move into it by running:
mkdir contract-collision
cd contract-collision

What do these commands do?

The first command creates the local project folder. The second command makes that folder Terminal's current directory.

  • Confirm Terminal is inside the new folder by running:
pwd

What does this command show?

The command prints the full path of Terminal's current directory.

You should see a path ending in Desktop/contract-collision.

  • Run the final installation plus settings diagnostic from the workspace:
claude doctor

What does this verify?

The diagnostic confirms that Claude Code remains valid from inside the new workspace. It also confirms that the saved settings file can be read.

You should see diagnostics for a valid Claude Code installation plus its settings file.

  • Initialize the contract-collision repository by running:
git init

What does this command do?

Git creates an empty repository inside contract-collision. The repository metadata lives in its hidden .git directory.

Git reports that it initialized a repository inside contract-collision.

A browser sign-in can appear if this Claude Code installation has not authenticated yet. Complete that sign-in with the account connected to your existing Claude Code access.

Before you run this, do you expect a regular shell prompt or an interactive Claude Code prompt?

  • Start the interactive session from the repository by running:
claude

What happens when Claude Code starts?

Claude Code opens its interactive prompt in the current repository. Agent teams can only spawn from an interactive session.

The default in-process teammate mode works inside the current terminal. No tmux or iTerm2 setup is required.

You will see the interactive Claude Code prompt inside contract-collision. The session is now ready for team instructions.

Interactive prompt did not open?

  • Confirm that the earlier version check reported Claude Code 2.1.178 or later.
  • Confirm that the current path ends with Desktop/contract-collision.
  • Complete account authentication if Claude Code requests it.

Need a hand? Help me open the interactive Claude Code session.

Your repository is ready. Next, you will build two components whose passing local tests hide a broken shared contract.

Build Locally Correct Components

Your repository is ready. The Claude Code lead now needs a fixture where each component can prove itself against a private assumption.

The producer counts total in integer cents. The consumer reads the same field as whole dollars.

Each Node.js component test will pass against its own local specification. That visible success creates the false confidence this exercise needs.

In this step, get ready to:
  • Create the module configuration plus both legacy specifications.
  • Build each component under its own local assumption.
  • Prove the component suite has two passing tests.
Create the fixture foundations

The two local specifications deliberately disagree about units. Their status lines preserve those assumptions as evidence for the coordination work ahead.

  • Send the following prompt to the Claude Code lead:
Create these files with exactly the content below. Preserve every blank line.

package.json
{
  "name": "contract-collision",
  "private": true,
  "type": "module"
}

specs/producer-spec.md
# Producer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the producer assumed that an `order.created` event used a field named `total` containing integer cents.

specs/consumer-spec.md
# Consumer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the consumer assumed that an `order.created` event used a field named `total` containing whole dollars, which it multiplied by 100.

After creating them, report the three paths. Do not create other files.

What Does This Fixture Establish?

  • The type value in package.json tells Node.js to interpret .js files as ECMAScript modules.
  • The producer specification defines total as integer cents.
  • The consumer specification defines total as whole dollars.
  • Approve the three listed file edits if Claude Code requests permission.

Claude Code should report package.json plus both files inside specs/. The canonical contract path appears only inside the legacy status lines.

Missing a Fixture File?

  • Ask the lead to compare its reported paths with the three paths in the prompt.
  • Ask the lead to preserve the wording exactly if it summarizes either specification.

Still missing a file? Help me correct the fixture without creating extra files.

Build the producer slice

The producer test treats 2500 as integer cents. Its narrow test proves that the producer preserves that local meaning.

  • Send the producer fixture to the Claude Code lead with this prompt:
Create these two files with exactly the content below. Preserve every blank line.

src/producer.js
export function createOrderEvent(orderId, total) {
  return {
    type: 'order.created',
    orderId,
    total,
  };
}

test/producer.test.js
import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';

test('producer emits integer cents in its local contract', () => {
  assert.deepStrictEqual(createOrderEvent('order-123', 2500), {
    type: 'order.created',
    orderId: 'order-123',
    total: 2500,
  });
});

After writing both files, run node --test.
Report the test count and failure count.
Do not create an integration test.

What Does the Producer Prove?

  • The createOrderEvent() function places the supplied amount in total without conversion.
  • The producer test expects 2500 to remain 2500 inside the event.
  • The test checks only the producer's local assumption.
  • Approve the edits to src/producer.js if Claude Code requests permission.
  • Approve the edit to test/producer.test.js if Claude Code requests permission.

The lead should report one passing test with zero failures. Your first locally correct component now has objective test evidence.

Producer Test Did Not Pass?

  • Check that src/producer.js returns the field named total.
  • Check that the test imports ../src/producer.js with the file extension included.

Need help with the failure? Help me compare the producer files with the fixture.

Build the consumer and verify the suite

The consumer's private assumption completes the collision. It receives 25 whole dollars in its local test.

The consumer multiplies that value by 100. Its expected result is therefore 2500 charged cents.

  • Send the consumer fixture to the Claude Code lead with this prompt:
Create these two files with exactly the content below. Preserve every blank line.

src/consumer.js
export function chargeOrder(event) {
  return {
    orderId: event.orderId,
    chargedCents: event.total * 100,
  };
}

test/consumer.test.js
import test from 'node:test';
import assert from 'node:assert/strict';

import { chargeOrder } from '../src/consumer.js';

test('consumer converts whole dollars in its local contract', () => {
  assert.deepStrictEqual(
    chargeOrder({
      type: 'order.created',
      orderId: 'order-123',
      total: 25,
    }),
    {
      orderId: 'order-123',
      chargedCents: 2500,
    },
  );
});

What Does the Consumer Prove?

  • The chargeOrder() function assumes event.total contains whole dollars.
  • Multiplying by 100 converts the test input into integer cents.
  • The consumer test validates only its own local specification.
  • Approve the edit to src/consumer.js if Claude Code requests permission.
  • Approve the edit to test/consumer.test.js if Claude Code requests permission.

Before the full run, do you expect the shared field name to reveal the unit disagreement while the tests remain separate?

  • Run the complete component suite by sending this prompt to the Claude Code lead:
Run node --test. Do not edit any files. Report the test count and failure count.

What Does This Command Check?

The node --test command discovers both *.test.js files. Each test supplies input that matches its component's private unit assumption.

You should see both component tests pass. The summary reports two passing tests with zero failures.

Well done. You now have two green component tests built on incompatible assumptions.

Suite Does Not Show Two Passing Tests?

  • Check that both test filenames end with .test.js.
  • Check that package.json contains the module value for type.
  • Check that the producer test uses 2500 while the consumer test uses 25.

Still seeing a failure? Help me diagnose the component suite without adding an integration test.

At this checkpoint, test/integration.test.js remains absent. contracts/order-event.md remains absent too.

✔️ Awesome, I've got everything!

Your seven fixture files match the locally correct state.

ⓧ I'd like to double check the full code

  • Compare each file with the complete fixture below.
{
  "name": "contract-collision",
  "private": true,
  "type": "module"
}
# Producer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the producer assumed that an `order.created` event used a field named `total` containing integer cents.
# Consumer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the consumer assumed that an `order.created` event used a field named `total` containing whole dollars, which it multiplied by 100.
export function createOrderEvent(orderId, total) {
  return {
    type: 'order.created',
    orderId,
    total,
  };
}
export function chargeOrder(event) {
  return {
    orderId: event.orderId,
    chargedCents: event.total * 100,
  };
}
import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';

test('producer emits integer cents in its local contract', () => {
  assert.deepStrictEqual(createOrderEvent('order-123', 2500), {
    type: 'order.created',
    orderId: 'order-123',
    total: 2500,
  });
});
import test from 'node:test';
import assert from 'node:assert/strict';

import { chargeOrder } from '../src/consumer.js';

test('consumer converts whole dollars in its local contract', () => {
  assert.deepStrictEqual(
    chargeOrder({
      type: 'order.created',
      orderId: 'order-123',
      total: 25,
    }),
    {
      orderId: 'order-123',
      chargedCents: 2500,
    },
  );
});

Both local tests are green under separate assumptions. Next, a dependency-gated release task combines them. That gate exposes the missing contract ownership.

Expose the Naive Release Failure

Your producer and consumer now look healthy in isolation. Their passing tests create the confidence this release gate is designed to challenge.

File-level ownership leaves the shared event contract unowned. In this step, a Claude Code agent team will validate each component before an integration owner exposes the collision.

In this step, get ready to:
  • Create a diagnostic team with three named owners.
  • Build a shared task graph that blocks integration behind both component checks.
  • Capture the failing integration evidence without applying a fix.
Spawn the diagnostic team

Each teammate has its own context. Predictable names make later assignments easier to inspect.

  • Ask the lead to spawn exactly three teammates in in-process mode named producer-owner, consumer-owner, and integration-owner.
  • Tell the lead that this team is diagnostic only.

Good. Your lead now has three owners whose work can be traced separately.

Why Use Agent Teams Here?

Agent teams expose coordination through a shared task list and direct messages. That makes ownership gaps visible while work is still moving.

Focused subagents return results to their caller. This exercise needs peers that coordinate around dependencies.

All three teammates share the same project directory. An explicit ownership map protects the existing fixture from accidental edits.

Ownership Boundaries

  • The producer-owner may read only specs/producer-spec.md, src/producer.js, and test/producer.test.js.
  • The consumer-owner may read only specs/consumer-spec.md, src/consumer.js, and test/consumer.test.js.
  • The integration-owner owns only test/integration.test.js once the release task becomes available.
  • No teammate may repair the contract during this diagnostic pass.
  • Ask the lead to enforce this ownership map for the diagnostic team.
Gate release behind local validation

A dependency gate keeps downstream work blocked until its prerequisites are complete. The shared task list makes that sequencing visible.

Build This Task Graph

  • The Validate producer locally task belongs to producer-owner. It validates only the assigned producer specification, source file, and test file.
  • The Validate consumer locally task belongs to consumer-owner. It validates only the assigned consumer specification, source file, and test file.
  • The Run release integration task belongs to integration-owner. It depends on both validation tasks.
  • Each validation owner marks its task completed only after its local test passes.
  • Ask the lead to create this three-task graph in the shared task list.

Before you inspect the graph, predict whether the release task can start while either validation remains unfinished.

  • Press Ctrl+T to inspect the shared task list.

You should see both validation tasks available. The release integration task remains blocked behind them.

  • Press Ctrl+T to return to the lead conversation.
  • Ask the lead to start both component validation tasks.

Each owner should report a passing local test without changing any file. Both validation tasks should then show completed.

Before you inspect the graph again, predict what happens to the release task when both prerequisites are complete.

  • Press Ctrl+T to inspect the updated task graph.

You should see the release integration task unblocked automatically. The graph has now released the first system-level check.

Capture the contract collision

The integration test feeds the producer's real output directly into the consumer. This is the first check that asks whether both local contracts agree.

  • Use the up and down arrows to select integration-owner.
  • Press Enter to view the teammate transcript.
  • Tell integration-owner to claim the unblocked Run release integration task.
  • Give integration-owner sole ownership of test/integration.test.js.
  • Ask integration-owner to create test/integration.test.js with this exact test:
import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';
import { chargeOrder } from '../src/consumer.js';

test('producer output is valid consumer input', () => {
  const event = createOrderEvent('order-123', 2500);
  const charge = chargeOrder(event);

  assert.deepStrictEqual(charge, {
    orderId: 'order-123',
    chargedCents: 2500,
  });
});

What Does This Test Prove?

  • The test calls createOrderEvent with an amount of 2500.
  • It passes the returned event directly into chargeOrder.
  • The strict assertion expects the final charge to remain 2500 cents.
  • This path tests the boundary that both component tests skip.

✔️ Awesome, I've got everything!

Your integration file matches the diagnostic release fixture.

ⓧ I'd like to double check the full code

import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';
import { chargeOrder } from '../src/consumer.js';

test('producer output is valid consumer input', () => {
  const event = createOrderEvent('order-123', 2500);
  const charge = chargeOrder(event);

  assert.deepStrictEqual(charge, {
    orderId: 'order-123',
    chargedCents: 2500,
  });
});
  • Tell integration-owner to save test/integration.test.js.

Before the release owner runs the suite, predict whether two passing component tests guarantee a passing integration test.

  • Ask integration-owner to run the complete test suite with this command:
node --test

This Failure Is the Evidence

The producer test passes. The consumer test also passes.

The integration path sends 2500 cents into a conversion that multiplies the value by 100. The assertion receives chargedCents as 250000 instead of 2500.

Seeing a Different Result?

  • Check that src/producer.js still emits the field total.
  • Check that src/consumer.js still calculates chargedCents: event.total * 100.
  • Check that the new test is saved at test/integration.test.js.

Need help? Help me compare my release output with the expected contract collision.

  • Require integration-owner to message the lead that both component tests passed while the integration assertion received 250000 instead of 2500.
  • Keep the Run release integration task in progress as failed release evidence.
  • Preserve the integration-owner transcript for the replan.

You now have objective proof that local correctness can still produce a broken system. Next up, you'll replan around a dedicated contract owner.

Define the Canonical Contract

Your Claude Code agent team has exposed the exact failure: both component tests pass while the integration gate receives 250000 charged cents instead of 2500.

The missing unit of work is contract ownership. In this step, you will add one owner for the decision before either component owner can begin its migration.

In this step, get ready to:
  • Add a fourth teammate with sole ownership of the canonical contract.
  • Build a dependency graph that pauses both migrations behind the contract decision.
  • Capture direct handoff acknowledgements before releasing implementation work.
Rebuild the task graph around contract ownership

A shared task graph turns the contract decision into a dependency gate. Both component owners must wait for one authoritative field name and unit.

  • Return to the Claude Code lead session from the failed release gate.
  • Tell the lead to retain the failed release task as evidence.
  • Tell the lead to spawn a fourth teammate named contract-owner.
  • Press Ctrl+T to confirm contract-owner appears alongside the existing three teammates.

Good, the missing ownership boundary now has a named teammate.

  • Tell the lead to create a new task named canonical-contract.
  • Tell the lead to assign canonical-contract exclusively to contract-owner.
  • Press Ctrl+T to confirm the new task has one owner.

The new task establishes who makes the decision. The next dependencies establish when implementation may begin.

  • Tell the lead to create the producer migration task with canonical-contract as its dependency.
  • Tell the lead to create the consumer migration task with canonical-contract as its dependency.
  • Press Ctrl+T to confirm both migration tasks remain blocked behind the contract task.
  • Tell the lead to create the final release task with both migration tasks as its dependencies.
  • Press Ctrl+T to confirm the final release task remains blocked behind both migrations.
Give the contract one owner

The canonical document gives every owner the same event shape and unit. The existing local specifications remain collision evidence.

  • Tell contract-owner to leave specs/producer-spec.md unchanged.
  • Tell contract-owner to leave specs/consumer-spec.md unchanged.
  • Give contract-owner the exact contents for contracts/order-event.md by pasting this reference into the lead session:
# Order Event Contract

Status: Canonical
Owner: contract-owner

An `order.created` event has this shape:

    {
      "type": "order.created",
      "orderId": "order-123",
      "totalCents": 2500
    }

Rules:

- `type` is `order.created`.
- `orderId` identifies the order.
- `totalCents` is the integer number of cents charged for the order.
- Producers emit `totalCents` and consumers read `totalCents` without conversion.

What does this contract decide?

  • The Canonical status establishes this file as the authoritative contract.
  • The contract-owner owner records who controls future contract decisions.
  • The event shape replaces the ambiguous total field with totalCents.
  • The rules define the value as integer cents with no consumer-side conversion.
  • The legacy specifications stay unchanged so the original collision remains visible.
  • Tell contract-owner to save contracts/order-event.md.
  • Use the up and down arrow keys to select contract-owner.
  • Press Enter to view its transcript.

You should see confirmation that contracts/order-event.md was created. You should also see that both legacy specification files were left unchanged.

Contract file does not match?

  • Check that only contract-owner wrote contracts/order-event.md.
  • Tell contract-owner to restore either legacy specification if it changed.
  • Compare the field spelling carefully because totalCents is case-sensitive.

Still stuck? Help me verify the canonical contract and restore the legacy specifications.

✔️ Awesome, I've got everything!

The canonical contract is saved with one owner and one unit definition.

ⓧ I'd like to double check the full code

# Order Event Contract

Status: Canonical
Owner: contract-owner

An `order.created` event has this shape:

    {
      "type": "order.created",
      "orderId": "order-123",
      "totalCents": 2500
    }

Rules:

- `type` is `order.created`.
- `orderId` identifies the order.
- `totalCents` is the integer number of cents charged for the order.
- Producers emit `totalCents` and consumers read `totalCents` without conversion.
Complete handoffs before releasing migrations

A saved contract becomes actionable when each affected owner acknowledges the exact decision. Their replies prove that both migrations start from the same field and unit.

  • Return to the lead transcript.
  • Tell contract-owner to message producer-owner that the canonical field is totalCents represented as integer cents.
  • Tell contract-owner to request producer-owner's intended file changes.
  • Tell contract-owner to message consumer-owner with the same field and unit decision.
  • Tell contract-owner to request consumer-owner's intended file changes.
  • Use the up and down arrow keys to select producer-owner.
  • Press Enter to view its reply.

The producer reply should name src/producer.js and test/producer.test.js. It should describe replacing total with totalCents.

  • Use the up and down arrow keys to select consumer-owner.
  • Press Enter to view its reply.

The consumer reply should name src/consumer.js and test/consumer.test.js. It should describe reading totalCents without multiplying by 100.

  • Tell the lead to mark canonical-contract as completed only after both replies are visible.

Before you inspect the task list, which tasks do you expect to be released by the completed contract dependency?

  • Press Ctrl+T to inspect the final task graph.

You should see canonical-contract as completed. Both migration tasks should show the contract dependency as satisfied.

The final release task should remain pending behind both migrations. This proves implementation can proceed without bypassing the integration gate.

  • Leave both migration tasks unclaimed until the coordinated repair begins.

Your source files and tests still contain the pre-repair collision. The team now has an agreed contract before any implementation changes begin.

The coordination gap now has an owner and a dependency gate. Next, each component owner will apply the decision while the integration owner protects the release.

Execute the Coordinated Repair

Your Claude Code team has agreed on one canonical event contract. That decision is now the only authoritative source for the repair.

Separate owners now apply the contract within their file boundaries. A downstream owner then verifies their combined result through the dependency-gated release task.

In this step, get ready to:
  • Release the producer and consumer migration tasks under exclusive file ownership.
  • Collect passing local-test handoffs from both component owners.
  • Close the release gate after all three tests pass together.
Release the component migrations

The completed contract task has released both migration tasks. The two owners can work in parallel because their assigned files do not overlap.

  • Inspect the shared task list with Ctrl+T to confirm both migration tasks are available.
  • Tell producer-owner to update src/producer.js so createOrderEvent(orderId, totalCents) returns type: 'order.created', orderId, and totalCents.
  • Tell producer-owner to update test/producer.test.js with the test name producer emits the canonical cents contract and an expected totalCents value of 2500.
  • Tell producer-owner to run its local test after saving both assigned files.
  • Tell consumer-owner to update src/consumer.js so chargeOrder(event) assigns event.totalCents directly to chargedCents.
  • Tell consumer-owner to update test/consumer.test.js with the test name consumer charges canonical integer cents without conversion and a totalCents input of 2500.
  • Tell consumer-owner to run its local test after saving both assigned files.

Each owner should now report a passing local test from its own side of the contract. Their file boundaries keep the parallel repair from creating a second collision.

Complete the direct handoffs

A passing local test proves one migration works in isolation. The integration owner also needs a direct handoff from each component owner before the release task begins.

  • Tell producer-owner to mark the producer migration task as completed.
  • Tell producer-owner to message integration-owner with its changed files and passing local-test result.
  • Tell consumer-owner to mark the consumer migration task as completed.
  • Tell consumer-owner to message integration-owner with its changed files and passing local-test result.
  • Inspect the shared task list with Ctrl+T to confirm both migration tasks are complete.

The final release task should now be unblocked automatically. The two direct messages give integration-owner a concise record of what changed before it tests the combined system.

Does a completed task still look active?

  • Ask the lead to identify which owner has already reported finished work.
  • Tell the lead to nudge that owner to update the shared task state.

If the status still lags, help me reconcile the migration task status.

Close the release gate

The integration owner is the downstream verifier for the repaired system. It closes the gate only when producer output satisfies the consumer contract in the same run.

Before the full run, predict whether the repaired suite now clears the release gate.

  • Tell integration-owner to review both direct completion messages.
  • Have integration-owner run the full release suite with this command:
node --test

What does this command verify?

The built-in Node.js test runner discovers the producer test, consumer test, and integration test. One run verifies both local migrations beside their shared contract boundary.

The summary now reports three passing tests and zero failures. The previous 250000 result is gone because both components use totalCents without conversion.

Is the release suite still failing?

  • Check that src/producer.js emits totalCents.
  • Check that src/consumer.js reads event.totalCents directly.
  • Check that both component tests use the canonical totalCents field.

Still seeing a failure? Help me compare the repaired contract files.

✔️ Awesome, I've got everything!

Your repaired workspace has the canonical contract, both component migrations, and the unchanged integration gate.

ⓧ I'd like to double check the full code

  • Compare the current workspace against these complete files.
{
  "name": "contract-collision",
  "private": true,
  "type": "module"
}

Why package.json stays unchanged

The module setting still tells Node.js to interpret the project’s .js files as ECMAScript modules. The contract repair does not require a package change.

# Producer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the producer assumed that an `order.created` event used a field named `total` containing integer cents.

Why the producer specification stays

This file remains labeled as legacy collision evidence. It preserves the assumption that helped produce the original integration failure.

# Consumer Local Specification

Status: Legacy collision fixture. The canonical contract is `contracts/order-event.md`.

Before team coordination, the consumer assumed that an `order.created` event used a field named `total` containing whole dollars, which it multiplied by 100.

Why the consumer specification stays

This specification also remains as collision evidence. Its outdated whole-dollar assumption is no longer authoritative.

# Order Event Contract

Status: Canonical
Owner: contract-owner

An `order.created` event has this shape:

    {
      "type": "order.created",
      "orderId": "order-123",
      "totalCents": 2500
    }

Rules:

- `type` is `order.created`.
- `orderId` identifies the order.
- `totalCents` is the integer number of cents charged for the order.
- Producers emit `totalCents` and consumers read `totalCents` without conversion.

What the canonical contract controls

This file gives contract-owner sole ownership of the shared field decision. Both component implementations now follow its integer-cents rule.

export function createOrderEvent(orderId, totalCents) {
  return {
    type: 'order.created',
    orderId,
    totalCents,
  };
}

How the producer applies the contract

The producer accepts totalCents and emits the same field. Its output now names the unit directly.

import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';

test('producer emits the canonical cents contract', () => {
  assert.deepStrictEqual(createOrderEvent('order-123', 2500), {
    type: 'order.created',
    orderId: 'order-123',
    totalCents: 2500,
  });
});

What the producer test proves

The assertion proves that a producer call emits the canonical field with 2500 integer cents. The test no longer validates the legacy total field.

export function chargeOrder(event) {
  return {
    orderId: event.orderId,
    chargedCents: event.totalCents,
  };
}

How the consumer applies the contract

The consumer reads event.totalCents directly. No unit conversion changes the producer’s value.

import test from 'node:test';
import assert from 'node:assert/strict';

import { chargeOrder } from '../src/consumer.js';

test('consumer charges canonical integer cents without conversion', () => {
  assert.deepStrictEqual(
    chargeOrder({
      type: 'order.created',
      orderId: 'order-123',
      totalCents: 2500,
    }),
    {
      orderId: 'order-123',
      chargedCents: 2500,
    },
  );
});

What the consumer test proves

The assertion supplies canonical integer cents to the consumer. It confirms that the charged amount remains 2500.

import test from 'node:test';
import assert from 'node:assert/strict';

import { createOrderEvent } from '../src/producer.js';
import { chargeOrder } from '../src/consumer.js';

test('producer output is valid consumer input', () => {
  const event = createOrderEvent('order-123', 2500);
  const charge = chargeOrder(event);

  assert.deepStrictEqual(charge, {
    orderId: 'order-123',
    chargedCents: 2500,
  });
});

What the release gate proves

The integration test passes real producer output directly into the consumer. Its unchanged expectation proves the coordinated repair resolved the contract collision.

  • Tell integration-owner to mark the final release task as completed.
  • Inspect the shared task list with Ctrl+T to confirm the dependency-gated release task is complete.

That is the coordinated repair complete. Your team has turned one owned contract decision into three passing tests across independently maintained components.

Secret mission

Add a Backward-Compatible Currency Field

Your repaired pipeline agrees on amounts. Now evolve its contract with a currency field while keeping legacy events valid. Coordinate four owners so contract decisions still reach the release gate before implementation can outrun them.

Clean Up Your Resources

Clean Up Your Resources

Your project files live locally with no ongoing infrastructure cost. Choose a cleanup option based on whether you want to keep using the active Claude Code team.

Cost warning

Each active teammate counts against your existing plan or API usage. Agent teams use significantly more tokens than a single session. Choose Pause or Delete when you finish working.

Resources you used:

  • Your local contract-collision Git repository containing the contract, legacy specifications, JavaScript source files, tests, and repository history.
  • Your interactive Claude Code lead session with producer-owner, consumer-owner, integration-owner, and contract-owner.
  • Your shared task list containing the contract decision, dependency gates, direct handoffs, and completed migration tasks.

Keep everything running

No action is needed. Choose this if you want to continue experimenting with the compatible currency contract or the current team structure.

  • Keep the contract-collision repository in its current location.
  • Continue using the current interactive team while the Claude Code lead session remains open.
  • Use the shared task list as a record of the completed dependencies and owner handoffs.

Pause - I'll come back to this later

End the active team session to stop the coordination work while preserving the repository. A future session can use the persisted task record as a reference.

  • Return to the Claude Code lead session from earlier.
  • Exit the current session.
  • Keep the contract-collision repository in its current location.
  • Spawn fresh teammates when you return.

The /resume command does not restore in-process teammates. Your local task record remains available for reference.

Delete - I don't want to use this again

End the interactive team before removing the local repository. Claude Code removes the team configuration when the session ends.

  • Return to the Claude Code lead session from earlier.
  • Exit the current session.
  • Press Cmd+Space to open macOS search.
  • Type Finder and press Enter.
  • Search for the contract-collision folder.
  • Drag the contract-collision folder to the Trash.
  • Search for contract-collision again to confirm that the repository is gone.
  • Leave the persisted shared task list to Claude Code's normal retention cleanup.

This removes the contract, specifications, source files, tests, and Git history. No cloud resources or third-party packages need removal.

Nice Work!

Nice Work!

You pulled it off! Your Claude Code agent team rescued a broken order-event pipeline through shared contract ownership. The final integration gate proves the compatible event contract across five passing tests.

You've learned how to:

  • Built a deterministic release gate that used integration testing to expose 250000 charged cents after both component tests passed.
  • Used a dependency-gated task graph to coordinate four named teammates without overlapping file ownership.
  • Replanned around a single contract owner who handed the exact totalCents decision directly to both component owners.
  • Completed the Secret Mission by adding the backward-compatible currency field. Legacy events resolve to credits. Explicit values such as tokens survive end to end.

Ready to quiz yourself?