From 91bf331cc6e83bfba3c74ff1b930a5b333acaaa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Fri, 26 Jun 2026 15:09:27 +0800 Subject: [PATCH] ci: isolate per-run agent state for triage and PR review (#5885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the triage job and the PR-review job run the qwen agent directly on the persistent self-hosted ECS pool with no per-run isolation. $HOME, /tmp and the workspace are reused between runs, so a prior run's agent session/memory (default ~/.qwen) or leftover draft comments (/tmp/stage-*.md, which survive git clean) can bleed into the next run. This surfaced on #5874: its triage posted #5872's review verbatim (wrong author, wrong approver, wrong diff), while the same run's internal stage actually exercised #5874 — the PR id was correct, the agent state was stale. Point QWEN_HOME at a per-run $RUNNER_TEMP/qwen-home on the Qwen step and reset it (plus /tmp/stage-*.md) in the pre-run cleanup, for both jobs. QWEN_HOME relocates the entire global qwen dir (storage.ts), so this isolates sessions/memory/temp without touching $HOME and disturbing git/npm. The tmux-testing job is already container-isolated and unchanged. Refs #5882 --- .github/workflows/qwen-code-pr-review.yml | 20 +++++++++++++++---- .github/workflows/qwen-triage.yml | 24 +++++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.github/workflows/qwen-code-pr-review.yml b/.github/workflows/qwen-code-pr-review.yml index cdd7834dcc..025efcbdf2 100644 --- a/.github/workflows/qwen-code-pr-review.yml +++ b/.github/workflows/qwen-code-pr-review.yml @@ -326,12 +326,20 @@ jobs: pull-requests: 'write' issues: 'write' steps: - # Self-hosted runners reuse the workspace, so an interrupted review can - # leave a stale `.qwen/tmp/review-pr-*` worktree or `qwen-review/*` branch - # that trips the checkout below. Prune defensively; never fail the job. - - name: 'Clean stale review worktrees' + # Self-hosted runners reuse $HOME, /tmp and the workspace, so a prior run + # can bleed into this one: its agent session/memory (under QWEN_HOME), + # leftover draft comments (/tmp/stage-*.md, which survive `git clean`), or + # a stale `.qwen/tmp/review-pr-*` worktree / `qwen-review/*` branch from an + # interrupted review. Reset all three per run; never fail the job. + - name: 'Clean stale agent state' run: |- set -uo pipefail + # Fresh per-run agent home (must match QWEN_HOME on the Qwen step + # below) + drop any leftover stage drafts. + QWEN_HOME="${RUNNER_TEMP:?}/qwen-home" + rm -rf "$QWEN_HOME" 2>/dev/null || true + mkdir -p "$QWEN_HOME" + rm -f /tmp/stage-*.md 2>/dev/null || true # `.git` is a directory in a normal checkout but a gitlink file in a # worktree; -e covers both, and a missing .git (first run) too. if [ ! -e .git ]; then @@ -409,6 +417,10 @@ jobs: PR_NUMBER: '${{ steps.context.outputs.pr_number }}' REVIEW_MODE: '${{ steps.context.outputs.review_mode }}' TIMEOUT_MINUTES: '${{ steps.context.outputs.timeout_minutes }}' + # Per-run agent home so this review's session/memory cannot leak into + # the next on the reused self-hosted workspace (reset in "Clean stale + # agent state"). Must match the QWEN_HOME computed there. + QWEN_HOME: '${{ runner.temp }}/qwen-home' run: |- set -euo pipefail fail() { diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml index 874cdf92c0..49def93232 100644 --- a/.github/workflows/qwen-triage.yml +++ b/.github/workflows/qwen-triage.yml @@ -195,13 +195,21 @@ jobs: -f content='eyes' > /dev/null || echo "Failed to add triage acknowledgement reaction; continuing." >&2 - # Self-hosted ECS runners reuse the workspace between runs, so an - # interrupted triage (its agent has enter_worktree/exit_worktree) can - # leave a stale `.qwen/tmp/*` worktree that trips the checkout below. - # Prune defensively; never fail the job. No-op on a fresh hosted runner. - - name: 'Clean stale triage worktrees' + # Self-hosted ECS runners reuse $HOME, /tmp and the workspace between runs, + # so a prior run can bleed into this one: its agent session/memory (under + # QWEN_HOME), leftover draft comments (/tmp/stage-*.md, which survive + # `git clean`), or a stale `.qwen/tmp/*` worktree from an interrupted + # triage (its agent has enter_worktree/exit_worktree). Reset all three + # per run; never fail the job. No-op on a fresh hosted runner. + - name: 'Clean stale agent state' run: |- set -uo pipefail + # Fresh per-run agent home (must match QWEN_HOME on the Qwen step + # below) + drop any leftover stage drafts. + QWEN_HOME="${RUNNER_TEMP:?}/qwen-home" + rm -rf "$QWEN_HOME" 2>/dev/null || true + mkdir -p "$QWEN_HOME" + rm -f /tmp/stage-*.md 2>/dev/null || true # `.git` is a directory in a normal checkout but a gitlink file in a # worktree; -e covers both, and a missing .git (first run) too. if [ ! -e .git ]; then @@ -210,7 +218,7 @@ jobs: fi rm -rf .qwen/tmp/* 2>/dev/null || true git worktree prune -v || true - echo "stale triage worktrees cleaned" + echo "stale agent state cleaned" - name: 'Checkout repo' uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2 @@ -234,6 +242,10 @@ jobs: GITHUB_TOKEN: '${{ secrets.QWEN_CODE_BOT_TOKEN || secrets.CI_BOT_PAT }}' GH_TOKEN: '${{ secrets.QWEN_CODE_BOT_TOKEN || secrets.CI_BOT_PAT }}' REPOSITORY: '${{ github.repository }}' + # Per-run agent home so this run's session/memory cannot leak into the + # next on the reused self-hosted workspace (reset in "Clean stale + # agent state"). Must match the QWEN_HOME computed there. + QWEN_HOME: '${{ runner.temp }}/qwen-home' with: OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}' OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'