Synthesis merging: what happens after the first PR

I wrote about the multi-contributor problem in synthesis coding three weeks ago — what happens when someone else starts contributing to a project you built through sustained AI collaboration. The adopt-and-adapt pattern was the answer: don’t merge blindly, integrate deliberately.

Since then, one of my teams has been running adopt-and-adapt at scale. Twelve PRs from six engineers in a single day. Three rounds of contributions over three weeks. What I’ve learned is that the integration pattern isn’t static. It evolves. And the act of integration — what I’m calling synthesis merging — is more nuanced than the original article captured.

Synthesis merging is not code review

In a standard code review, the reviewer evaluates and approves or rejects. In synthesis merging, the lead synthesist doesn’t just evaluate — they synthesize. They take the contribution, understand it in the context of the full system, and integrate it in a way that makes the combined result better than what either the contributor or the lead would have produced alone.

The distinction matters because it changes what you look for. A code reviewer asks: “Is this correct? Does it follow standards?” A synthesis merger asks those questions plus: “How does this interact with the other contributions being integrated today? What gaps exist between this contribution and the rest of the system that neither party could have seen?”

On March 3, I integrated PRs from three different engineers who had all modified the same ecommerce subsystem. Each PR was well-implemented in isolation. But when I merged them sequentially, I could see what none of the individual contributors could: a writer guidance field (writing_notes) that had been threaded through intros and headlines but not through product descriptions. Sixteen lines across two files. Not a bug — the feature worked. Not a request — nobody asked for it. It was a coherence improvement that only became visible from the integration vantage point.

That’s synthesis merging. Making it better, not just making it work.

Ordering matters more than you think

When you’re integrating one PR, ordering is irrelevant. When you’re integrating twelve in a day, it becomes a design decision.

I had twelve open PRs from six engineers. Three touched the ecommerce subsystem. Three touched WordPress/CMS integration. The rest were independent. The naive approach would have been to merge them in submission order, or largest-first, or some other arbitrary sequence.

Instead, I mapped the file overlaps before starting. PRs #10, #20, and #22 all modified ecommerce code. PR #10 (a description field) was smaller and more contained than PR #20 (writer guidance across multiple templates). By integrating #10 first, the subsequent conflicts with #20 were predictable — additive rather than interleaving. Had I reversed the order, the same conflicts would have been harder to resolve because #20 touched more functions.

The principle: when multiple PRs touch the same subsystem, integrate the simpler one first. This makes subsequent conflicts predictable and additive. It’s the same logic as dependency ordering in builds — you compile the leaf nodes before the packages that depend on them.

One independent PR — a bug fix — had zero overlap with anything. I integrated it first. Zero conflicts. Fifteen minutes. Getting a clean win early also validated that the integration pipeline was working correctly before tackling the complex merges.

Auto-merge is the hidden danger

Git’s three-way merge is good at textual conflict resolution. It’s terrible at semantic conflict resolution. And the dangerous cases aren’t the ones with conflict markers — those force you to look. The dangerous cases are the ones git resolves automatically but incorrectly.

When I merged PR #20 on top of #10, git produced a clean merge with no conflict markers. But reading the merged result, I found a duplicate function call: prompt_library.get_prompt() was called twice in the same function — once without a description parameter, once with it. Two contributors had modified the same function differently. One refactored it into a two-step pattern; the other added parameters to the original call. Git merged both patterns into the function body. Textually correct. Semantically broken.

The fix was one line — removing the redundant call. Finding it required reading the merged code as if reviewing it fresh, not just checking for conflict markers. This is where the synthesist’s knowledge of the codebase is irreplaceable. Automated merge tools handle text. The synthesist handles meaning.

The three phases of evolution

The intensity of synthesis merging should decrease over time. If you’re still doing full adopt-and-adapt after the team’s fifth round of contributions, either the team isn’t learning or you’re not trusting appropriately.

I’ve seen this play out over three weeks:

Phase 1: Full adopt-and-adapt. The first round of contributions (mid-February) required substantial rework during integration. Seven to eight issues per PR. Missing audit logging. JWT claims lost on refresh. N+1 queries. Code built against a snapshot of a system that no longer existed. Every PR needed a fresh integration branch with selective, file-by-file reconstruction.

Phase 2: Lighter-touch integration. Ten days later, the same team’s second round had fewer than half the issues. One PR needed only a debug comment removed. The code followed patterns from the first round’s feedback. Integration meant merging the contributor’s branch and making targeted fixes — not reconstruction.

Phase 3: Zero-adjustment merges. By early March, some PRs required no adjustments at all. A bug fix came through clean — correct scope, backward compatible, tests passing. The code showed that the contributor had internalized the standards through the review feedback loop.

This progression is the measurable signal that synthesis merging is working. Track the adjustment count per PR. When it consistently approaches zero, the team is ready for more autonomy — peer-reviewed direct merges with lead approval as the final check, not the primary integration step.

The phases aren’t permanent promotions. If a contribution introduces a security gap or architectural regression, the intensity goes back up. They’re a current assessment, adjusted in both directions as the evidence warrants.

Contributor attribution is infrastructure

Each squash merge commit in my workflow includes explicit contributor attribution using git’s Co-authored-by trailer:

feat: add ecommerce description field to content pipeline

Integrates product description generation with writer guidance support.

Co-authored-by: Contributor Name <[email protected]>

GitHub recognizes Co-authored-by trailers and counts them toward the contributor’s activity graph. This matters more than most leads realize. Developers use contribution graphs for career advancement. A synthesis merging workflow that funnels all commits through the lead’s name — even with human-readable credit in the message body — effectively erases contributors from the project’s visible history.

The attribution model should match the integration phase:

  • Phase 1 (substantial rework): Lead as commit author, contributor as Co-authored-by. Both did meaningful work; the lead did more of the final implementation.
  • Phase 2 (minor adjustments): Contributor as commit --author, lead as Co-authored-by. The code is predominantly the contributor’s; the lead refined it.
  • Phase 3 (direct merge): Standard PR merge flow. The contributor is automatically the commit author. The lead’s review is recorded in the PR, not the commit.

This progression mirrors the trust progression. As contributors earn more integration autonomy, they earn more visible credit. Attribution isn’t decoration — it’s the mechanism that makes the team feel ownership of the codebase, which is the precondition for the lead eventually stepping back from the integration role.

Batching reveals architecture

One unexpected benefit of integrating multiple PRs in sequence: architectural patterns become visible that no individual diff would reveal.

Three PRs all added optional fields to the same ecommerce data model (ProductInput). Three separate contributors, working independently, each added their field correctly. But seeing the three additions in sequence revealed:

  • The model was becoming a catch-all for loosely related data
  • A prompt injection pattern ({% if field is defined and field %}) was being duplicated across templates
  • The service layer had a growing parameter list being threaded through multiple function calls

None of these are problems in isolation. Each PR was correctly scoped. But the trajectory was clear: this subsystem would benefit from a refactor into a more extensible pattern before the next round of additions.

The synthesist sees the full sequence of changes, not just individual diffs. Use this vantage point to identify architectural drift early — before it becomes pain.

The staging branch is a convergence point, not a mirror

A practical workflow detail that proved more nuanced than expected: the staging branch (develop) in our workflow auto-deploys to a staging environment. The natural instinct is to treat it as a downstream mirror of main — push main to develop, staging updates, done.

That model breaks when contributors push their own work to develop for staging validation. The staging branch becomes a convergence point for two independent streams: the synthesist’s curated work from main, and contributors’ in-progress work that hasn’t been synthesis-merged yet.

I learned this when pushing an integrated release to develop and having the push rejected — the branch had three unintegrated commits from an engineer’s feature PR. Force-pushing would have destroyed their work. The right approach was to merge main into develop, preserving both streams.

The integration pipeline works like this:

  1. Create integration branch off main
  2. Merge and adjust contributor code
  3. Run the full test suite locally
  4. Merge to develop (not force-push) — auto-deploys to staging
  5. Verify on the staging URL
  6. Squash merge to main
  7. Deploy to production (explicit approval required)

Step 4 is where the subtlety lives. You must check for divergence before pushing, and merge if needed. The staging environment needs both the integrated release and any in-progress contributor work. Treating develop as a one-way mirror of main destroys the bidirectional flow that makes the staging environment useful.

Tests catch what contributors can’t see

Each PR passes its own CI independently. The synthesis merge still catches failures.

In one integration, combining three PRs that had each passed their own CI produced fourteen test failures — all caused by cross-PR interactions. One PR added a minimum response size threshold to an extraction service. Existing tests used mock responses of about forty bytes — well below the new threshold. Those tests had been passing for months, but now their mock responses were being rejected as incomplete and routed through a fallback path the tests didn’t expect. Another PR constrained a mock client with spec=, which prevented the nested attribute access that a different PR’s tests relied on. A third PR added a new extraction strategy, but the “all strategies fail” test only mocked the original two strategies — the new one succeeded, breaking the test’s premise.

None of these failures were visible to any individual contributor. Each PR was correct in isolation. The conflicts existed only in the combination — in the space between contributions that hadn’t seen each other yet.

Earlier in the project, a single PR broke seven existing tests in an unrelated component because it added a new fetch call on mount that collided with the test infrastructure’s mock patterns. The fix was eleven lines. Finding the root cause required understanding both the component’s data flow and the test mock architecture — context no individual contributor had.

The synthesis merge’s test run isn’t just “does the new code work?” It’s “does the new code break anything that was working?” This is the integration quality gate that individual contributors working on branches fundamentally cannot provide. Run the full test suite on the integration branch — not just the tests for the PRs being merged. Cross-PR conflicts hide in tests that none of the individual PRs touched.

AI-assisted code needs systematic convention review

A pattern specific to synthesis-coded projects: contributors using AI coding tools produce code that is functionally correct but drifts from project-specific conventions. The AI writes clean code — it follows general best practices. But it loses project-specific conventions (brand terminology, messaging rules, CSS framework patterns, role-based access conventions) when the context window gets long. These conventions don’t exist in the AI’s training data, so once they fall out of active context, the AI can’t recover them on its own.

One of my engineers observed this with Tailwind conventions during longer Claude Code sessions. Another noticed it during code reviews. During one PR review, I caught brand terminology violations and AI messaging compliance issues — the kind of thing a standard correctness-focused code review would miss.

The fix isn’t behavioral (“remind the AI to follow conventions”). That’s fragile. The fix is systematic: make convention verification a formal step in the review process. We added a PR review checklist to the project’s CLAUDE.md with two tiers — standard items (correctness, patterns, tests, security) that apply to every project, and project-specific items (brand terminology, AI messaging rules, CSS conventions) that are unique to ours. The checklist became a formal gate in integration review, not an optional pass.

Every synthesis-coded project should define its own checklist. The standard items are universal. The project-specific items are where convention drift actually happens — and where the synthesis merge catches what automated tools cannot.

The meta-lesson

Synthesis merging exists on a spectrum. At one end, the lead reconstructs the contribution from scratch. At the other, the lead reviews and approves a PR that merges directly. The skill is reading the signals — quality trends, issue severity, contributor growth — and adjusting the integration intensity at the right time.

The goal is not to maintain control. It’s to make yourself unnecessary. Every integration cycle should leave the team closer to self-sufficiency: contributors who internalize standards, peers who catch issues in review, a codebase with enough documentation that implicit conventions become explicit ones.

The full methodology is documented in the open-source Multi-contributor Synthesis Coding runbook. It covers the adopt-and-adapt pattern, quality gates, communication principles, and the detailed contribution workflow. This article describes how that methodology evolves in practice — from the first integration to the point where the team runs the process without you.


This article is part of the synthesis coding series. See also: When someone else contributes to your synthesis-coded project.

Rajiv Pant is President of Flatiron Software and Snapshot AI, where he leads organizational growth and AI innovation. He is former Chief Product & Technology Officer at The Wall Street Journal, The New York Times, and Hearst Magazines. Earlier in his career, he headed technology for Condé Nast’s brands including Reddit. Rajiv coined the terms “synthesis engineering” and “synthesis coding” to describe the systematic integration of human expertise with AI capabilities in professional software development. Connect with him on LinkedIn or read more at rajiv.com.