Skip to main content
Longshot includes a sophisticated merge queue that integrates completed work from parallel agents back into the main branch. The merge system supports multiple strategies, automatic conflict detection, and intelligent retry logic.

Overview

When an agent completes a task:
  1. The branch is pushed to the remote repository
  2. The branch is enqueued in the merge queue with a priority
  3. The merge queue processes branches serially
  4. On conflict, the branch is automatically rebased and retried
  5. After exhausting retries, a fix task is created

Merge Strategies

Longshot supports three merge strategies, configured via MERGE_STRATEGY in .env:

Fast-Forward

.env
MERGE_STRATEGY=fast-forward
Attempts a fast-forward merge (no merge commit):
git merge --ff-only origin/worker/task-001
Pros:
  • Clean, linear history
  • No merge commits cluttering the log
  • Ideal for sequential work
Cons:
  • Fails if main has diverged
  • Requires frequent rebasing
When to use:
  • Low-concurrency workflows (1-5 agents)
  • Projects that value linear history
  • When tasks are unlikely to conflict
.env
MERGE_STRATEGY=rebase
Rebases the feature branch onto main before merging:
git rebase origin/main origin/worker/task-001
git merge --ff-only origin/worker/task-001
Pros:
  • Linear history like fast-forward
  • Automatically resolves divergent branches
  • Works well with parallel agents
  • Conflicts are detected early
Cons:
  • Rewrites commit history (force push required)
  • Slightly slower than fast-forward
When to use:
  • Default choice for most Longshot deployments
  • Medium to high concurrency (5-50 agents)
  • Projects that want clean history with parallel development
Rebase is the recommended strategy for Longshot. It balances clean history with parallel execution, and the automatic retry system handles most conflicts transparently.

Merge Commit

.env
MERGE_STRATEGY=merge-commit
Creates a merge commit:
git merge --no-ff origin/worker/task-001
Pros:
  • Never rewrites history
  • Preserves exact branch topology
  • Explicit record of when work was integrated
  • Most permissive (rarely fails)
Cons:
  • Creates extra merge commits
  • History can become cluttered at scale
When to use:
  • High-concurrency workflows (50+ agents)
  • When history preservation is critical
  • As a fallback when rebase fails

Automatic Fallback

If the configured strategy fails (non-conflict error), Longshot automatically falls back to merge-commit:
if (!result.success && !result.conflicted && this.mergeStrategy !== "merge-commit") {
  logger.warn(`${this.mergeStrategy} failed, falling back to merge-commit`);
  await this.abortMerge(cwd);
  result = await this.mergeBranch(mergeRef, this.mainBranch, "merge-commit", cwd);
}
This ensures maximum merge success rate even when the primary strategy encounters issues.

Conflict Detection and Retry

Longshot includes a sophisticated conflict retry system:

Retry Logic

When a merge conflict occurs:
  1. Detection: The merge queue detects conflicting files
  2. Abort: The merge is aborted to restore clean state
  3. Rebase: The branch is rebased onto the latest main
  4. Re-queue: The branch is re-queued with high priority (priority 1)
  5. Retry: The merge is attempted again
Default: 2 retries before escalating to a fix task.

Configuration

Configure retry behavior in code (not exposed in .env yet):
const mergeQueue = new MergeQueue({
  mergeStrategy: "rebase",
  mainBranch: "main",
  repoPath: "./target-repo",
  maxConflictRetries: 2,  // Default: 2
});

Why Retries Work

After rebase, the branch is based on the latest main, which includes:
  • All branches that merged since the conflict
  • Any conflicting changes that were integrated
In many cases, the conflict resolves automatically on retry because:
  1. The conflicting branch already merged
  2. The changes are now compatible after rebase
  3. The files no longer overlap

Retry Example

Iteration 1:
  Branch A: Adds function foo() to utils.ts (line 10)
  Branch B: Adds function bar() to utils.ts (line 10)
  → Conflict!

Retry 1:
  Branch A merges first
  Branch B rebased onto main (foo is now at line 10)
  Branch B adds bar() at line 15 (after foo)
  → Success!

Priority-Based Queue

The merge queue uses priority ordering:
mergeQueue.enqueue(branch, priority);

Priority Levels

  • Priority 1 (highest) - Conflict retries
  • Priority 5 (default) - Normal task completions
  • Priority 10 (lowest) - Low-importance merges
Branches with the same priority are processed in FIFO order (by enqueuedAt timestamp).

Deduplication

The queue automatically deduplicates:
  • Branches already merged are skipped
  • Branches already in the queue are not added again

Background Processing

The merge queue runs as a background loop:
mergeQueue.startBackground(intervalMs: 5000);
Every 5 seconds:
  1. Check if there are branches in the queue
  2. Dequeue the highest-priority branch
  3. Attempt the merge
  4. Fire callbacks for merge results
  5. Continue until queue is empty

Callbacks

The orchestrator registers callbacks to handle merge results:
mergeQueue.onMergeResult((result) => {
  logger.info("Merge result", {
    branch: result.branch,
    status: result.status,  // "merged" | "conflict" | "failed"
    message: result.message,
  });
});

mergeQueue.onConflict((info) => {
  logger.warn("Merge conflict", {
    branch: info.branch,
    conflictingFiles: info.conflictingFiles,
  });
  // Create a fix task for the reconciler
});

Merge Lifecycle

Full lifecycle of a branch merge:

1. Task Completion

// In orchestrator
const handoff = await workerPool.assignTask(task);
mergeQueue.enqueue(task.branch, 5);  // Priority 5 (normal)

2. Queue Processing

const branch = mergeQueue.dequeue();  // "worker/task-001"

3. Git Operations

# Checkout main
git checkout main

# Fetch the remote branch
git fetch origin worker/task-001

# Merge using configured strategy
git rebase origin/main origin/worker/task-001
git merge --ff-only origin/worker/task-001

# Push to remote
git push origin main

# Clean up remote branch
git push origin --delete worker/task-001

4. Success

{
  success: true,
  status: "merged",
  branch: "worker/task-001",
  message: "Merged successfully"
}

5. Conflict → Retry

{
  success: false,
  status: "skipped",
  branch: "worker/task-001",
  message: "Conflict retry 1/2 — re-queued (rebased)"
}

// Branch is automatically rebased and re-queued

6. Conflict → Fix Task

After exhausting retries:
{
  success: false,
  status: "conflict",
  branch: "worker/task-001",
  message: "Merge conflict: 2 conflicting files",
  conflicts: ["src/utils.ts", "src/types.ts"]
}

// Reconciler creates a fix task

Merge Statistics

The merge queue tracks statistics:
const stats = mergeQueue.getMergeStats();
// {
//   totalMerged: 45,
//   totalSkipped: 0,
//   totalFailed: 2,
//   totalConflicts: 3,
// }

Merge Success Rate

Calculated as:
mergeSuccessRate = totalMerged / (totalMerged + totalConflicts + totalFailed)
A healthy system maintains >90% merge success rate.

Dashboard Integration

The dashboard displays merge queue activity in real-time:

Merge Queue Panel

  • Success Rate - Visual progress bar and percentage
  • Merged - Count of successfully integrated branches
  • Conflicts - Branches with merge conflicts
  • Failed - Merges that failed for other reasons

Activity Feed

Merge events appear color-coded:
  • Green - >> merged worker/task-001
  • Yellow - !! conflict worker/task-001
  • Red - xx merge fail worker/task-001

Troubleshooting

High Conflict Rate

If you see many conflicts:
  1. Increase retry limit: Set maxConflictRetries: 3 or higher
  2. Switch to merge-commit: Reduces conflicts at the cost of history cleanliness
  3. Review task boundaries: Ensure tasks don’t overlap in scope
  4. Stagger task assignment: Reduce parallelism slightly

Merge Queue Stuck

If the queue stops processing:
  1. Check if background loop is running: mergeQueue.isBackgroundRunning()
  2. Review logs for git errors
  3. Manually process: await mergeQueue.processQueue()
  4. Verify git mutex is not deadlocked

Push Failures

If branches merge locally but fail to push:
  1. Verify GIT_TOKEN has push access
  2. Check for branch protection rules
  3. Ensure main branch exists on remote
  4. Review network connectivity

Slow Merge Processing

If merges take longer than expected:
  1. Reduce intervalMs in startBackground() (default: 5000ms)
  2. Check if rebases are slow due to large repo size
  3. Monitor CPU/disk I/O on orchestrator machine
  4. Consider using a dedicated git worktree

Advanced Configuration

Git Mutex

Prevent concurrent git operations:
import { Mutex } from "async-mutex";

const gitMutex = {
  mutex: new Mutex(),
  acquire: () => gitMutex.mutex.acquire(),
  release: () => gitMutex.mutex.release(),
};

const mergeQueue = new MergeQueue({
  // ... config
  gitMutex,
});
This ensures only one merge happens at a time, preventing git lock conflicts.

Custom Priority Logic

// High priority for critical fixes
if (task.description.includes("URGENT")) {
  mergeQueue.enqueue(task.branch, 1);
} else {
  mergeQueue.enqueue(task.branch, 5);
}

Manual Merge Control

Disable background processing for manual control:
mergeQueue.stopBackground();

// Manually process queue
const results = await mergeQueue.processQueue();
for (const result of results) {
  console.log(result);
}

Best Practices

Strategy Selection

  • Default: Use rebase for clean history and automatic conflict handling
  • High concurrency: Use merge-commit for >50 parallel agents
  • Low concurrency: Use fast-forward for simplest linear history

Conflict Management

  • Let retries handle most conflicts automatically
  • Monitor conflict rate in the dashboard
  • Review fix tasks to identify systematic issues
  • Adjust task granularity if conflicts are frequent

Queue Tuning

  • Keep intervalMs at 5000ms for balanced responsiveness
  • Use priority levels sparingly (most tasks should be priority 5)
  • Monitor queue length - sustained growth indicates bottleneck

Testing Strategies

Test each merge strategy with your codebase:
# Test fast-forward
MERGE_STRATEGY=fast-forward pnpm start

# Test rebase
MERGE_STRATEGY=rebase pnpm start

# Test merge-commit
MERGE_STRATEGY=merge-commit pnpm start
Observe:
  • Merge success rate
  • Conflict frequency
  • History cleanliness (git log --graph)
  • Overall throughput

Next Steps

Debugging

Debug merge conflicts and failures

Running with Dashboard

Monitor merge queue in real-time

Build docs developers (and LLMs) love