From 6e6c616cdfaf4726113c0e031bc389d4a472110a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 19:02:49 +0000 Subject: [PATCH 1/2] =?UTF-8?q?queue:=20repair=20the=20automation=20?= =?UTF-8?q?=E2=80=94=20filing=20gate,=20push=20retry,=20silent=20failures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three faults kept #69/#71/#72 open since 2026-08-28: - queue_filing.yml never checked PyAutoBrain out beside the repo, so the gate's pytest (and Claude's own in-prompt gate) died on 30 tests/test_board.py theme-import errors on every run since dcd1e2c (2026-08-24). Add the sibling checkout, matching validate.yml, before the Claude step. - queue_actions.yml's push retry rebased a stale commit and discarded the action on conflict (#69). Rebuild instead: fetch, reset --hard to the moved main, re-run the idempotent action script, re-commit. - queue_actions.yml never reported its own failures (the close step is implicitly if: success()). Add an if: failure() report step, same shape as queue_filing.yml's. Closes #75. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01C4so7jVxLUyBjfVsxnLNom --- .github/workflows/queue_actions.yml | 50 +++++++++++++++++++++-------- .github/workflows/queue_filing.yml | 13 ++++++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/.github/workflows/queue_actions.yml b/.github/workflows/queue_actions.yml index 6456621..7bd5a03 100644 --- a/.github/workflows/queue_actions.yml +++ b/.github/workflows/queue_actions.yml @@ -70,32 +70,30 @@ jobs: ISSUE_BODY: ${{ github.event.issue.body }} run: | printf '%s' "$ISSUE_BODY" > /tmp/issue_body.txt + # The label picks the script; the push retry below re-runs the same + # command on a moved main, so the mapping is stated once and the + # command travels as a step output. case ",$LABELS," in *,queue-add,*) action=add - status=$(python scripts/inbox_actions.py add \ - --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/inbox_actions.py add" ;; *,queue-dismiss,*) action=dismiss - status=$(python scripts/inbox_actions.py dismiss \ - --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/inbox_actions.py dismiss" ;; *,interests-add,*) action=interests-add - status=$(python scripts/interests_actions.py add \ - --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/interests_actions.py add" ;; *,interests-dismiss,*) action=interests-dismiss - status=$(python scripts/interests_actions.py dismiss \ - --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/interests_actions.py dismiss" ;; *,interests-clear,*) action=interests-clear - status=$(python scripts/interests_actions.py clear \ - --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/interests_actions.py clear" ;; *) action=read - status=$(python scripts/queue_mark_done.py \ - --queue reading-queue.md --body-file /tmp/issue_body.txt) || true ;; + cmd="scripts/queue_mark_done.py --queue reading-queue.md" ;; esac + status=$(python $cmd --body-file /tmp/issue_body.txt) || true # A status the scripts do not define means they died before printing; # normalise so the close step reports rather than mis-closing. case "$status" in @@ -104,6 +102,7 @@ jobs: *) status="${status:-error}" ;; esac echo "action=${action}" >> "$GITHUB_OUTPUT" + echo "cmd=${cmd}" >> "$GITHUB_OUTPUT" echo "status=${status}" >> "$GITHUB_OUTPUT" echo "queue action ${action}: ${status}" # Did anything actually change on disk? Cheaper and more honest than @@ -118,6 +117,7 @@ jobs: if: steps.mark.outputs.changed == 'yes' env: ACTION: ${{ steps.mark.outputs.action }} + CMD: ${{ steps.mark.outputs.cmd }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" @@ -131,9 +131,23 @@ jobs: esac git add reading-queue.md arxiv-inbox.md arxiv-interests.md git commit -m "${msg} (#${ISSUE}) [skip ci]" + # A rejected push means a sibling run landed a commit first. + # Rebasing the stale commit conflicts on adjacent queue lines and + # used to discard the action entirely (#69) — rebuild instead: + # reset to the moved tip and re-run the action script, which is + # idempotent (already-there / already-gone / already-done). for attempt in 1 2 3; do if git push; then exit 0; fi - git pull --rebase origin main || exit 1 + [ "$attempt" = 3 ] && break + git fetch origin main + git reset --hard origin/main + python $CMD --body-file /tmp/issue_body.txt || true + if [ -z "$(git status --porcelain -- reading-queue.md arxiv-inbox.md arxiv-interests.md)" ]; then + echo "change already on main after the concurrent commit — nothing left to push" + exit 0 + fi + git add reading-queue.md arxiv-inbox.md arxiv-interests.md + git commit -m "${msg} (#${ISSUE}) [skip ci]" done echo "::error::could not push the queue commit after 3 attempts" exit 1 @@ -185,3 +199,13 @@ jobs: gh issue comment "$ISSUE" --body \ "Could not process this queue action (status: ${STATUS:-error}) — the paper's line was not found. Handle manually; leaving the issue open." ;; esac + + # The close step above is implicitly `if: success()`, so a died push + # used to leave the issue open and completely silent (#69 read as + # "never swept up" rather than "failed"). Same shape as + # queue_filing.yml's failure report. + - name: Report failure on the issue + if: failure() + run: | + gh issue comment "$ISSUE" --body \ + "Queue action failed — see the workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. The issue stays open; handle manually if needed." || true diff --git a/.github/workflows/queue_filing.yml b/.github/workflows/queue_filing.yml index 7af34a9..5ea3cf0 100644 --- a/.github/workflows/queue_filing.yml +++ b/.github/workflows/queue_filing.yml @@ -65,6 +65,19 @@ jobs: - uses: actions/checkout@v4 + # The board tests import the family look from the Brain + # (PyAutoBrain/board/_theme.py — moved there in dcd1e2c, which added + # this step to validate.yml and knowledge_board.yml but not here). + # Without it the gate's pytest — and Claude's own in-prompt gate, which + # is told to fix its own mistakes until green but cannot fix a missing + # checkout — dies on 30 tests/test_board.py errors after the filing + # work is already done. So it lands before the Claude step, not just + # before the gate. + - uses: actions/checkout@v4 + with: + repository: PyAutoLabs/PyAutoBrain + path: PyAutoBrain + - uses: actions/setup-python@v5 with: python-version: "3.12" From b5bc248d79a6154d796b3e968db4ab01c86ec1e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 19:10:56 +0000 Subject: [PATCH 2/2] queue: gate the retry's clean-tree exit on an idempotent status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adversarial-review finding: after reset --hard, a re-apply that errors (no-section, a crash swallowed by || true) also leaves a clean tree, and the retry would exit 0 — closing the issue on attempt 1's stale status. Accept the clean tree as "already landed" only when the re-run returned one of the scripts' idempotent statuses, and surface what actually landed to the close step (steps.push.outputs.status wins over attempt 1's). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01C4so7jVxLUyBjfVsxnLNom --- .github/workflows/queue_actions.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/queue_actions.yml b/.github/workflows/queue_actions.yml index 7bd5a03..f655997 100644 --- a/.github/workflows/queue_actions.yml +++ b/.github/workflows/queue_actions.yml @@ -114,6 +114,7 @@ jobs: fi - name: Commit + push (explicit paths only) + id: push if: steps.mark.outputs.changed == 'yes' env: ACTION: ${{ steps.mark.outputs.action }} @@ -141,7 +142,20 @@ jobs: [ "$attempt" = 3 ] && break git fetch origin main git reset --hard origin/main - python $CMD --body-file /tmp/issue_body.txt || true + retry_status=$(python $CMD --body-file /tmp/issue_body.txt) || true + # A clean tree only means "done" if the re-run actually succeeded: + # an error status (no-section, a crash) also leaves no diff, and + # exiting 0 on it would close the issue on the first run's stale + # status — the same silent-loss class this retry exists to fix. + case "$retry_status" in + marked|already-done|added|already-there|dismissed|already-gone|already-clear|cleared:*) ;; + *) + echo "::error::re-apply on the moved main returned '${retry_status:-error}'" + exit 1 ;; + esac + # The close step reports what actually landed, not attempt 1's + # status (last write wins). + echo "status=${retry_status}" >> "$GITHUB_OUTPUT" if [ -z "$(git status --porcelain -- reading-queue.md arxiv-inbox.md arxiv-interests.md)" ]; then echo "change already on main after the concurrent commit — nothing left to push" exit 0 @@ -158,7 +172,7 @@ jobs: - name: Close or report on the issue env: - STATUS: ${{ steps.mark.outputs.status }} + STATUS: ${{ steps.push.outputs.status || steps.mark.outputs.status }} ACTION: ${{ steps.mark.outputs.action }} run: | # Two suggestion tiers share these statuses, so the comment names