September 29, 2026 · Harness Engineering · Coding Agents · CI · How-to · EN

CI Gates for Multiple Coding Agents: Validate the Combined Change

Design CI gates for multiple coding agents with worker checks, contract-boundary tests, integrated-candidate proof, generated-output verification, recovery tests, and evidence artifacts.

CI Gates for Multiple Coding Agents: Validate the Combined Change

CI Gates for Multiple Coding Agents: Validate the Combined Change

A repository can have five green coding-agent branches and one broken product.

Each agent may have tested its own patch correctly. The API branch passed against schema version A. The client branch passed against generated types from version B. The migration branch passed on an empty database. The UI branch used a fixture that bypassed the API entirely.

Every local result is real. The combined conclusion is false.

CI for multiple coding agents must prove the integrated candidate, not merely collect green worker runs. The pipeline needs separate gates for worker claims, changed contracts, producer-consumer boundaries, the combined outcome, and release-specific risks.

This guide starts after work has been decomposed into bounded tasks and before the final claim-first code review. It complements dependency-aware integration by turning its validation boundaries into repeatable automation.

If your repository does not yet have standard task packets, validation contracts, and evidence handoffs, repository-harness provides a starting structure.

The pipeline protects claims, not branches

A branch is a transport mechanism. It is not the unit of correctness.

The useful unit is a claim:

C1: API accepts JPEG and PNG avatars up to 2 MB.
C2: generated clients represent every accepted API error.
C3: web and CLI surface those errors consistently.
C4: the migration preserves existing avatar references.
C5: all four claims hold on candidate 8c21f4e.

CI should answer four different questions:

GateQuestionRuns againstFailure owner
Worker gateDoes this bounded implementation support its claim?worker checkpointworker
Contract gateIs the changed source of truth valid and accepted by consumers?producer plus representative consumerscontract owner
Candidate gateDo accepted checkpoints work together?exact integration candidateintegrator plus owning worker
Release gateIs the artifact safe to publish, migrate, or deploy?immutable release candidaterelease owner

Collapsing these into one command creates ambiguous failures. Running only worker gates misses integration faults. Running only a full suite after everything merges makes failures slow to localize.

The reliable pipeline preserves both: fast claim-local feedback and explicit combined proof.

A layered multi-agent CI architecture

Layered CI gates for multiple coding agents Worker checkpoints pass focused claim checks. Contract producers and consumers then pass boundary gates. One exact integrated candidate runs combined repository and outcome checks. Release-specific migration, recovery, packaging, and deployment proof follows. Failures return to the owning source before the candidate is rebuilt. WORKER PROOF → CONTRACT PROOF → CANDIDATE PROOF → RELEASE PROOF WORKER GATES focused tests · lint · build claim + checkpoint identity BOUNDARY GATES producer ↔ consumer schema · generated · compatibility CANDIDATE GATES combined suite · smoke clean tree · outcome proof RELEASE GATES migration · recovery package · deploy · rollback GATE FAILS preserve evidence · identify owner · repair source · rebuild candidate Repeated failures repair task contracts, source-of-truth maps, generators, tests, or repository instructions.

The ordering is intentional. A cheap worker failure should stop before integration. A producer-consumer mismatch should stop before the full repository suite. Release writers should never run against a candidate that failed combined proof.

1. Freeze one exact candidate

Do not let CI prove one commit while review examines another.

Record:

base_commit=4ab91ce
candidate_commit=8c21f4e
task_graph=docs/tasks/avatar-upload/graph.md
integration_record=docs/tasks/avatar-upload/integration.md
toolchain_lock=toolchain.lock

Every candidate-level artifact should include the candidate SHA. Every rerun should say whether it uses the same source, same toolchain, and same relevant environment.

A branch name such as integration/avatar-upload is not enough. It can move after the checks start.

If the candidate changes:

  1. classify which claims the new commit can affect;
  2. invalidate the affected evidence;
  3. rerun the required gates;
  4. preserve the old result as historical evidence, not current proof.

This is especially important when an agent responds to review feedback while other checks are still running.

2. Classify the change before selecting checks

The repository should map changed surfaces to required gates.

Change classFocused worker checksBoundary checksCandidate or release checks
Public API or schemaschema validation, contract testsold/new consumer compatibility, generated clientsrepresentative end-to-end request
Database migrationmigration unit testsreader/writer compatibilityold-state upgrade, rollback or forward-repair proof
Authenticationpolicy and unit testsclient/server authorization matrixnegative-path integration and audit evidence
Generated outputsource validationpinned regeneration and compilesecond generation leaves tree clean
CLI behaviorparser and command testsscript and API contract compatibilitypackaged-binary smoke
UI flowcomponent testsAPI/error mappingbrowser smoke on integrated backend
Recovery journalstate-machine testspersisted-state compatibilityfault injection and idempotent resume
Documentation or instructionslink and syntax checkscommand existence and path checksclean-session discovery probe

Do not route solely by file extension. A one-line schema edit may require more proof than a 500-line internal refactor.

The classification should come from accepted behavior and ownership:

change_classes:
  - public-api
  - generated-client
  - browser-flow
required_claims:
  - C1
  - C2
  - C3

Agents can propose the classification. The repository policy decides the gates.

3. Run worker gates against bounded claims

Worker CI should be fast enough to run before a handoff.

A useful worker result contains:

## Worker evidence
- Claim: C2 — generated clients match the accepted avatar schema
- Base: `4ab91ce`
- Checkpoint: `agent/avatar-clients@7d52a10`
- Input contract: `openapi/avatar.yaml@19c88d4`
- Commands:
  - `scripts/validate-openapi`
  - `scripts/generate-clients`
  - `scripts/check-generated-clean`
  - `npm test -- avatar-client`
- Result: PASS
- Omitted: package publish smoke
- Residual risk: publish metadata is deferred to the release gate

Worker gates should prove the narrow claim without pretending to prove the whole product.

Good worker gates are:

  • deterministic where possible;
  • tied to a stable checkpoint;
  • explicit about source inputs;
  • small enough to rerun during integration;
  • honest about omitted environments and tests.

A worker should not report “all tests pass” when it ran one package suite. Name the command and the scope.

4. Test producer-consumer boundaries

Most multi-agent failures live between green branches.

Identify every dependency edge:

OpenAPI schema -> generated TypeScript client -> web uploader
OpenAPI schema -> generated Go client -> CLI command
migration -> repository reader -> API response
feature flag -> backend behavior -> frontend visibility

For each edge, define a boundary gate before integration begins.

EdgeBoundary gate
Schema → clientregenerate from exact schema; compile consumer
API → UIcontract fixture plus real integrated smoke
Migration → readerload pre-change state, migrate, read with new code
Event producer → consumerschema compatibility plus replay fixture
CLI → servicepackaged CLI against candidate service
Config → runtimeparse accepted config and verify observable behavior

The strongest boundary check uses the producer artifact and consumer code that will actually ship together.

Fixtures are useful but can become parallel sources of truth. If a fixture can disagree with the authoritative schema without failing, the boundary gate is incomplete.

5. Regenerate derived output from canonical sources

Generated files attract conflicts because several agents can touch nearby source and output paths.

The gate should enforce this sequence:

  1. validate the authoritative schema, template, or specification;
  2. pin the generator version;
  3. remove or reset generated output according to repository policy;
  4. regenerate from the accepted source;
  5. compile or test representative consumers;
  6. run generation again;
  7. require the second run to produce no diff.

The second generation is critical. It proves the process is stable and the committed output is reproducible.

Do not pass a generated-output gate because the checked-in diff “looks plausible.” Do not resolve generated conflicts by hand and then bless the result with a compile-only check.

The source owns the behavior. The generator owns the derived text.

6. Build and test one integrated candidate

After worker and boundary gates pass, create one candidate in dependency order.

The candidate gate should cover:

  • required build and static analysis;
  • changed contract tests;
  • producer-consumer checks;
  • representative repository-wide tests;
  • generated-output cleanliness;
  • user-visible smoke behavior;
  • repository cleanliness after all checks;
  • explicit omissions and residual risks.

A candidate matrix might look like this:

candidate_gates:
  identity:
    - verify-base-and-candidate
  contracts:
    - validate-openapi
    - test-client-compatibility
  build:
    - build-server
    - build-web
    - build-cli
  behavior:
    - smoke-avatar-api
    - smoke-avatar-web
    - smoke-avatar-cli
  reproducibility:
    - regenerate-clean
    - verify-clean-tree

The matrix should reflect the requested outcome, not a generic collection of available scripts.

If web and CLI are both named in acceptance criteria, proving only the API is not sufficient. If the task changes error behavior, a happy-path smoke is not sufficient.

7. Treat migrations and recovery as state transitions

A green fresh-install test says little about upgrade safety.

For a migration, preserve representative old states:

empty database
small normal database
legacy nullable field
partially applied earlier migration
large or boundary-value record

Then prove the accepted transition:

old state -> new migration -> new reader/writer -> observable outcome

If rollback is promised, test rollback. If the policy is forward-only repair, test the repair path instead of implying reversibility.

Recovery workflows need stronger proof. Inject failure around mutation boundaries:

before mutation
mutation succeeded, journal not advanced
journal advanced, next mutation not started
process exits during cleanup

Reload persisted state and invoke recovery again. The next run must infer what already happened and continue safely.

A retry that works only in the same process is not crash recovery.

8. Separate required, advisory, and release-only checks

Not every check has the same authority.

Check typeMerge effectExample
Requiredblocks candidate acceptancecontract compatibility, core tests, clean generation
Advisoryreports risk but does not blockbroad performance sample with noisy infrastructure
Release-onlyblocks publishing or deploymentpackage smoke, signing, migration rehearsal
Scheduleddetects drift outside one changenightly dependency matrix, instruction-link audit

Name checks by what they prove:

contract/openapi-compatibility
boundary/avatar-web-api
candidate/avatar-outcome
reproducibility/generated-clean
release/package-smoke

Names such as test-1 and ci hide authority. A reviewer should know which accepted claim a required status protects.

If an advisory check repeatedly catches real defects, promote it or repair its reliability. If a required check is routinely ignored, it is not a real gate.

9. Preserve machine-readable evidence

Logs are useful but insufficient as the only record. They are noisy, expire, and rarely explain omissions.

Produce a small evidence manifest:

{
  "base": "4ab91ce",
  "candidate": "8c21f4e",
  "task": "avatar-upload",
  "claims": ["C1", "C2", "C3", "C4", "C5"],
  "toolchain": "toolchain.lock@6d91b2a",
  "required_checks": {
    "contract/openapi-compatibility": "pass",
    "boundary/avatar-web-api": "pass",
    "candidate/avatar-outcome": "pass",
    "reproducibility/generated-clean": "pass"
  },
  "omitted": ["windows packaged-cli smoke"],
  "residual_risks": ["package metadata is verified at release gate"]
}

Attach or archive:

  • structured test reports;
  • migration and fault-injection results;
  • generated-output diffs when failing;
  • screenshots or traces only where they prove an accepted UI behavior;
  • package hashes for release candidates;
  • the exact evidence manifest.

Evidence should support the coding-agent handoff protocol and the final review packet. It should not require a reviewer to reverse-engineer meaning from thousands of log lines.

10. Route failures back to the owner

The red check should identify the first wrong layer.

FailureReturn to
Worker unit or focused testowning worker
Disputed public behaviorcontract owner
Schema/client incompatibilityproducer and affected consumer owners
Hand-edited generated outputcanonical source and generator owner
Integration-order faultintegrator and task graph
Candidate-only seam failureowning workers plus integrator
Migration or recovery failurestate-transition owner
Packaging or deployment failurerelease owner
Repeated missing instructionrepository contract owner

Do not allow the integrator to patch every red check directly on the candidate. Integration-owned glue can be fixed there. A false worker claim or broken canonical source should return to its owner, get new evidence, and re-enter the candidate.

That preserves attribution and prevents the integration branch from becoming the only place where the system works.

11. Control flakiness instead of normalizing reruns

Multi-agent pipelines create more checks, which can expose existing flakes. The wrong response is to rerun until green.

Use a declared policy:

1. Preserve the first failure evidence.
2. Classify deterministic product failure vs infrastructure failure vs known flake.
3. Permit a bounded rerun only for documented infrastructure or quarantined-flake classes.
4. Record both attempts.
5. Never let a rerun erase the original failure.
6. Give every quarantined check an owner and expiration date.

A required check that passes on the third unexplained attempt does not provide strong proof.

Also inspect shared resources. Concurrent jobs may collide on ports, test databases, object-store buckets, browser profiles, caches, or deployment environments. Runtime isolation matters even when source worktrees are separate.

12. Keep write-capable workflows out of the proof path

Most CI gates should be read-only with respect to external systems.

Publishing packages, applying migrations, writing releases, or deploying production requires a separate release gate and narrower permissions.

Important distinctions:

  • cancellation is not serialization;
  • a green build does not authorize publishing;
  • branch protection does not make a workflow idempotent;
  • an agent-generated workflow change should not gain write credentials before review;
  • a release job needs a known artifact identity and safe retry behavior.

Use least privilege. Keep untrusted code away from secrets. Require environment approval where policy demands it. Queue or otherwise serialize write-capable release operations when overlapping writers would be unsafe.

The candidate pipeline proves the artifact. The release pipeline governs the external mutation.

13. Example GitHub Actions shape

The repository-specific commands will differ, but the separation should remain visible:

name: candidate-proof

on:
  pull_request:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  classify:
    runs-on: ubuntu-latest
    outputs:
      api: ${{ steps.changes.outputs.api }}
      generated: ${{ steps.changes.outputs.generated }}
      migration: ${{ steps.changes.outputs.migration }}
    steps:
      - uses: actions/checkout@v4
      - id: changes
        run: ./scripts/classify-change

  worker-and-static:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/validate-static
      - run: ./scripts/test-focused

  contract-boundaries:
    needs: [classify, worker-and-static]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/test-contract-boundaries
      - if: needs.classify.outputs.generated == 'true'
        run: ./scripts/check-generated-clean

  candidate:
    needs: [contract-boundaries]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/test-candidate
      - run: ./scripts/smoke-accepted-outcome
      - run: ./scripts/write-evidence-manifest
      - uses: actions/upload-artifact@v4
        with:
          name: candidate-evidence-${{ github.sha }}
          path: artifacts/evidence/

The release workflow is intentionally absent. It should consume a proven immutable artifact through a separate permission boundary rather than inheriting write access in every pull-request run.

Do not copy this file without adapting the change classifier, accepted outcomes, and security model. The architecture matters more than the vendor syntax.

14. Adopt the gates incrementally

A small repository does not need a sophisticated matrix on day one.

Stage 1: Name the evidence

Require every worker to report an exact checkpoint, command, result, omission, and risk.

Stage 2: Add one boundary gate

Choose the seam that has caused the most integration failures: schema/client, migration/reader, API/UI, or generator/output.

Stage 3: Pin the candidate

Run combined proof against one candidate SHA and preserve the evidence manifest.

Stage 4: Add generated and clean-tree checks

Make derived output reproducible and detect tests that mutate the repository.

Stage 5: Add state-transition proof

Cover migration, rollback, recovery, and failure injection where the product needs them.

Stage 6: Separate release authority

Move package, migration, and deployment writes behind their own permissions and gates.

Add complexity only when it controls a real risk. The smallest useful pipeline is one that can explain which claim failed, who owns the repair, and whether the integrated outcome is proven.

Multi-agent CI checklist

# Multi-agent candidate proof

## Identity
- [ ] exact base and candidate commits recorded
- [ ] task graph and integration record linked
- [ ] toolchain and relevant environment identified

## Worker proof
- [ ] each claim maps to one owner and checkpoint
- [ ] focused commands and results are preserved
- [ ] omissions and residual risks are explicit

## Boundaries
- [ ] changed contracts have authoritative sources
- [ ] producers integrate before consumers
- [ ] representative producer-consumer checks pass
- [ ] generated output is recreated and stable

## Candidate
- [ ] required build and static checks pass
- [ ] representative repository tests pass
- [ ] accepted user-visible outcome is reproduced
- [ ] migration or recovery proof runs when relevant
- [ ] final tree is clean

## Evidence
- [ ] machine-readable manifest names candidate and claims
- [ ] failed first attempts are preserved
- [ ] advisory and release-only checks are labeled
- [ ] artifacts have retention appropriate to review and release

## Authority
- [ ] failed claims return to the owning source
- [ ] external write workflows use separate permissions
- [ ] release jobs consume an immutable proven artifact

Make CI part of the repository contract

Keep the root instruction short and link to executable controllers:

## Validation for concurrent coding-agent work

- Every worker returns a stable checkpoint and claim-scoped evidence.
- Changed contracts require producer-consumer boundary checks.
- Worker-green is not candidate-green; one exact integrated commit must pass combined proof.
- Generated output is recreated from canonical sources and must be reproducible.
- Migration, recovery, packaging, and deployment risks use explicit gates.
- Failed claims return to their owning source before the candidate is rebuilt.
- External write workflows are separate from pull-request proof and use least privilege.

The detailed matrix belongs in version-controlled scripts and documentation. The root AGENTS.md should route agents to those sources instead of duplicating every command.

When the same CI failure repeats, repair the controller:

  • unclear change classification becomes a path-to-gate map;
  • missing contract proof becomes a boundary test;
  • unstable generation becomes a pinned toolchain;
  • silent omission becomes an evidence-manifest field;
  • unsafe retry becomes an idempotency or serialization rule;
  • wrong agent behavior becomes a task or repository-instruction correction.

That is harness engineering: every failure improves the environment that governs the next run.

Prove the combined change

Multiple coding agents can accelerate implementation without weakening confidence, but only when the pipeline preserves the distinction between local claims and combined truth.

Run focused worker checks. Test changed contracts across their consumer boundaries. Regenerate derived output. Freeze one candidate. Reproduce the accepted outcome. Preserve machine-readable evidence. Route failures back to the owner. Keep release writes behind a separate authority boundary.

Then review and ship the candidate whose claims survive together.

Use repository-harness on GitHub to establish the repository-visible tasks, validation contracts, handoffs, checkpoints, and evidence structure that make these CI gates repeatable.


FAQ

How should CI work when multiple coding agents change one repository?

Use layered gates. Each worker runs focused checks for its own claim, producer-consumer boundaries get compatibility tests, and one exact integrated candidate runs repository-wide and user-visible outcome checks. Preserve the candidate commit, commands, results, omissions, and artifacts so review can reproduce the proof.

Are passing tests on every coding-agent branch enough?

No. Separate branches may pass against different assumptions, contract versions, generated files, or base commits. CI must also test the branches together in dependency order and prove the combined behavior on one immutable candidate.

Which checks should block a multi-agent merge?

Block on checks that protect accepted behavior: changed-contract tests, producer-consumer compatibility, generated-output cleanliness, required build and static checks, migration or recovery proof when relevant, representative end-to-end behavior, and a clean reproducible candidate. Advisory checks should be labeled separately.

How do I keep multi-agent CI fast?

Classify the change, run focused worker checks early, cache only inputs with explicit keys, parallelize independent read-only checks, and reserve full candidate and release gates for the integrated commit. Speed should come from better routing, not from silently dropping coverage.

How should CI validate generated files from coding agents?

Validate the authoritative schema or template, pin the generator, regenerate from the accepted source, and require a second generation run to leave the tree clean. Never treat a hand-edited generated diff as sufficient proof.

Should failed CI be repaired directly on the integration branch?

Only integration-owned glue belongs there. A failed worker claim, source contract, generator input, migration, or subsystem behavior should return to its owning branch or canonical source, then the candidate should be rebuilt. This prevents the integration branch from becoming an unowned patch pile.

What evidence should a coding-agent CI run preserve?

Preserve the base and candidate commits, task and claim identifiers, changed-contract map, exact commands, toolchain identity, machine-readable results, logs for failures, generated-output status, omitted checks, residual risks, and links to durable artifacts. Evidence should explain what passed and what remains unproven.