Просмотр исходного кода

fix(ci): repair the review comment and the conflict-resolution guard

Two failures from the same afternoon, both in the bot workflow.

The review of #6272 ran for 34 minutes across four subagents and posted
"No issues found. Checked for bugs and CLAUDE.md compliance." — three
lines for a 73-file diff. The agent had written a per-area coverage
summary in its own last turn and then dropped it on the floor, because
the code-review skill's comment template carries findings and nothing
else. A comment that cannot distinguish a thorough clean review from a
run that died early is not evidence, so REVIEW.md now states what the
posted comment must show and the system prompt points the run at it.

The same run logged 67 permission denials. Only the inline-comment MCP
tool was named in --allowedTools, so `gh api`, writing the diff to a
scratch file, and reading it back were all auto-denied: agents spent
turns hunting for a writable directory, and the openapi.json copy check
REVIEW.md calls blocking could not be run at all ("gh api was
unavailable in this sandbox"). Name the tools the review actually uses.

The conflict resolution on #6243 resolved both conflicted files
correctly and was then rejected by its own guard: "Edits outside the
conflicted set: CLAUDE.md". The agent never touched CLAUDE.md — it had
Edit rights on exactly two paths and no shell. claude-code-action
deletes and restores CLAUDE.md, .claude/, .mcp.json and friends from
the base branch before it runs, because the PR head is untrusted, and
that restore is what dirtied the tree. Name that set once, exclude it
from the stray-edit check, and hand back rather than resolve when a
conflict lands inside it — the restore would silently overwrite the
resolution and stage the base copy.
Sanaei 4 часов назад
Родитель
Сommit
19a2c23c01
2 измененных файлов с 35 добавлено и 3 удалено
  1. 24 3
      .github/workflows/claude-bot.yml
  2. 11 0
      REVIEW.md

+ 24 - 3
.github/workflows/claude-bot.yml

@@ -506,8 +506,8 @@ jobs:
             --model claude-opus-5
             --model claude-opus-5
             --effort xhigh
             --effort xhigh
             --max-turns 100
             --max-turns 100
-            --allowedTools "mcp__github_inline_comment__create_inline_comment"
-            --append-system-prompt "Before reviewing, read REVIEW.md at the repository root and follow it: it defines what counts as a blocking finding in this repository, what not to report, and the repo-specific checks. Two overrides apply here. First, the skip gate for already-reviewed PRs: an existing Claude review comment justifies skipping ONLY when its 'Reviewed head:' SHA equals the PR's current head SHA; when the head has moved on, or this run was triggered by an explicit '@claude review' comment, run the full review, focusing on the commits since the previously reviewed head. Second, this is a headless run that terminates the moment you end your turn: launch every subagent with run_in_background set to false and wait for its result inside the same turn - never end your turn while a subagent is still running, and never end it before the review comment is posted. A run that ends without posting the review has failed."
+            --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh api:*),Bash(gh pr diff:*),Read(//tmp/**),Write(//tmp/**),Edit(//tmp/**)"
+            --append-system-prompt "Before reviewing, read REVIEW.md at the repository root and follow it: it defines what counts as a blocking finding in this repository, what not to report, and the repo-specific checks. Three overrides apply here. First, the skip gate for already-reviewed PRs: an existing Claude review comment justifies skipping ONLY when its 'Reviewed head:' SHA equals the PR's current head SHA; when the head has moved on, or this run was triggered by an explicit '@claude review' comment, run the full review, focusing on the commits since the previously reviewed head. Second, this is a headless run that terminates the moment you end your turn: launch every subagent with run_in_background set to false and wait for its result inside the same turn - never end your turn while a subagent is still running, and never end it before the review comment is posted. A run that ends without posting the review has failed. Third, the comment you post is the only part of this run anyone can see: it must carry the coverage list REVIEW.md asks for, whether or not you found anything."
       - name: Upload the run transcript
       - name: Upload the run transcript
         if: always()
         if: always()
         env:
         env:
@@ -656,6 +656,10 @@ jobs:
   resolve-conflicts:
   resolve-conflicts:
     if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, 'resolve pr conflicts') && github.event.comment.user.login == github.repository_owner && github.event.comment.author_association == 'OWNER'
     if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, 'resolve pr conflicts') && github.event.comment.user.login == github.repository_owner && github.event.comment.author_association == 'OWNER'
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
+    # claude-code-action replaces these with the base branch's copies before it
+    # runs, so a change to them is the action's doing, never the agent's.
+    env:
+      RESTORED_PATHS: ".claude .claude-pr .mcp.json .claude.json .gitmodules .ripgreprc CLAUDE.md CLAUDE.local.md .husky"
     concurrency:
     concurrency:
       group: claude-conflicts-${{ github.event.issue.number }}
       group: claude-conflicts-${{ github.event.issue.number }}
       cancel-in-progress: false
       cancel-in-progress: false
@@ -746,6 +750,18 @@ jobs:
             hand_back "The merge of \`${base}\` conflicts over paths this job refuses to hand to its tooling:
             hand_back "The merge of \`${base}\` conflicts over paths this job refuses to hand to its tooling:
           $(printf '%s\n' "$odd" | sed 's/^/- /')
           $(printf '%s\n' "$odd" | sed 's/^/- /')
 
 
+          Nothing was changed. Resolve those by hand."
+          fi
+          clobbered=$(printf '%s\n' "$files" | while IFS= read -r f; do
+            for p in $RESTORED_PATHS; do
+              case "$f" in "$p" | "$p"/*) printf '%s\n' "$f" ;; esac
+            done
+          done)
+          if [ -n "$clobbered" ]; then
+            git merge --abort 2>/dev/null || true
+            hand_back "The merge of \`${base}\` conflicts over paths the bot's own tooling replaces with the \`${base}\` copy before it runs, so a resolution there cannot survive:
+          $(printf '%s\n' "$clobbered" | sed 's/^/- /')
+
           Nothing was changed. Resolve those by hand."
           Nothing was changed. Resolve those by hand."
           fi
           fi
           rules=""
           rules=""
@@ -856,7 +872,12 @@ jobs:
           stray=""
           stray=""
           while IFS= read -r f; do
           while IFS= read -r f; do
             [ -z "$f" ] && continue
             [ -z "$f" ] && continue
-            if ! grep -qxF "$f" <<< "$FILES"; then
+            grep -qxF "$f" <<< "$FILES" && continue
+            restored=false
+            for p in $RESTORED_PATHS; do
+              case "$f" in "$p" | "$p"/*) restored=true ;; esac
+            done
+            if [ "$restored" = false ]; then
               stray="${stray} ${f}"
               stray="${stray} ${f}"
             fi
             fi
           done <<< "$(git diff --name-only)"
           done <<< "$(git diff --name-only)"

+ 11 - 0
REVIEW.md

@@ -73,3 +73,14 @@ Style, naming and refactoring suggestions are nits at most.
 Report at most five nits per review and say "plus N similar" in the summary
 Report at most five nits per review and say "plus N similar" in the summary
 for the rest. Lead the summary with "No blocking issues" when everything found
 for the rest. Lead the summary with "No blocking issues" when everything found
 is a nit. After the first review of a PR, report blocking findings only.
 is a nit. After the first review of a PR, report blocking findings only.
+
+## What the comment must show
+
+The posted comment is the only part of a review anyone sees, so a bare "no
+issues found" is a receipt, not a review: nothing in it says whether the diff
+was read or the run died early. Every comment therefore ends with a short
+coverage list — one line per area actually checked, naming what was examined
+and what it turned out to be, plus the head SHA and the size of the diff it
+covers. Say which claims could not be verified and why, including a check
+this environment blocked. Keep it under ten lines; it is evidence, not a
+retelling of the pull request.