feat: Auto-resolve pr_body.md conflicts before Codex runs#685
Conversation
- Add resolve-trivial-conflicts job to keepalive workflow - Auto-resolves pr_body.md and similar PR-specific files using --ours - Skips Codex invocation if all conflicts were trivial - Prevents wasted Codex rounds on predictable conflicts Files auto-resolved: - pr_body.md - ci/autofix/history.json - keepalive-metrics.ndjson
Automated Status SummaryHead SHA: fe24e46
Coverage Overview
Coverage Trend
Top Coverage Hotspots (lowest coverage)
Updated automatically; will refresh on subsequent CI/Docker completions. Keepalive checklistScopeNo scope information available Tasks
Acceptance criteria
|
🤖 Keepalive Loop StatusPR #685 | Agent: Codex | Iteration 0/5 Current State
🔍 Failure Classification| Error type | infrastructure | |
There was a problem hiding this comment.
Pull request overview
This PR introduces automated resolution of trivial merge conflicts for PR-specific files (like pr_body.md) before invoking Codex. The goal is to reduce unnecessary Codex invocations for predictable file conflicts that can be automatically resolved using the "ours" merge strategy.
Key changes:
- Added a new
resolve-trivial-conflictsjob that runs before Codex when conflicts are detected - Auto-resolves PR-specific files using
git checkout --oursstrategy - Modified
run-codexjob to skip execution when all conflicts were trivially resolved - Added standalone script
.github/scripts/auto_resolve_ignored_conflicts.shfor manual use
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| templates/consumer-repo/.github/workflows/agents-keepalive-loop.yml | Adds resolve-trivial-conflicts job and updates run-codex job dependencies and conditions |
| .github/scripts/auto_resolve_ignored_conflicts.sh | New standalone script for auto-resolving PR-specific file conflicts |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fi | ||
|
|
||
| # Files that should always keep the PR branch version | ||
| IGNORED_FILES=("pr_body.md" "ci/autofix/history.json" "keepalive-metrics.ndjson") |
There was a problem hiding this comment.
The file list here contains only 3 files, but the standalone script at .github/scripts/auto_resolve_ignored_conflicts.sh contains 6 files (including "coverage-trend-history.ndjson", "metrics-history.ndjson", and "residual-trend-history.ndjson"). These lists should be consistent to ensure the same behavior across different usage contexts.
| IGNORED_FILES=("pr_body.md" "ci/autofix/history.json" "keepalive-metrics.ndjson") | |
| IGNORED_FILES=("pr_body.md" "ci/autofix/history.json" "keepalive-metrics.ndjson" "coverage-trend-history.ndjson" "metrics-history.ndjson" "residual-trend-history.ndjson") |
| - resolve-trivial-conflicts | ||
| if: | | ||
| needs.evaluate.outputs.agent_type == 'codex' && | ||
| (needs.resolve-trivial-conflicts.result == 'skipped' || |
There was a problem hiding this comment.
The condition only checks for 'skipped' result, not 'failure'. If the resolve-trivial-conflicts job fails (e.g., git push fails at line 399), the run-codex job will not execute because the condition doesn't account for the 'failure' result. Consider updating the condition to also handle job failures, for example: needs.resolve-trivial-conflicts.result == 'skipped' || needs.resolve-trivial-conflicts.result == 'failure' || needs.resolve-trivial-conflicts.outputs.remaining_conflicts != '0'
| (needs.resolve-trivial-conflicts.result == 'skipped' || | |
| (needs.resolve-trivial-conflicts.result == 'skipped' || | |
| needs.resolve-trivial-conflicts.result == 'failure' || |
| echo "✓ Conflict resolution committed and pushed" | ||
| elif [ "$REMAINING" -gt 0 ]; then | ||
| echo "⚠ $REMAINING file(s) still have conflicts - Codex will handle these" | ||
| git merge --abort || true |
There was a problem hiding this comment.
When the merge fails but no unmerged files are detected by git diff --name-only --diff-filter=U (REMAINING=0, RESOLVED=0), neither the commit branch (line 397) nor the abort branch (line 401) executes. This leaves the repository in a conflicted merge state without cleanup. Consider adding an else clause to handle this edge case, likely by aborting the merge with git merge --abort.
| git merge --abort || true | |
| git merge --abort || true | |
| else | |
| echo "⚠ Merge did not complete successfully, but no conflicts were detected (resolved=$RESOLVED, remaining=$REMAINING). Aborting merge to clean up state." | |
| git merge --abort || true |
| git commit -m "fix: auto-resolve PR-specific file conflicts with $BASE_BRANCH | ||
|
|
||
| Files resolved using --ours strategy: | ||
| $(for f in "${IGNORED_CONFLICT_FILES[@]}"; do echo "- $f"; done) | ||
|
|
||
| These files are PR-specific and should not inherit content from the base branch." |
There was a problem hiding this comment.
The commit message lists all files in the IGNORED_CONFLICT_FILES array, regardless of which files actually had conflicts and were resolved. This could be misleading. Consider only listing the files that were actually resolved in this specific merge, similar to how the workflow inline script does it.
| if: ${{ vars.WORKFLOWS_APP_ID != '' }} | ||
| uses: actions/create-github-app-token@v2 | ||
| with: | ||
| app-id: ${{ vars.WORKFLOWS_APP_ID }} |
There was a problem hiding this comment.
The condition checks vars.WORKFLOWS_APP_ID but the preflight job at line 266 checks secrets.WORKFLOWS_APP_ID, and line 423 passes secrets.WORKFLOWS_APP_ID to the reusable workflow. This inconsistency could cause the app token generation to be skipped when WORKFLOWS_APP_ID is stored as a secret (the recommended approach per line 15 in the file header). Consider using secrets.WORKFLOWS_APP_ID here for consistency, or clarify if the App ID should be stored in vars vs secrets.
| if: ${{ vars.WORKFLOWS_APP_ID != '' }} | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ vars.WORKFLOWS_APP_ID }} | |
| if: ${{ secrets.WORKFLOWS_APP_ID != '' }} | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets.WORKFLOWS_APP_ID }} |
| if [ ${#REMAINING_CONFLICTS[@]} -eq 0 ]; then | ||
| # All conflicts resolved - commit | ||
| echo "All conflicts resolved. Committing..." | ||
| git commit -m "fix: auto-resolve PR-specific file conflicts with $BASE_BRANCH | ||
|
|
||
| Files resolved using --ours strategy: | ||
| $(for f in "${IGNORED_CONFLICT_FILES[@]}"; do echo "- $f"; done) | ||
|
|
||
| These files are PR-specific and should not inherit content from the base branch." | ||
| echo "✓ Merge conflict resolution committed" | ||
| exit 0 |
There was a problem hiding this comment.
When the merge fails but no unmerged files are found (RESOLVED_COUNT=0, REMAINING_CONFLICTS empty), the script attempts to commit at line 83. Since no files were staged, git commit will fail with "nothing to commit", and due to set -e at line 13, the script will exit with an error code. Consider checking if RESOLVED_COUNT > 0 before attempting to commit, or handling the edge case where a merge conflict is reported but no conflicted files are detected.
Automated Status Summary
Scope
After merging PR #103 (multi-agent routing infrastructure), we need to:
GITHUB_STEP_SUMMARYoutput so iteration results are visible in the Actions UIContext for Agent
Design Decisions & Constraints
<!-- keepalive-loop-summary -->| github-actions[bot] | NEW: CLI agent iteration tracking | ✅ Keep for CLI agents |<!-- keepalive-state:v1 -->| agents-workflows-bot[bot] | State tracking |<!-- keepalive-round: N -->| stranske | OLD: Instruction comment | ❌ CLI agents dont need this |agent:*label), we should have exactly one updating comment (<!-- keepalive-loop-summary -->) instead of accumulating 10+ comments per PR.Related Issues/PRs
References
Blockers & Dependencies
Tasks
Pipeline Validation
agent:codexlabelGITHUB_STEP_SUMMARY
agents-keepalive-loop.ymlafter agent runConditional Status Summary
buildStatusBlock()inagents_pr_meta_update_body.jsto acceptagentTypeparameteragentTypeis set (CLI agent): hide workflow table, hide head SHA/required checksComment Pattern Cleanup
agent:*label):<!-- gate-summary: -->comment posting (use step summary instead)<!-- keepalive-round: N -->instruction comments (task appendix replaces this)<!-- keepalive-loop-summary -->to be the single source of truthagent:*label):<!-- gate-summary: -->commentagent_typeoutput to detect job so downstream workflows know the modeagents-pr-meta.ymlto conditionally skip gate summary for CLI agent PRsAcceptance criteria
Dependencies
Head SHA: fd727c9
Latest Runs: ✅ success — Gate
Required: gate: ✅ success