From 763b4b0d6db4aeec85a726a73d1eb370bb190efc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 03:50:18 +0000 Subject: [PATCH] ci: add loop observability + mirror inspection tools to implement agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two hardening changes ahead of stress-testing the loop on a real bug: 1. Mirror the read-only inspection utilities (rg/grep/cat/sed/ls/head/tail/ wc/find) that #239 added to the fix agent into the implement agent's allowlist. The implement agent has the same latent gap that thrashed the sonnet fixer (42 denials); it has coped on sonnet-5 + 150 turns + native Read/Grep tools, but a heavy issue could hit the same wall. Cheap insurance, all read-only. 2. Upload the action's execution-output.json as an artifact (if: always()) from both the implement and fix jobs. That file carries the full turn-by-turn stream — every tool call AND every permission denial — whereas the Actions log only streams init + final result. When the sonnet fixer thrashed we could see '42 denials' but not WHICH tools; this makes the next failure diagnosable instead of guesswork. Reuses the repo's existing pinned actions/upload-artifact and never fails the job (if-no-files-found: ignore). Models are deliberately left as-is: sonnet-5 implement, opus deep review, opus fix — the heterogeneity decorrelates implementer and reviewer blind spots. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NVR5yWevceZEFbTJmpviiM --- .github/workflows/claude-implement.yml | 16 +++++++++++++++- .github/workflows/claude-pr-loop.yml | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-implement.yml b/.github/workflows/claude-implement.yml index 35542b4b5..4923a09f4 100644 --- a/.github/workflows/claude-implement.yml +++ b/.github/workflows/claude-implement.yml @@ -106,7 +106,7 @@ jobs: claude_args: | --max-turns 150 --model claude-sonnet-5 - --allowedTools "Bash(pnpm install:*),Bash(pnpm run:*),Bash(pnpm test:*),Bash(pnpm exec:*),Bash(pnpm --filter:*),Bash(pnpm ls:*),Bash(pnpm why:*),Bash(node:*),Bash(git status:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git checkout:*),Bash(git switch:*),Bash(git branch:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git config:*),Bash(mkdir:*),Bash(gh issue view:*),Bash(gh issue comment:*),Bash(gh pr create:*),Bash(gh pr view:*),Bash(gh pr edit:*),Bash(gh pr diff:*),Bash(gh pr checks:*)" + --allowedTools "Bash(pnpm install:*),Bash(pnpm run:*),Bash(pnpm test:*),Bash(pnpm exec:*),Bash(pnpm --filter:*),Bash(pnpm ls:*),Bash(pnpm why:*),Bash(node:*),Bash(rg:*),Bash(grep:*),Bash(cat:*),Bash(sed:*),Bash(ls:*),Bash(head:*),Bash(tail:*),Bash(wc:*),Bash(find:*),Bash(git status:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git checkout:*),Bash(git switch:*),Bash(git branch:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git config:*),Bash(mkdir:*),Bash(gh issue view:*),Bash(gh issue comment:*),Bash(gh pr create:*),Bash(gh pr view:*),Bash(gh pr edit:*),Bash(gh pr diff:*),Bash(gh pr checks:*)" prompt: | REPO: ${{ github.repository }} ISSUE NUMBER: ${{ github.event.issue.number }} @@ -174,6 +174,20 @@ jobs: to main, and never write "@claude" or "@coderabbitai" in any comment, commit message, or PR text. + # Observability: the action writes a full turn-by-turn stream (every + # tool call, and every permission denial) to this file. Upload it so a + # failed/thrashing run is diagnosable after the fact — the Actions log + # itself only carries the init + final result. `always()` so it is kept + # even when the Claude step fails (exactly when it is needed). + - name: Upload Claude execution log + if: always() && steps.perm.outputs.allowed == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: claude-implement-log-${{ github.event.issue.number }} + path: ${{ runner.temp }}/claude-execution-output.json + if-no-files-found: ignore + retention-days: 14 + # Optional second reviewer (repo variable AI_LOOP_COPILOT=true). # Copilot's automatic-review ruleset skips bot-authored PRs on personal # repos, so the review must be requested explicitly. diff --git a/.github/workflows/claude-pr-loop.yml b/.github/workflows/claude-pr-loop.yml index 2786bac99..c1fcdfb9e 100644 --- a/.github/workflows/claude-pr-loop.yml +++ b/.github/workflows/claude-pr-loop.yml @@ -421,6 +421,20 @@ jobs: (every remaining thread is refuted), say so in the summary comment and stop — do not make cosmetic commits. + # Observability: the action writes a full turn-by-turn stream (every + # tool call, and every permission denial) to this file. Upload it so a + # failed/thrashing fix pass is diagnosable after the fact — the Actions + # log itself only carries the init + final result. `always()` so it is + # kept even when the fix step fails (exactly when it is needed). + - name: Upload Claude execution log + if: always() && steps.claude.conclusion != 'skipped' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: claude-fix-log-pr${{ needs.gate.outputs.pr }}-iter${{ needs.gate.outputs.iteration }} + path: ${{ runner.temp }}/claude-execution-output.json + if-no-files-found: ignore + retention-days: 14 + # No push + threads still open would stall the loop forever (no event # fires). End in a named state instead. - name: Terminal state check