Why does agent loop create merge conflicts? #313
Closed
opened 2026-05-28 02:50:14 +00:00 by guettli
·
2 comments
No Branch/Tag Specified
main
issue-563-agentloop-validation
dummy-pr-test
issue-560-fix-firebase-run-url
issue-539-stable-imap-uid
issue-533-shared-email-list
plan-issue-555
drop-nix
plan-issue-484
plan-issue-539
plan-issue-535
plan-issue-474
plan-issue-533
fix-dagger-engineless-precommit
issue-521-fix-deploy-yml-wait-time-api
issue-502-fix-email-id-collision-mailbox
issue-492-eliminate-duplicate-build-runner
issue-494-website-change-detection
issue-491-parallelize-check
issue-478-fix-stalwart-dual-stack-bind
issue-475-allowed-addresses-glob
issue-473-search-result-reorder
issue-453-update-agentloop-defaults
issue-466-structured-search
issue-505-exclude-chaos-monkey-from-regular-ci
issue-509-fix-search-result-sorting
fix-ink-sparkle-remaining-tests
issue-506-fix-search-emails-tests
issue-504-runner-wait-time
issue-488-search-notes
issue-472-changelog-issue-links
issue-501-folder-search-local-sqlite
issue-486-fix-stale-test-shader-mismatch
fix/prevent-settled-search-rerun-473
issue-467-fix-search-stale-results
issue-446-installed-versions-in-changelog
issue-462-fix-pr
issue-448-chaos-monkey-test
issue-436-notes-on-emails
issue-429-unify-mail-display
issue-422-move-to-folder-create-new
issue-414-ensure-not-run-as-root
issue-424-unify-email-list-views
issue-419-trusted-senders-page
issue-425-fix-prs
test-foo
issue-421-bug-report
issue-383-fix-ci
issue-394-fix-deploy-flutter-version
issue-391-fix-ci-double-trigger
issue-376-combined-inbox-v2
issue-376-combined-inbox
issue-384-fix-open-prs
sops-migrate
issue-339-safe-first-on-imap-fetch
issue-340-try-catch-measure-height
issue-342-pin-intl-version
issue-341-guard-threademails-last
issue-335-agentloop-code-test
issue-329-fix
issue-315-fix
issue-320-fix
issue-325-fix
issue-312-fix
issue-311-fix
issue-305-fix
issue-304-fix
issue-299-fix
issue-300-fix
issue-298-fix
issue-296-fix
issue-294-fix
issue-289-fix
issue-288-fix
issue-287-fix
issue-286-fix
issue-277-fix
issue-282-fix
issue-280-fix
issue-272-fix
issue-268-fix
issue-267-fix
issue-266-fix
issue-258-fix
issue-260-fix
issue-257-fix
issue-253-fix
issue-216-fix
issue-251-fix
issue-249-fix
issue-question-fixes
issue-235-fix
issue-236-fix-v2
issue-237-fix
issue-236-fix
issue-228-fix
issue-217-fix
issue-214-fix
issue-213-fix
issue-208-fix
issue-205-fix
issue-204-fix
issue-203-fix
issue-202-fix
issue-129-fix
issue-161-fix
issue-160-fix
issue-201-fix
issue-210-fix
issue-198-fix
issue-200-fix
issue-144-fix
issue-199-fix
fix/playstore-upload-use-requests
issue-193-fix
issue-186-fix
issue-185-fix
issue-192-fix
issue-183-fix
issue-175-fix
issue-172-fix
issue-171-fix
issue-167-fix
issue-136-fix
issue-162-fix
issue-179-fix
issue-155-fix
issue-154-fix
issue-152-fix
issue-151-fix
issue-141-fix
issue-150-fix
issue-164-fix
migrate-to-dagger
task/d1-ci-matrix
task/a4-typeconverter-json
task/u7-onboarding-walkthrough
task/d3-sync-doc
task/a5-layer-boundary-lint
task/t5-golden-tests
task/p5-date-cache
task/s4-link-handling
task/p3-html-parse-isolate
task/u8-mark-all-read
task/u3-recent-searches
task/a3-jmap-injectable-http-client
task/r5-tls-error-handling
fix/playstore-redirect-retry
task/t3-repository-contract-tests
task/p2-email-list-pagination
task/p1-fts5-search
fix/playstore-upload-timeout
task/a1-email-detail-notifier
fix/upgrade-workmanager-0.9
fix/android-core-library-desugaring
task/p4-db-indexes
task/r3-html-error-boundary
task/d2-check-coverage
task/a2-email-tile
task/t4-migration-tests
task/t2-widget-tests
task/t1-email-repo-coverage
task/u6-connection-status
task/u4-push-notifications
task/u2-draft-sync
task/u1-list-unsubscribe
task/s2-hostname-validation
task/r6-reliability-fuzz-tests
task/r4-sync-error-banner
task/r2-force-resync
task/r1-undo-history-persistence
No results found.
Labels
Clear labels
NeedSupervisor
State/InProgress
State/Later
State/Planned
automerge
ci-failure
do-not-merge
loop/code
loop/code-ci-pending
loop/code-done
loop/code-in-process
loop/merge
loop/merge-done
loop/merge-in-process
loop/plan
loop/plan-done
loop/plan-in-process
Issue escalated to a human supervisor; agentloop will skip it until cleared.
Eligible for automatic merge by CI
Issue opened by agentloop to track a failing CI workflow; used for deduplication.
Plan PR — review only, do not merge.
Add to run the built-in "code" prompt; override at prompts/code.md.
Prompt "code" finished; waiting for the PR's CI to pass before advancing.
Prompt "code" finished successfully.
Agent for the "code" prompt is currently running on this issue.
Managed by agentloop
Managed by agentloop
Managed by agentloop
Add to run the built-in "plan" prompt; override at prompts/plan.md.
Prompt "plan" finished successfully.
Agent for the "plan" prompt is currently running on this issue.
No labels
State/Planned
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: guettli/sharedinbox#313
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
My intention was that the agent loop does planning concurrently (issue with label ToPlan), but issues which need to code changes are done one after the other.
This does not seem to be the case.
See
https://codeberg.org/guettli/sharedinbox/pulls/307
Research why merge conflict happens in above case
Create plan how to fix agent loop, so merge conflicts are avoided.
Implementation Plan: Preventing Merge Conflicts in the Agent Loop
Root Cause Analysis
PR #307 shows merge conflicts in 10 files (db_schema_version.dart, user_preferences.dart, etc.). After reading
scripts/agent_loop.pyin detail, there are two independent root causes:Root Cause 1: Multiple concurrent open
issue-N-fixPRsThe loop can end up with two or more open issue branches simultaneously. Here is one concrete path:
MAX_AGENT_AGE_SECONDSguard).State/Questionand clears the state file — but the PR forissue-N-fixstays open.State/Questionand skips it. The loop then falls through to the "Find a Ready issue" block and starts an agent for issue N+1.issue-(N+1)-fixbranching from the same version of main as issue N's branch.db_schema_versionfrom 34 → 35).The same thing happens after a server restart/state-file loss: the catch-up loop scans open PRs and tries to merge them in order, but all of them were created from the same base commit on main.
Root Cause 2: No freshness check before merging
When CI passes on an issue PR, the loop immediately calls
_merge_pr()without checking whether the branch is still up-to-date with main. In the meantime, main may have advanced (via Renovate PRs, direct CI-fix pushes, or other merged issues). For files where both main and the branch made an identical change (incrementing a version counter from the same base value), git cannot auto-resolve the conflict.The existing
_handle_pr_still_open_after_mergehelper already spawns a rebase agent when it detectsmergeable == Falseafter a failed merge — but this is reactive, not proactive. A cleaner fix is to check mergeability before attempting the merge.What Does NOT Need Changing
The planning agent path (State/ToPlan →
planagent) does not create branches or modify code, so it cannot cause merge conflicts by itself. Making planning concurrent is a separate enhancement (not a bug fix) and should be a separate issue.Files to Change
Only
scripts/agent_loop.pyneeds to change. No other files are involved.Proposed Changes
Change 1 — Block new implementation agents while an active issue PR is open
Insert a guard at the bottom of
_run_loop(), just before the "Find a Ready issue" block (currently around line 1065):This ensures that at most one implementation branch is alive at a time (unless it is explicitly stuck in
State/Question, in which case we let the next issue proceed so the loop doesn't stall forever).Change 2 — Check PR mergeability before attempting to merge
Add a helper:
Then, in both the "CI passed — merge" path (section 2b, ~line 788) and the catch-up loop (section 2c, ~line 862), add before
_merge_pr():This reuses the existing rebase-agent pattern from
_handle_pr_still_open_after_mergebut triggers it before the merge attempt rather than after.Change 3 — Close or comment on orphaned PRs when an agent is killed
When the timeout guard kills an agent (section 1, ~line 663), the PR it created is left open but the issue is set to
State/Question. This PR becomes an orphan. Add after the kill-and-clear block:This does not automatically close the PR (a human might want to salvage partial work), but it makes the situation visible and prevents the human from being confused about why the PR is sitting open.
Risks and Open Questions
Change 1 can stall if a PR is permanently stuck (e.g., CI is always failing on the branch and no human intervenes). Mitigation: The guard only applies to non-
State/QuestionPRs. If a CI-failing PR is manually set toState/Questionby a human, the loop will skip it and continue. Document this as an escape hatch.Forgejo's
mergeablefield can benullwhile it is still computing (this is the standard Forgejo/GitHub behavior). Change 2 handles this by waiting one tick and retrying. However, if Forgejo never computes it (a rare API bug), the loop would spin forever. Mitigation: count the number of consecutivenullresponses and fall through to the optimistic merge after N retries (e.g., 3 ticks = 30 minutes).Rebase agent may not have the right git identity set up in
~/sharedinbox. The existing_start_agentprompt should include instructions to configure git identity before rebasing if needed.The concurrent-planning enhancement the issue author mentions ("planning concurrently") is NOT addressed here. The current loop runs one agent at a time (including planners). Making planners truly concurrent (e.g., while an implementation CI is pending) requires tracking multiple concurrent agent PIDs, which is a larger architectural change. Recommend opening a separate issue for that.
Implementation Order
Each change can be reviewed and merged independently.
Planning complete. To resume this session: