Merge Request Lifecycle
When a worker completes successfully:- Worker commits changes to
task/<id>branch in its copy - Orchestrator creates a MergeRequest in the DB with status
Queued - Refinery picks up the MR and processes it
- Outcome:
Merged,Conflicted,NeedsResolution,VerifyFailed, orFailed
MergeRequest Structure
Each merge request tracks:Git-Based Merge (Source is a Git Repo)
For projects that are git repositories, Enki uses a temp clone workflow to keep the source working tree clean:Step 1: Create Temp Clone
The refinery creates a temporary shared clone of the source repo:--shared reuses the source repo’s object store — fast and space-efficient. All merge work happens in this temp clone.
Step 2: Fetch Worker Branch
Fetch the worker’s branch from its copy into the temp clone:Step 3: Merge
Checkout the default branch and merge the worker branch:Three-way merge: Git automatically resolves non-overlapping additions. If worker A adds file
a.rs and worker B adds file b.rs, the merge succeeds without conflicts.Step 4: Conflict Detection
If the merge fails, list conflicted files:- Get the conflict diff:
git diff - Emit
MergeNeedsResolutionevent with:temp_dir: Path to the temp cloneconflict_files: List of conflicted file pathsconflict_diff: Full diff showing conflict markers
- Prevent temp clone cleanup (
std::mem::forget(_cleanup)) - Spawn a merger agent to work in the temp clone
The temp clone is kept alive for the merger agent. When the merger finishes resolving conflicts, it commits the resolution, and the refinery picks up from step 5.
Step 5: Verification (Optional)
If.enki/verify.sh exists, run it in the temp clone:
MergeOutcome::VerifyFailed with stdout/stderr.
Step 6: Bring Result Back to Source
Fetch the merged result from the temp clone to the source repo:--ff-only ensures the source branch pointer just moves forward (no new merge commit). The real merge happened in the temp clone.
Step 7: Cleanup
Delete the temp clone and the worker’s task branch:Filesystem Merge (Source is Not a Git Repo)
For projects that aren’t git repos (e.g., a directory of scripts), Enki uses a filesystem merge:- Find the baseline commit in the worker’s copy (the “main” branch before the task branch)
- Get changed files:
git diff --name-status <base> HEAD - Verify (run
.enki/verify.shif present) - Copy files back to source:
- Added/Modified: Copy file from worker copy to source
- Deleted: Remove file from source
- Update MR status to
Merged
Filesystem merges don’t detect conflicts — they blindly overwrite. Use git-based projects if you need conflict detection.
MergeNeedsResolution: The Merger Agent
When a merge has conflicts that can’t be auto-resolved, the refinery emitsEvent::MergeNeedsResolution. The coordinator:
- Spawns a merger agent (ACP session with role
merger) - Gives the agent minimal tools (
MERGER_TOOLS): read files, edit files, run git commands - Provides context:
temp_dir: Where to work (the temp clone with conflict markers)conflict_files: List of conflicted filesconflict_diff: Full diff showing<<<<<<< HEADmarkers
- Agent resolves conflicts by editing files and committing
- Agent finishes → refinery picks up at step 5 (verification)
Verify Script: .enki/verify.sh
The optional .enki/verify.sh script runs after the merge completes (in the temp clone for git projects, in the worker copy for non-git projects).
Exit code 0: Merge is accepted.
Non-zero exit: Merge is rejected, task fails.
Example: Run Tests Before Merge
MergeOutcome::VerifyFailed.
Conflict Resolution Flow
Here’s a detailed look at how conflicts are handled:Merge Priority
Merge requests have apriority field (default: 2). Higher priority MRs are processed first. Use this to prioritize critical fixes over routine changes.
The refinery processes MRs in priority order (highest first), then by
queued_at (oldest first).Merge Outcomes
The refinery reports one of five outcomes:MergeOutcome::Merged
Merge succeeded, verification passed (if applicable), and the branch was merged into the source repo. The task is Done.
MergeOutcome::Conflicted(String)
Merge had conflicts that couldn’t be resolved (e.g., no merger agent available, or merger failed). The task is Blocked. The conflict detail is stored in review_note.
MergeOutcome::NeedsResolution { temp_dir, conflict_files, conflict_diff }
Merge had conflicts that need an agent to resolve. The coordinator spawns a merger agent to work in temp_dir.
MergeOutcome::VerifyFailed(String)
.enki/verify.sh exited non-zero. The task is Failed. Stdout/stderr from verify.sh is stored in review_note.
MergeOutcome::Failed(String)
Something went wrong (git command failed, filesystem error, etc.). The task is Failed. Error detail is stored in review_note.
Worker Output Without Merge: Artifact Mode
Roles withoutput = "artifact" (like researcher, code_referencer) skip the refinery entirely. Their output is saved to .enki/artifacts/<exec>/<step>.md and immediately made available to dependent steps via upstream_outputs.
No branch, no merge, no conflicts.
Best Practices
Keep merges small: The more changes in a branch, the higher the conflict risk. Break large tasks into smaller steps when possible.
Debugging Merge Failures
If a merge fails, check:- MergeRequest in DB:
SELECT * FROM merge_requests WHERE id = 'mr-...'- Check
statusandreview_notefor error details
- Check
- Worker branch: Does
task/<id>have any file changes? (git diff <base> task/<id>) - Conflict diff: If
status = Conflicted,review_notecontains the conflict detail - Verify output: If
status = VerifyFailed,review_notecontains stdout/stderr from verify.sh
What if verify.sh is flaky?
What if verify.sh is flaky?
If verify.sh sometimes fails due to non-determinism (e.g., race conditions in tests), consider:
- Retry the task:
Command::RetryTask { task_id }will re-run the worker and re-attempt the merge. - Fix the flake: Update verify.sh to handle retries or skip flaky tests temporarily.
- Remove verify.sh: If verification isn’t critical, delete the script.
Next Steps
Roles
Understand agent roles and output modes
Worker Isolation
Learn about copy-on-write cloning
