\n%s
\n\n\n' "$summary"
@@ -840,8 +847,10 @@ jobs:
DESCRIPTION='The tmux test did not complete because the qwen process failed or was killed. This is not a pass/fail result for the affected flow; check runner resources and PR code for crashes or memory leaks.'
elif [ "${VERDICT:-}" = "timeout" ]; then
DESCRIPTION='The tmux test did not complete before the time limit. This is not a pass/fail result for the affected flow; see the workflow run and artifacts for details.'
- else
+ elif [ "${VERDICT:-}" = "pass" ] || [ "${VERDICT:-}" = "fail" ]; then
DESCRIPTION='Launched the changed app in a real tmux session and exercised the affected flow.'
+ else
+ DESCRIPTION='The tmux test produced an unrecognized verdict, so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details.'
fi
{
printf '%s\n\n' ''
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 3f8028510d4..8bda92bd463 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -17,7 +17,7 @@ function step(name) {
const escaped = escapeRegExp(name);
const match = workflow.match(
new RegExp(
- `\\n\\s+- name: '${escaped}'[\\s\\S]*?(?=\\n\\s+- name: '|\\n\\s{2}[a-zA-Z0-9_-]+:|$)`,
+ `\\n\\s+- name:\\s*(['"])${escaped}\\1[\\s\\S]*?(?=\\n\\s+- name:\\s*['"]|\\n\\s{2}[a-zA-Z0-9_-]+:|$)`,
),
);
return match?.[0] ?? '';
@@ -35,6 +35,10 @@ describe('qwen-triage tmux workflow', () => {
expect(postStep).toContain("-e 's/>/\\>/g'");
expect(postStep).toContain("tr -d '\\000'");
expect(postStep).toContain('Log could not be rendered');
+ expect(postStep).toContain('if ! content="$(');
+ expect(postStep).toContain('set -o pipefail');
+ expect(postStep).toContain('head_status=0');
+ expect(postStep).toContain('::warning::emit_block failed');
});
it('passes the selected OpenAI model into the app under tmux test', () => {
@@ -49,15 +53,28 @@ describe('qwen-triage tmux workflow', () => {
expect(postStep).toContain('if [ "${VERDICT:-}" = "infra-error" ]; then');
expect(postStep).toContain('elif [ "${VERDICT:-}" = "timeout" ]; then');
+ expect(postStep).toContain(
+ 'elif [ "${VERDICT:-}" = "pass" ] || [ "${VERDICT:-}" = "fail" ]; then',
+ );
+ expect(postStep).toContain('VERDICT_LABEL="${VERDICT:-unknown}"');
+ expect(postStep).toContain("VERDICT_LABEL='infra-error (crash/OOM)'");
expect(postStep).toContain('The tmux test did not complete');
+ expect(postStep).toContain(
+ 'The tmux test did not complete before the time limit',
+ );
expect(postStep).toContain('not a pass/fail result');
expect(postStep).toContain('crashes or memory leaks');
+ expect(postStep).toContain(
+ 'Launched the changed app in a real tmux session and exercised the affected flow.',
+ );
+ expect(postStep).toContain('produced an unrecognized verdict');
});
it('installs the heavy tmux test harness only for runnable PRs', () => {
const installStep = step('Install tmux runner tools');
const resolverStep = step('Install PR resolver tools');
+ expect(resolverStep).toContain('apt-get install');
expect(installStep).toContain('if: "steps.pr.outputs.decision == \'run\'"');
expect(resolverStep).not.toContain('tmux');
expect(resolverStep).not.toContain('npm install');
From 02938102a8c6823461115025bb3ce3ddff2a6d30 Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Sun, 21 Jun 2026 19:55:27 +0800
Subject: [PATCH 04/10] fix(ci): address tmux triage review followups
---
.github/workflows/qwen-triage.yml | 2 +-
scripts/tests/qwen-triage-workflow.test.js | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 24b9b1ba445..7e907b84497 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -372,7 +372,7 @@ jobs:
set -euo pipefail
apt-get install -y --no-install-recommends tmux util-linux
- npm install -g '@qwen-code/qwen-code@latest'
+ npm install -g --registry=https://registry.npmjs.org '@qwen-code/qwen-code@latest'
qwen --version
tmux -V
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 8bda92bd463..101a790ddb3 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -68,6 +68,8 @@ describe('qwen-triage tmux workflow', () => {
'Launched the changed app in a real tmux session and exercised the affected flow.',
);
expect(postStep).toContain('produced an unrecognized verdict');
+ expect(postStep).toContain('"$VERDICT_LABEL" "$RUN_URL"');
+ expect(postStep).toContain('printf \'%s\\n\\n\' "$DESCRIPTION"');
});
it('installs the heavy tmux test harness only for runnable PRs', () => {
@@ -76,6 +78,14 @@ describe('qwen-triage tmux workflow', () => {
expect(resolverStep).toContain('apt-get install');
expect(installStep).toContain('if: "steps.pr.outputs.decision == \'run\'"');
+ expect(installStep).toContain(
+ 'apt-get install -y --no-install-recommends tmux util-linux',
+ );
+ expect(installStep).toContain(
+ "npm install -g --registry=https://registry.npmjs.org '@qwen-code/qwen-code@latest'",
+ );
+ expect(installStep).toContain('qwen --version');
+ expect(installStep).toContain('tmux -V');
expect(resolverStep).not.toContain('tmux');
expect(resolverStep).not.toContain('npm install');
expect(resolverStep).not.toContain('qwen --version');
From f423fb425c379d3e7af346b99f818b2345b9e043 Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Sun, 21 Jun 2026 21:56:21 +0800
Subject: [PATCH 05/10] fix(ci): simplify tmux triage hardening
---
.github/workflows/qwen-triage.yml | 34 +++++++++-------------
scripts/tests/qwen-triage-workflow.test.js | 4 ++-
2 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 7e907b84497..1bf70d29243 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -345,6 +345,17 @@ jobs:
echo "decision=na" >> "$GITHUB_OUTPUT"
fi
+ # Install before checkout so PR-controlled .npmrc cannot affect npm.
+ - name: 'Install tmux runner tools'
+ if: "steps.pr.outputs.decision == 'run'"
+ run: |-
+ set -euo pipefail
+ apt-get install -y --no-install-recommends tmux util-linux
+
+ npm install -g --registry=https://registry.npmjs.org '@qwen-code/qwen-code@latest'
+ qwen --version
+ tmux -V
+
- name: 'Clean stale review worktrees'
if: "steps.pr.outputs.decision == 'run'"
run: |-
@@ -362,20 +373,6 @@ jobs:
ref: 'refs/pull/${{ steps.pr.outputs.pr_number }}/merge'
fetch-depth: 1
- # Heavy tmux/qwen/util-linux setup is deferred here (gated on
- # decision == 'run') so non-TUI PRs skip it. Any step added between
- # 'Resolve PR and check state' and this one must not need tmux, qwen, or
- # util-linux/runuser; those are installed here, not in resolver tools.
- - name: 'Install tmux runner tools'
- if: "steps.pr.outputs.decision == 'run'"
- run: |-
- set -euo pipefail
- apt-get install -y --no-install-recommends tmux util-linux
-
- npm install -g --registry=https://registry.npmjs.org '@qwen-code/qwen-code@latest'
- qwen --version
- tmux -V
-
- name: 'Install and build PR app'
id: 'prepare'
if: "steps.pr.outputs.decision == 'run'"
@@ -793,15 +790,12 @@ jobs:
fi
if ! content="$(
set -o pipefail
- {
- head_status=0
- head -c "$max" "$file" || head_status=$?
- printf '%s' "$truncated"
- exit "$head_status"
- } | tr -d '\000' | sed -e 's/&/\&/g' -e 's/\</g' -e 's/>/\>/g'
+ head -c "$max" "$file" | tr -d '\000' | sed -e 's/&/\&/g' -e 's/\</g' -e 's/>/\>/g'
)"; then
echo "::warning::emit_block failed while rendering $summary; see run artifacts." >&2
content='Log could not be rendered; see run artifacts.'
+ elif [ -n "$truncated" ]; then
+ content="${content}${truncated}"
fi
printf '\n%s
\n\n\n' "$summary"
printf '%s\n' "$content"
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 101a790ddb3..6bad0403438 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -37,7 +37,6 @@ describe('qwen-triage tmux workflow', () => {
expect(postStep).toContain('Log could not be rendered');
expect(postStep).toContain('if ! content="$(');
expect(postStep).toContain('set -o pipefail');
- expect(postStep).toContain('head_status=0');
expect(postStep).toContain('::warning::emit_block failed');
});
@@ -92,5 +91,8 @@ describe('qwen-triage tmux workflow', () => {
expect(
workflow.indexOf("- name: 'Resolve PR and check state'"),
).toBeLessThan(workflow.indexOf("- name: 'Install tmux runner tools'"));
+ expect(
+ workflow.indexOf("- name: 'Install tmux runner tools'"),
+ ).toBeLessThan(workflow.indexOf("- name: 'Checkout PR merge ref'"));
});
});
From 7fb69d6b8e0bf4c1ea1fb585808d1e922d7da77b Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Mon, 22 Jun 2026 00:39:49 +0800
Subject: [PATCH 06/10] fix(ci): sanitize tmux triage verdict output
---
.github/workflows/qwen-triage.yml | 41 ++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 1bf70d29243..470fd01da76 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -389,13 +389,15 @@ jobs:
set +e
{
printf '%s\n' '$ npm ci --prefer-offline --no-audit --progress=false'
- runuser -u node -- npm ci --prefer-offline --no-audit --progress=false
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV \
+ npm ci --prefer-offline --no-audit --progress=false
install_status=$?
if [ "$install_status" -ne 0 ]; then
printf '\n%s\n' "npm ci failed with exit code ${install_status}."
else
printf '\n%s\n' '$ npm run build'
- runuser -u node -- npm run build
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV \
+ npm run build
build_status=$?
if [ "$build_status" -ne 0 ]; then
printf '\n%s\n' "npm run build failed with exit code ${build_status}."
@@ -823,7 +825,7 @@ jobs:
case "$PREPARE_FAILURE_PHASE" in
install) PREPARE_COMMAND='npm ci' ;;
build) PREPARE_COMMAND='npm run build' ;;
- *) PREPARE_COMMAND="$PREPARE_FAILURE_PHASE" ;;
+ *) PREPARE_COMMAND='install/build' ;;
esac
{
printf '%s\n\n' ''
@@ -835,17 +837,28 @@ jobs:
else
REPORT="$(find tmux-results -name 'report.md' 2>/dev/null | head -1 || true)"
TRANSCRIPT="$(find tmux-results -name 'tmux-readable-full.log' 2>/dev/null | head -1 || true)"
- VERDICT_LABEL="${VERDICT:-unknown}"
- if [ "${VERDICT:-}" = "infra-error" ]; then
- VERDICT_LABEL='infra-error (crash/OOM)'
- DESCRIPTION='The tmux test did not complete because the qwen process failed or was killed. This is not a pass/fail result for the affected flow; check runner resources and PR code for crashes or memory leaks.'
- elif [ "${VERDICT:-}" = "timeout" ]; then
- DESCRIPTION='The tmux test did not complete before the time limit. This is not a pass/fail result for the affected flow; see the workflow run and artifacts for details.'
- elif [ "${VERDICT:-}" = "pass" ] || [ "${VERDICT:-}" = "fail" ]; then
- DESCRIPTION='Launched the changed app in a real tmux session and exercised the affected flow.'
- else
- DESCRIPTION='The tmux test produced an unrecognized verdict, so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details.'
- fi
+ case "${VERDICT:-}" in
+ infra-error)
+ VERDICT_LABEL='infra-error (crash/OOM)'
+ DESCRIPTION='The tmux test did not complete because the qwen process failed or was killed. This is not a pass/fail result for the affected flow; check runner resources and PR code for crashes or memory leaks.'
+ ;;
+ timeout)
+ VERDICT_LABEL='timeout'
+ DESCRIPTION='The tmux test did not complete before the time limit. This is not a pass/fail result for the affected flow; see the workflow run and artifacts for details.'
+ ;;
+ pass)
+ VERDICT_LABEL='pass'
+ DESCRIPTION='Launched the changed app in a real tmux session and exercised the affected flow.'
+ ;;
+ fail)
+ VERDICT_LABEL='fail'
+ DESCRIPTION='Launched the changed app in a real tmux session and exercised the affected flow.'
+ ;;
+ *)
+ VERDICT_LABEL='unknown'
+ DESCRIPTION='The tmux test produced an unrecognized verdict, so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details.'
+ ;;
+ esac
{
printf '%s\n\n' ''
printf '**tmux real-user testing: %s** - [workflow run](%s)\n\n' "$VERDICT_LABEL" "$RUN_URL"
From d8f4d7c3b42962afca1da427c8fc83a0d3f66821 Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Mon, 22 Jun 2026 10:10:30 +0800
Subject: [PATCH 07/10] fix(ci): harden tmux result rendering
---
.github/workflows/qwen-triage.yml | 20 ++++++++++-----
scripts/tests/qwen-triage-workflow.test.js | 30 +++++++++++++++++-----
2 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 470fd01da76..a47f23abb4d 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -389,14 +389,14 @@ jobs:
set +e
{
printf '%s\n' '$ npm ci --prefer-offline --no-audit --progress=false'
- runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV \
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH \
npm ci --prefer-offline --no-audit --progress=false
install_status=$?
if [ "$install_status" -ne 0 ]; then
printf '\n%s\n' "npm ci failed with exit code ${install_status}."
else
printf '\n%s\n' '$ npm run build'
- runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV \
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH \
npm run build
build_status=$?
if [ "$build_status" -ne 0 ]; then
@@ -784,22 +784,27 @@ jobs:
# as markdown — which a
# backtick fence (breakable by a long enough ``` run) cannot guarantee.
# Order matters: escape & first so the < / > entities aren't re-escaped.
+ html_escape() {
+ sed -e 's/&/\&/g' -e 's/\</g' -e 's/>/\>/g'
+ }
+
emit_block() {
- local summary="$1" file="$2" max="$3" content truncated=''
+ local summary="$1" file="$2" max="$3" content truncated='' summary_html
[ -n "$file" ] && [ -f "$file" ] || return 0
+ summary_html="$(printf '%s' "$summary" | html_escape)"
if [ "$(wc -c < "$file")" -gt "$max" ]; then
truncated=$'\n\n...truncated -- full log in the run artifacts.'
fi
if ! content="$(
set -o pipefail
- head -c "$max" "$file" | tr -d '\000' | sed -e 's/&/\&/g' -e 's/\</g' -e 's/>/\>/g'
+ head -c "$max" "$file" | tr -d '\000' | html_escape
)"; then
echo "::warning::emit_block failed while rendering $summary; see run artifacts." >&2
content='Log could not be rendered; see run artifacts.'
elif [ -n "$truncated" ]; then
content="${content}${truncated}"
fi
- printf '\n%s
\n\n\n' "$summary"
+ printf '\n%s
\n\n\n' "$summary_html"
printf '%s\n' "$content"
printf '
\n\n \n\n'
}
@@ -856,7 +861,10 @@ jobs:
;;
*)
VERDICT_LABEL='unknown'
- DESCRIPTION='The tmux test produced an unrecognized verdict, so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details.'
+ UNKNOWN_VERDICT="$(
+ printf '%s' "${VERDICT:-}" | tr -d '\000' | tr '\r\n' ' ' | head -c 200 | html_escape
+ )"
+ DESCRIPTION="The tmux test produced an unrecognized verdict (${UNKNOWN_VERDICT}), so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details."
;;
esac
{
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 6bad0403438..7438fb7871a 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -33,11 +33,18 @@ describe('qwen-triage tmux workflow', () => {
expect(postStep).toContain("sed -e 's/&/\\&/g'");
expect(postStep).toContain("-e 's/\\</g'");
expect(postStep).toContain("-e 's/>/\\>/g'");
+ expect(postStep).toContain('html_escape()');
expect(postStep).toContain("tr -d '\\000'");
expect(postStep).toContain('Log could not be rendered');
expect(postStep).toContain('if ! content="$(');
expect(postStep).toContain('set -o pipefail');
expect(postStep).toContain('::warning::emit_block failed');
+ expect(postStep).toContain(
+ "summary_html=\"$(printf '%s' \"$summary\" | html_escape)\"",
+ );
+ expect(postStep).toContain(
+ "'\\n%s
\\n\\n\\n' \"$summary_html\"",
+ );
});
it('passes the selected OpenAI model into the app under tmux test', () => {
@@ -50,13 +57,12 @@ describe('qwen-triage tmux workflow', () => {
it('reports timeout and infra-error without claiming the flow was exercised', () => {
const postStep = step('Post tmux result comment');
- expect(postStep).toContain('if [ "${VERDICT:-}" = "infra-error" ]; then');
- expect(postStep).toContain('elif [ "${VERDICT:-}" = "timeout" ]; then');
- expect(postStep).toContain(
- 'elif [ "${VERDICT:-}" = "pass" ] || [ "${VERDICT:-}" = "fail" ]; then',
- );
- expect(postStep).toContain('VERDICT_LABEL="${VERDICT:-unknown}"');
+ expect(postStep).toContain('case "${VERDICT:-}" in');
expect(postStep).toContain("VERDICT_LABEL='infra-error (crash/OOM)'");
+ expect(postStep).toContain("VERDICT_LABEL='timeout'");
+ expect(postStep).toContain("VERDICT_LABEL='pass'");
+ expect(postStep).toContain("VERDICT_LABEL='fail'");
+ expect(postStep).toContain("VERDICT_LABEL='unknown'");
expect(postStep).toContain('The tmux test did not complete');
expect(postStep).toContain(
'The tmux test did not complete before the time limit',
@@ -67,10 +73,22 @@ describe('qwen-triage tmux workflow', () => {
'Launched the changed app in a real tmux session and exercised the affected flow.',
);
expect(postStep).toContain('produced an unrecognized verdict');
+ expect(postStep).toContain('UNKNOWN_VERDICT="$(');
+ expect(postStep).toContain('tr \'\\r\\n\' \' \'');
+ expect(postStep).toContain('${UNKNOWN_VERDICT}');
expect(postStep).toContain('"$VERDICT_LABEL" "$RUN_URL"');
expect(postStep).toContain('printf \'%s\\n\\n\' "$DESCRIPTION"');
});
+ it('removes GitHub command files from PR-controlled lifecycle scripts', () => {
+ const prepareStep = step('Install and build PR app');
+
+ expect(prepareStep).toContain('-u GITHUB_OUTPUT');
+ expect(prepareStep).toContain('-u GITHUB_STATE');
+ expect(prepareStep).toContain('-u GITHUB_ENV');
+ expect(prepareStep).toContain('-u GITHUB_PATH');
+ });
+
it('installs the heavy tmux test harness only for runnable PRs', () => {
const installStep = step('Install tmux runner tools');
const resolverStep = step('Install PR resolver tools');
From b7fa62296d4ec204154c046df719b6acafd736a6 Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Mon, 22 Jun 2026 10:59:23 +0800
Subject: [PATCH 08/10] ci: strip step summary from triage build
---
.github/workflows/qwen-triage.yml | 4 ++--
scripts/tests/qwen-triage-workflow.test.js | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index a47f23abb4d..5039adeac57 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -389,14 +389,14 @@ jobs:
set +e
{
printf '%s\n' '$ npm ci --prefer-offline --no-audit --progress=false'
- runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH \
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH -u GITHUB_STEP_SUMMARY \
npm ci --prefer-offline --no-audit --progress=false
install_status=$?
if [ "$install_status" -ne 0 ]; then
printf '\n%s\n' "npm ci failed with exit code ${install_status}."
else
printf '\n%s\n' '$ npm run build'
- runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH \
+ runuser -u node -- env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH -u GITHUB_STEP_SUMMARY \
npm run build
build_status=$?
if [ "$build_status" -ne 0 ]; then
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 7438fb7871a..18f6e6655f6 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -87,6 +87,7 @@ describe('qwen-triage tmux workflow', () => {
expect(prepareStep).toContain('-u GITHUB_STATE');
expect(prepareStep).toContain('-u GITHUB_ENV');
expect(prepareStep).toContain('-u GITHUB_PATH');
+ expect(prepareStep).toContain('-u GITHUB_STEP_SUMMARY');
});
it('installs the heavy tmux test harness only for runnable PRs', () => {
From a67aea413530d6dcadbac295bf0c971073b4a2de Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Mon, 22 Jun 2026 12:44:26 +0800
Subject: [PATCH 09/10] fix(ci): report missing tmux artifacts
---
.github/workflows/qwen-triage.yml | 8 ++++++++
scripts/tests/qwen-triage-workflow.test.js | 11 ++++++++---
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 5039adeac57..7a295e50a71 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -842,6 +842,10 @@ jobs:
else
REPORT="$(find tmux-results -name 'report.md' 2>/dev/null | head -1 || true)"
TRANSCRIPT="$(find tmux-results -name 'tmux-readable-full.log' 2>/dev/null | head -1 || true)"
+ if [ -z "$REPORT" ] && [ -z "$TRANSCRIPT" ]; then
+ MISSING_ARTIFACTS_NOTE='No report.md or tmux-readable-full.log was found in tmux-results, so detailed report sections are omitted.'
+ echo "::warning::${MISSING_ARTIFACTS_NOTE}"
+ fi
case "${VERDICT:-}" in
infra-error)
VERDICT_LABEL='infra-error (crash/OOM)'
@@ -864,6 +868,7 @@ jobs:
UNKNOWN_VERDICT="$(
printf '%s' "${VERDICT:-}" | tr -d '\000' | tr '\r\n' ' ' | head -c 200 | html_escape
)"
+ echo "::warning::Unrecognized tmux verdict: ${UNKNOWN_VERDICT}"
DESCRIPTION="The tmux test produced an unrecognized verdict (${UNKNOWN_VERDICT}), so this is not a pass/fail result for the affected flow. See the workflow run and artifacts for details."
;;
esac
@@ -871,6 +876,9 @@ jobs:
printf '%s\n\n' ''
printf '**tmux real-user testing: %s** - [workflow run](%s)\n\n' "$VERDICT_LABEL" "$RUN_URL"
printf '%s\n\n' "$DESCRIPTION"
+ if [ -n "${MISSING_ARTIFACTS_NOTE:-}" ]; then
+ printf '%s\n\n' "$MISSING_ARTIFACTS_NOTE"
+ fi
emit_block 'E2E test report' "$REPORT" 20000
emit_block 'Full tmux transcript' "$TRANSCRIPT" 30000
printf '%s\n' '— _Qwen Code · tmux real-user testing_'
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 18f6e6655f6..508fa381c05 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -40,10 +40,10 @@ describe('qwen-triage tmux workflow', () => {
expect(postStep).toContain('set -o pipefail');
expect(postStep).toContain('::warning::emit_block failed');
expect(postStep).toContain(
- "summary_html=\"$(printf '%s' \"$summary\" | html_escape)\"",
+ 'summary_html="$(printf \'%s\' "$summary" | html_escape)"',
);
expect(postStep).toContain(
- "'\\n%s
\\n\\n\\n' \"$summary_html\"",
+ '\'\\n%s
\\n\\n\\n\' "$summary_html"',
);
});
@@ -74,10 +74,15 @@ describe('qwen-triage tmux workflow', () => {
);
expect(postStep).toContain('produced an unrecognized verdict');
expect(postStep).toContain('UNKNOWN_VERDICT="$(');
- expect(postStep).toContain('tr \'\\r\\n\' \' \'');
+ expect(postStep).toContain('::warning::Unrecognized tmux verdict');
+ expect(postStep).toContain("tr '\\r\\n' ' '");
expect(postStep).toContain('${UNKNOWN_VERDICT}');
expect(postStep).toContain('"$VERDICT_LABEL" "$RUN_URL"');
expect(postStep).toContain('printf \'%s\\n\\n\' "$DESCRIPTION"');
+ expect(postStep).toContain('MISSING_ARTIFACTS_NOTE=');
+ expect(postStep).toContain(
+ 'No report.md or tmux-readable-full.log was found in tmux-results',
+ );
});
it('removes GitHub command files from PR-controlled lifecycle scripts', () => {
From 7190ad6336430ef54fecf072695cfd958f942ad4 Mon Sep 17 00:00:00 2001
From: yiliang114 <1204183885@qq.com>
Date: Mon, 22 Jun 2026 14:01:49 +0800
Subject: [PATCH 10/10] fix(ci): clarify tmux prepare diagnostics
---
.github/workflows/qwen-triage.yml | 15 ++++++++++++++-
scripts/tests/qwen-triage-workflow.test.js | 20 ++++++++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml
index 7a295e50a71..a3989cf64a0 100644
--- a/.github/workflows/qwen-triage.yml
+++ b/.github/workflows/qwen-triage.yml
@@ -830,12 +830,25 @@ jobs:
case "$PREPARE_FAILURE_PHASE" in
install) PREPARE_COMMAND='npm ci' ;;
build) PREPARE_COMMAND='npm run build' ;;
- *) PREPARE_COMMAND='install/build' ;;
+ *)
+ PREPARE_COMMAND='install/build'
+ UNKNOWN_PREPARE_PHASE="$(
+ printf '%s' "$PREPARE_FAILURE_PHASE" | tr -d '\000' | tr '\r\n' ' ' | head -c 200 | html_escape
+ )"
+ echo "::warning::Unrecognized prepare failure phase: ${UNKNOWN_PREPARE_PHASE}"
+ ;;
esac
+ if [ -z "$PREPARE_LOG" ]; then
+ PREPARE_LOG_NOTE='No prepare.log was found in tmux-results, so the install/build log section is omitted.'
+ echo "::warning::${PREPARE_LOG_NOTE}"
+ fi
{
printf '%s\n\n' ''
printf '**tmux real-user testing: fail** - [workflow run](%s)\n\n' "$RUN_URL"
printf 'The PR app could not be launched because `%s` failed before the tmux session started. This is treated as a PR failure verdict rather than an infrastructure failure.\n\n' "$PREPARE_COMMAND"
+ if [ -n "${PREPARE_LOG_NOTE:-}" ]; then
+ printf '%s\n\n' "$PREPARE_LOG_NOTE"
+ fi
emit_block 'Install/build log' "$PREPARE_LOG" 20000
printf '%s\n' '— _Qwen Code · tmux real-user testing_'
} > "$BODY_FILE"
diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js
index 508fa381c05..e1cbcae7dd6 100644
--- a/scripts/tests/qwen-triage-workflow.test.js
+++ b/scripts/tests/qwen-triage-workflow.test.js
@@ -87,12 +87,32 @@ describe('qwen-triage tmux workflow', () => {
it('removes GitHub command files from PR-controlled lifecycle scripts', () => {
const prepareStep = step('Install and build PR app');
+ const strippedEnv =
+ 'env -u GITHUB_OUTPUT -u GITHUB_STATE -u GITHUB_ENV -u GITHUB_PATH -u GITHUB_STEP_SUMMARY';
expect(prepareStep).toContain('-u GITHUB_OUTPUT');
expect(prepareStep).toContain('-u GITHUB_STATE');
expect(prepareStep).toContain('-u GITHUB_ENV');
expect(prepareStep).toContain('-u GITHUB_PATH');
expect(prepareStep).toContain('-u GITHUB_STEP_SUMMARY');
+ expect(prepareStep).toMatch(
+ new RegExp(`${escapeRegExp(strippedEnv)} \\\\\\s+npm ci`),
+ );
+ expect(prepareStep).toMatch(
+ new RegExp(`${escapeRegExp(strippedEnv)} \\\\\\s+npm run build`),
+ );
+ });
+
+ it('does not echo unrecognized prepare failure phases into comments', () => {
+ const postStep = step('Post tmux result comment');
+
+ expect(postStep).toContain("PREPARE_COMMAND='install/build'");
+ expect(postStep).toContain('::warning::Unrecognized prepare failure phase');
+ expect(postStep).toContain('PREPARE_LOG_NOTE=');
+ expect(postStep).toContain(
+ 'No prepare.log was found in tmux-results, so the install/build log section is omitted.',
+ );
+ expect(postStep).not.toContain('PREPARE_COMMAND="$PREPARE_FAILURE_PHASE"');
});
it('installs the heavy tmux test harness only for runnable PRs', () => {