diff --git a/.agents/skills/pr-fixup/SKILL.md b/.agents/skills/pr-fixup/SKILL.md index 9058395e39..115dee6c1c 100644 --- a/.agents/skills/pr-fixup/SKILL.md +++ b/.agents/skills/pr-fixup/SKILL.md @@ -30,7 +30,7 @@ The first thing you do — before fetching PR state, before reading logs, before Create these tasks immediately (use your task/todo tracking tool if available): -1. **Gather PR state** — Use `pr-poller` when available; otherwise gather compact CI + bot review state via `scripts/pr-state` +1. **Gather PR state** — Use `pr-poller` when available; otherwise gather compact CI + bot review state via `scripts/pr-state`; always check PR mergeability and local conflict state 2. **Fix failing CI checks** — Read failing run logs (via `scripts/run-quiet gh-run -- gh run view ...`), fix issues, run E2E tests locally if needed 3. **Triage review comments** — Classify each comment as valid, already addressed, nitpick, or wrong 4. **Address each comment** — Fix or reply with reasoning, resolve threads @@ -54,12 +54,18 @@ If available, invoke the `pr-poller` subagent with the PR number (or let it reso - Counts unresolved review threads and bot issue comments - Returns a structured report between `=== pr-poller report ===` and `=== end ===` markers +### Merge-conflict gate + +Always check for merge conflicts during task 1 before acting on any clean-poller shortcut. A PR can be blocked by conflicts before any check fails. + +Inspect GitHub's `mergeable` / `mergeStateStatus` fields and the local index (`git ls-files -u`) before fixing CI or review comments. If GitHub reports file-level conflicts, the local index is unmerged, or conflict markers exist in tracked source files, load `references/merge-conflicts.md` and resolve the conflicts first. Do not start a new merge/rebase while the index is already unmerged, and do not discard unrelated user changes to make conflict resolution easier. + **Parse the report.** The fields you care about: - `ci_failed` — list of `{name, run_id, conclusion, url}`. Empty list ⇒ CI is green. - `ci_pending` — anything still running when the 20-min cap hit. Decide whether to re-invoke `pr-poller` after a short delay, or proceed with what you have and re-check at step 6. - `bots.` — `done` / `rate_limited` / `pending` / `timeout`. Anything in `done` or `rate_limited` has had its chance; treat the rest as missing data, not a blocker. -- `unresolved_review_threads` and `issue_comments_from_bots` — drive steps 3-4. If both are 0 and `ci_failed` is empty, skip to step 5 (still run verify + push if you have fixes from earlier). +- `unresolved_review_threads` and `issue_comments_from_bots` — drive steps 3-4. If both are 0, `ci_failed` is empty, and the merge-conflict gate is clean, skip to step 5 (still run verify + push if you have fixes from earlier). **E2E CI outlasts the poller.** The pr-poller caps at ~20 minutes. Standard E2E shards plus container shards often run longer. GitHub can expand E2E matrix jobs late, and the shard matrix may appear only after the build job finishes; if pending checks briefly drop near zero and then jump when E2E shards appear, keep treating that as normal pending CI unless a shard reports failure. If the report shows `ci_pending` with only E2E/lint jobs and `ci_failed` is empty, re-invoke pr-poller once those jobs finish — do not spin a manual `gh pr checks` loop in the parent. If the cap hits with E2E still pending, report "CI in progress" to the user instead of blocking, and include the exact pending shard names from `ci_pending`. diff --git a/.agents/skills/pr-fixup/references/merge-conflicts.md b/.agents/skills/pr-fixup/references/merge-conflicts.md new file mode 100644 index 0000000000..6694c233b7 --- /dev/null +++ b/.agents/skills/pr-fixup/references/merge-conflicts.md @@ -0,0 +1,74 @@ +# Merge Conflicts + +Use this when the PR fixup flow finds GitHub file-level conflicts, an unmerged local index, or conflict markers in tracked files. + +## Detect + +Inspect GitHub's mergeability state: + +```bash +gh pr view --json number,url,baseRefName,headRefName,mergeable,mergeStateStatus +``` + +Treat `mergeable:"CONFLICTING"` or `mergeStateStatus:"DIRTY"` as an actionable merge-conflict blocker. Treat `mergeable:"UNKNOWN"` as inconclusive: wait one short cadence and query again before deciding. States such as `BEHIND`, `BLOCKED`, `UNSTABLE`, or `HAS_HOOKS` may require an update or more CI/review work, but they are not by themselves proof of file-level conflicts. + +Inspect the local worktree: + +```bash +git status --short +git ls-files -u +git grep -n -E '^(<<<<<<<|>>>>>>>)' +``` + +If `git ls-files -u` prints entries, or conflict markers are present in tracked source files, resolve those conflicts before fixing CI or review comments. `git grep` scans only tracked files, and intentionally checks only the unambiguous start/end markers so Markdown setext headings do not create false positives. Do not start a new merge/rebase while the index is already unmerged. + +## Resolve + +### When GitHub reports conflicts and the local index is clean + +1. Fetch the latest base branch: + ```bash + git fetch origin + ``` +2. Prefer merging `origin/` into the PR branch for conflict-fixup work: + ```bash + git merge --no-edit origin/ + ``` + Use `git rebase origin/` only when the branch already uses a rebase-style history or the user asks for it. If a rebase is used and succeeds, the push later may need `git push --force-with-lease`. +3. If conflicts appear, inspect each conflicted file, preserve the intended behavior from both sides, remove all conflict markers, and stage only the resolved files. +4. Confirm the conflict is gone before continuing: + ```bash + git ls-files -u + git grep -n -E '^(<<<<<<<|>>>>>>>)' + git diff --check + git diff --cached --check + ``` + +### When the local index is already unmerged + +If `git ls-files -u` shows entries, a previous merge or rebase was left incomplete. Do not start another merge. Instead: + +1. Inspect each conflicted file: + ```bash + git diff --diff-filter=U + ``` +2. Resolve conflict markers manually, preserving the intended behavior from both sides. +3. Stage each resolved file: + ```bash + git add + ``` +4. Confirm the conflict is gone before continuing: + ```bash + git ls-files -u + git grep -n -E '^(<<<<<<<|>>>>>>>)' + git diff --check + git diff --cached --check + ``` +5. Complete the interrupted operation: + ```bash + git commit + # or, if mid-rebase: + git rebase --continue + ``` + +Do not discard unrelated user changes to make a merge/rebase easier. If unrelated dirty files block the conflict-resolution attempt, stop and ask before stashing, committing, or reverting them.