diff --git a/.github/workflows/ai-review-claude.yml b/.github/workflows/ai-review-claude.yml index 1924809..e3e8b96 100644 --- a/.github/workflows/ai-review-claude.yml +++ b/.github/workflows/ai-review-claude.yml @@ -16,7 +16,7 @@ on: description: 'Maximum agentic turns' required: false type: number - default: 30 + default: 35 prompt: description: 'Custom review prompt (overrides prompt file if provided)' required: false @@ -104,8 +104,11 @@ jobs: Output your review findings in markdown format. Do NOT post a PR comment yourself - just output the review content. + # always(): a run that hits --max-turns fails the step above, and without + # this the review it already wrote is thrown away and the PR just gets a + # red check with no comment. - name: Post review comment - if: steps.claude-review.outputs.execution_file != '' + if: always() && steps.claude-review.outputs.execution_file != '' uses: actions/github-script@v7 env: EXECUTION_FILE: ${{ steps.claude-review.outputs.execution_file }} @@ -127,13 +130,27 @@ jobs: const data = JSON.parse(fs.readFileSync(execFile, 'utf8')); // Find the result message (last message with type "result") const resultMsg = data.filter(m => m.type === 'result').pop(); - const review = resultMsg?.result || ''; + let review = resultMsg?.result || ''; + let note = ''; + + // A failed run (error_max_turns, API error) carries no `result`, so + // fall back to the last thing Claude wrote and flag it as partial. + if (review.trim() === '') { + const texts = data + .filter(m => m.type === 'assistant') + .flatMap(m => (m.message?.content || []) + .filter(c => c.type === 'text') + .map(c => c.text || '')); + review = texts.pop() || ''; + const reason = resultMsg?.errors?.join('; ') || resultMsg?.subtype || 'run did not finish'; + note = `\n\n> Partial review: ${reason}.`; + } if (review && review.trim() !== '') { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request?.number || context.payload.issue?.number, - body: `## 🤖 Claude Code Review\n\n${review}\n\n---\n*Automated review by Claude (Anthropic) · ${model}${customPrompt}*` + body: `## 🤖 Claude Code Review\n\n${review}${note}\n\n---\n*Automated review by Claude (Anthropic) · ${model}${customPrompt}*` }); } diff --git a/.github/workflows/ai-review-codex.yml b/.github/workflows/ai-review-codex.yml index b765512..60090fd 100644 --- a/.github/workflows/ai-review-codex.yml +++ b/.github/workflows/ai-review-codex.yml @@ -16,7 +16,7 @@ on: description: 'Codex model to use' required: false type: string - default: 'gpt-5.4' + default: 'gpt-5.6-terra' safety_strategy: description: 'Codex safety strategy (drop-sudo, permissive, etc.)' required: false @@ -49,6 +49,18 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || format('refs/pull/{0}/head', github.event.issue.number) }} + # Codex runs with --sandbox workspace-write, which keeps .git read-only. + # checkout leaves LFS-tracked files as pointers, so in repos that use LFS + # every git command runs the LFS filters, which write to .git/lfs/tmp and + # fail with "read-only file system" (exit 128). Detach the filters here, + # outside the sandbox; the review only needs to read files. + - name: Detach git LFS filters + run: git lfs uninstall --local || true + + # Without it, codex logs a warning and falls back to its bundled copy. + - name: Install bubblewrap + run: sudo apt-get update -qq && sudo apt-get install -y -qq bubblewrap + - name: Load prompt id: prompt run: | diff --git a/README.md b/README.md index 5dd6827..7ad4625 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Automated PR code review using various AI providers. Each workflow posts review | Workflow | Provider | Model | |----------|----------|-------| | `ai-review-kimi.yml` | [Moonshot AI](https://platform.moonshot.ai/) | kimi-k2.5 | -| `ai-review-codex.yml` | [OpenAI](https://platform.openai.com/) | gpt-5.4 | +| `ai-review-codex.yml` | [OpenAI](https://platform.openai.com/) | gpt-5.6-terra | | `ai-review-claude.yml` | [Anthropic](https://console.anthropic.com/) | sonnet | ### Quick Start @@ -104,7 +104,7 @@ jobs: secrets: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} with: - model: 'gpt-5.4' # Codex model + model: 'gpt-5.6-terra' # Codex model safety_strategy: 'drop-sudo' # Codex safety strategy prompt: '' # Custom prompt (overrides prompt file) ``` @@ -119,7 +119,7 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} with: model: 'sonnet' # sonnet, opus, or haiku - max_turns: 30 # Max agentic turns + max_turns: 35 # Max agentic turns allowed_tools: '...' # Allowed Claude tools prompt: '' # Custom prompt (overrides prompt file) ```