Skip to content

fix(release): use the run-scoped token for the publish checkout - #15728

Merged
wenshao merged 2 commits into
mainfrom
autofix/issue-15726
Oct 28, 2026
Merged

wenshao merged 2 commits into
mainfrom
autofix/issue-15726

Conversation

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

What this PR does

The release workflow's Publish Release job now fetches the release tree with the run-scoped GitHub token instead of the long-lived bot PAT. The bot PAT remains exported only to the steps that write: the release-branch force push, GitHub Release creation, and the downstream dispatch.

A workflow meta-test pins that split: the read-only checkout must not override the token, while the push and release-creation steps must keep receiving the PAT.

Why it's needed

On 2026-10-27 two release runs failed in Publish Release at Checkout with could not read Username for 'https://github.com': terminal prompts disabled and git exit 128, then passed the identical checkout on re-run, with run 40486976670 completing successfully on that attempt. That checkout was the only read-only fetch in the workflow authenticating with the long-lived PAT, and it already disables credential persistence, so the PAT added an auth dependency without adding access. The PAT was only needed where the job actually writes; moving the read-only fetch onto the run-scoped token removes the failing dependency and narrows credential exposure.

Reviewer Test Plan

How to verify

Read the publish job in the release workflow: its first checkout should have no token override and should keep persist-credentials: false; the release-branch push step should still receive CI_BOT_PAT, and "Create GitHub Release and Tag" should still use it as GITHUB_TOKEN.

Run the focused meta-tests:

npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/release-workflow.test.js

Expected: 103 tests pass. Re-adding token: '${{ secrets.CI_BOT_PAT }}' to the publish checkout should make the new test fail, which is the regression witness. A production dry-run release is the end-to-end check when credentials are available; this session could not dispatch one.

Evidence (Before & After)

Non-UI CI wiring change, so there are no screenshots. Before: the publish checkout passed token: secrets.CI_BOT_PAT, and two runs failed at that step with the git auth error above. After: the checkout uses the run-scoped token, the PAT stays on the write steps, and the new test pins both halves. Mutation probe: re-adding the token made the new test fail (1 failed | 102 skipped); restoring the change returned 103 passed.

Tested on

OS Status
🍏 macOS ⚠ïļ
🊟 Windows ⚠ïļ
🐧 Linux ✅

Environment (optional)

Linux workspace: npm run build, npm run typecheck, npm run lint, and focused Vitest suites. No production GitHub credentials were available, so the release run itself was not exercised.

Risk & Scope

  • Main risk or tradeoff: The read-only fetch now depends on the job's GITHUB_TOKEN instead of the PAT. The token has contents: write on this job and can read a public repository, so the fetch is unchanged in capability; the PAT remains available for every write path.
  • Not validated / out of scope: The transient GitHub-side PAT rejection itself; no checkout retry wrapper was added because the existing re-run path already recovers and the failing dependency is removed. A real dry-run release requires production credentials.
  • Breaking changes / migration notes: None. The release workflow still receives secrets.CI_BOT_PAT at job level for the write steps.

Linked Issues

Fixes #15726

äļ­æ–‡čŊŽ

朎 PR 做䚆äŧ€äđˆ

发åļƒå·Ĩä―œæĩįš„ Publish Release äŧŧåŠĄįŽ°åœĻä―ŋį”ĻæœŽæŽĄčŋčĄŒčŒƒå›īå†…įš„ GitHub äŧĪį‰Œæ‹‰å–å‘åļƒäŧĢį æ ‘ïžŒč€Œäļæ˜Ŋé•ŋæœŸæœ‰æ•ˆįš„æœšå™Ļäšš PAT。朚å™Ļäšš PAT äŧåŠåŊžå‡šįŧ™æ‰§čĄŒå†™å…Ĩįš„æ­ĨéŠĪ发åļƒåˆ†æ”ŊåžšåˆķæŽĻ送、创åŧš GitHub ReleaseäŧĨ及äļļ dispatch。

新åĒžįš„å·Ĩä―œæĩå…ƒæĩ‹čŊ•锁åŪšäš†čŋ™äļ€åˆ†å·Ĩ及čŊŧæĢ€å‡šäļåū—č͆ᛖäŧĪį‰ŒïžŒč€ŒæŽĻ送äļŽåˆ›åŧš Release įš„æ­ĨéŠĪåŋ…éĄŧįŧ§įŧ­æŽĨæ”ķ PAT。

äļšäŧ€äđˆéœ€č́åۃ

2026-10-27 有äļĪæŽĄå‘åļƒčŋčĄŒåœĻ Publish Release įš„ Checkout æ­ĨéŠĪåĪąčīĨæŠĨ错äļš could not read Username for 'https://github.com': terminal prompts disabled äŧĨ及 git é€€å‡šį  128随后åœĻé‡č·‘æ—ķį›ļåŒįš„æĢ€å‡šåī通čŋ‡äš†ïžŒäļ”čŋčĄŒ 40486976670 åœĻčŊĨæŽĄå°čŊ•äļ­åیæ•ī成功。čŊĨæĢ€å‡šæ˜Ŋå·Ĩä―œæĩäļ­å”Ŋäļ€ä―ŋį”Ļé•ŋ期 PAT é‰īæƒįš„åŠčŊŧæ‹‰å–ïžŒč€Œäļ”åۃå·ēįŧį́į”Ļ䚆凭æŪ持äđ…化因æ­Ī PAT 及åĒžåŠ äš†é‰ī权äūčĩ–åđķæēĄæœ‰åĒžåŠ čŪŋé—Ūčƒ―åŠ›ã€‚PAT 及åœĻäŧŧåŠĄįœŸæ­Ģ写å…Ĩįš„åœ°æ–đ才需čĶïž›æŠŠåŠčŊŧ拉取æ”đ到čŋčĄŒčŒƒå›īäŧĪį‰ŒäļŠïžŒį§ŧé™Ī乆åĪąčīĨįš„äūčĩ–åđķįžĐ小䚆凭æŪæšīéœēéĒ。

åŪĄæŸĨ者æĩ‹čŊ•čŪĄåˆ’

åĶ‚ä―•éŠŒčŊ

æŸĨįœ‹å‘åļƒå·Ĩä―œæĩäļ­įš„ publish äŧŧåŠĄïžšåŪƒįš„éĶ–æŽĄæĢ€å‡šäļåš”č͆ᛖ tokenåđķåš”äŋį•™ persist-credentials: false发åļƒåˆ†æ”ŊæŽĻ送æ­ĨéŠĪäŧå𔿔ķ到 CI_BOT_PAT“Create GitHub Release and Tag”äŧåš”æŠŠåŪƒä―œäļš GITHUB_TOKEN ä―ŋį”Ļ。

čŋčĄŒčšį„Ķįš„å…ƒæĩ‹čŊ•ïžš

npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/release-workflow.test.js

éĒ„æœŸïžš103 äļŠæĩ‹čŊ•通čŋ‡ã€‚把 token: '${{ secrets.CI_BOT_PAT }}' 加回发åļƒäŧŧåŠĄæĢ€å‡šåš”ä―ŋ新æĩ‹čŊ•åĪąčīĨčŋ™å°ąæ˜Ŋå›žå―’čŊæŪ。有凭æŪæ—ķïžŒį”Ÿäš§ dry-run 发å˜ŊįŦŊ到įŦŊæĢ€æŸĨ朎䞚čŊæ— æģ•æīū发。

čŊæŪ前后åŊđæŊ”

čŋ™æ˜Ŋ非 UI įš„ CI æŽĨįšŋæ”đåŠĻ因æ­ĪæēĄæœ‰æˆŠå›ū。äŋŪæ”đ前发åļƒäŧŧåŠĄæĢ€å‡šäž å…Ĩ token: secrets.CI_BOT_PATäļĪæŽĄčŋčĄŒåœĻčŊĨæ­ĨéŠĪ因äļŠčŋ° git é‰ī权错čŊŊåĪąčīĨ。äŋŪæ”đ后æĢ€å‡šä―ŋį”ĻčŋčĄŒčŒƒå›īäŧĪį‰ŒïžŒPAT äŋį•™åœĻ写å…Ĩæ­ĨéŠĪäļŠïžŒæ–°æĩ‹čŊ•锁åŪšäš†čŋ™äļĪéƒĻ分。变垂æŽĒ针把 token 加回后新æĩ‹čŊ•åĪąčīĨ1 failed | 102 skippedæĒåĪæ”đåŠĻ后变äļš 103 passed。

æĩ‹čŊ•åđģ台

OS įŠķ态
🍏 macOS ⚠ïļ
🊟 Windows ⚠ïļ
🐧 Linux ✅

įŽŊåĒƒïžˆåŊ选

Linux å·Ĩä―œåŒšïžšnpm run build、npm run typecheck、npm run lintäŧĨåŠčšį„Ķįš„ Vitest åĨ—äŧķ。æēĄæœ‰į”Ÿäš§ GitHub 凭æŪ因æ­Ī朊åŪžé™…čŋčĄŒå‘åĩįĻ‹ã€‚

éĢŽé™ĐäļŽčŒƒå›ī

  • äļŧč́éĢŽé™Đæˆ–å–čˆïžšåŠčŊŧæ‹‰å–įŽ°åœĻäūčĩ–äŧŧåŠĄįš„ GITHUB_TOKEN 而äļæ˜Ŋ PAT。čŊĨäŧĪį‰ŒåœĻæ­ĪäŧŧåŠĄäļŠå…·æœ‰ contents: writeäđŸčƒ―čŊŧ取兎垀äŧ“嚓因æ­Īæ‹‰å–čƒ―åŠ›äļå˜ïž›PAT äŧåŊį”Ļ䚎所有写å…Ĩč·Ŋåū„。
  • 朊銌čŊ / čķ…å‡ščŒƒå›īïžšGitHub äū§įš„įžŽæ—ķ PAT 拒įŧæœŽčšŦïž›æēĄæœ‰æ·ŧ加æĢ€å‡šé‡čŊ•包čĢ…ïžŒå› äļšįŽ°æœ‰é‡č·‘č·Ŋåū„å·ēįŧåŊäŧĨæĒåĪïžŒäļ”åĪąčīĨįš„äūčĩ–å·ēčĒŦį§ŧé™Īã€‚įœŸåŪž dry-run 发åļƒéœ€čĶį”Ÿäš§å‡­æŪ。
  • į ī坏性变æ›ī / čŋį§ŧčŊŽïžšæ— ã€‚发åļƒå·Ĩä―œæĩåœĻäŧŧåŠĄįš§åˆŦäŧäžšæ”ķ到 secrets.CI_BOT_PAT äū›å†™å…Ĩæ­ĨéŠĪä―ŋį”Ļ。

å…ģ联 Issue

Fixes #15726

â€ĶAT (#15726)

The publish job's read-only checkout used secrets.CI_BOT_PAT while every
other checkout in the release workflow uses the run-scoped token. Two
release runs failed at exactly that step with "could not read Username for
'https://github.com': terminal prompts disabled" (git exit 128), and both
passed on re-run. The bot PAT is already exported to the push and
release-creation steps that write, so the read-only fetch does not need it.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

Release failure v0.26.2-preview.1 — publish checkout auth

Root cause

The Publish Release job's first checkout authenticated with secrets.CI_BOT_PAT; every other checkout in the release workflow uses the run-scoped github.token. Run 40486976670 (attempt 2) and the concurrent run 40510643634 (attempt 1) both failed in that job at the Checkout step after about 31 seconds with:

  • could not read Username for 'https://github.com': terminal prompts disabled
  • The process '/usr/bin/git' failed with exit code 128

Both runs passed the identical checkout on re-run (attempt 3 of 40486976670 at 15:08:50Z and attempt 2 of 40510643634 at 15:16:27Z), and run 40486976670 completed successfully on that attempt. That points to a transient rejection of the long-lived PAT during a read-only fetch rather than a broken release ref. The PAT is not needed for that fetch: the checkout already sets persist-credentials: false, and the steps that actually write install the credential themselves — the release-branch force push runs gh auth setup-git with GH_TOKEN set from the PAT, and release creation plus the downstream dispatch pass the PAT explicitly as GITHUB_TOKEN. The read-only fetch now uses the default run-scoped token, removing the failing dependency and narrowing credential exposure.

Fix

  • Removed the PAT token override from the publish job's initial checkout. The bot PAT remains exported exactly to the steps that push or write releases.
  • Added a workflow meta-test pinning the read-only checkout to the run-scoped token and pinning the PAT to the push and release-creation steps, so a future edit cannot quietly reintroduce the failing wiring.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • vitest on release-workflow, workflow-size, package-scripts, ai-release-notes-workflow, install-script, runtime-archive-cache — 6 files passed, 555 tests passed, 20 skipped
  • bash .github/scripts/check-workflow-size.sh — passed (release.yml 40032 bytes, inside the 4096-byte growth allowance)
  • Mutation probe: re-adding token: '${{ secrets.CI_BOT_PAT }}' to the publish checkout made the new test fail (1 failed | 102 skipped); restoring the change returned 103 passed
  • The original failure was a GitHub-hosted authentication flake and cannot be reproduced locally; the workflow meta-test is the surrogate witness. A real dry-run release was not dispatched because this session has no production credentials.
äļ­æ–‡čŊŽ

发åļƒåĪąčīĨ v0.26.2-preview.1 —— 发åļƒäŧŧåŠĄæĢ€å‡šé‰ī权

æ đ因

Publish Release äŧŧåŠĄįš„éĶ–æŽĄæĢ€å‡šä―ŋį”Ļ secrets.CI_BOT_PAT é‰īæƒïžŒč€Œå‘åļƒå·Ĩä―œæĩäļ­įš„å…ķäŧ–所有æĢ€å‡šéƒ―ä―ŋį”ĻæœŽæŽĄčŋčĄŒčŒƒå›īå†…įš„ github.token。čŋčĄŒ 40486976670ïžˆįŽŽ 2 æŽĄå°čŊ•äļŽåđķå‘įš„čŋčĄŒ 40510643634ïžˆįŽŽ 1 æŽĄå°čŊ•ïž‰éƒ―åœĻčŊĨäŧŧåŠĄįš„ Checkout æ­ĨéŠĪåĪąčīĨïžŒč€—æ—ķįšĶ 31 į§’ïžŒé”™čŊŊäļšïžš

  • could not read Username for 'https://github.com': terminal prompts disabled
  • The process '/usr/bin/git' failed with exit code 128

äļĪæŽĄčŋčĄŒåœĻé‡č·‘æ—ķéƒ―é€ščŋ‡äš†åیå…Ļį›ļåŒįš„æĢ€å‡šïžˆ40486976670 įš„įŽŽ 3 æŽĄå°čŊ•䚎 15:08:50Z、40510643634 įš„įŽŽ 2 æŽĄå°čŊ•䚎 15:16:27Zåđķäļ” 40486976670 åœĻčŊĨæŽĄå°čŊ•äļ­åیæ•ī成功。čŋ™čŊŽé—ŪéĒ˜å‡šåœĻ及čŊŧ拉取æ—ķé•ŋ期 PAT čĒŦįžŽæ—ķ拒įŧïžŒč€Œäļæ˜Ŋ发åļƒ ref 损坏。čŊĨ拉取åđķäļéœ€č́ PATïžšæĢ€å‡šæœŽčšŦå·ēčŪūį―Ū persist-credentials: falseïžŒč€ŒįœŸæ­Ģæ‰§čĄŒå†™å…Ĩįš„æ­ĨéŠĪ侚臩行åŪ‰č̅凭æŪ——发åļƒåˆ†æ”ŊåžšåˆķæŽĻ送通čŋ‡ gh auth setup-git åđķä―ŋį”Ļ PAT ä―œäļš GH_TOKEN创åŧš Release 和äļļ dispatch äđŸæ˜ū垏把 PAT ä―œäļš GITHUB_TOKEN äž å…Ĩã€‚įŽ°åœĻ及čŊŧ拉取æ”đį”Ļéŧ˜čŪĪįš„čŋčĄŒčŒƒå›īäŧĪį‰ŒïžŒį§ŧé™Ī乆åĪąčīĨįš„äūčĩ–åđķįžĐ小䚆凭æŪæšīéœēéĒ。

äŋŪåĪ

  • į§ŧé™Ī䚆发åļƒäŧŧåŠĄéĶ–æŽĄæĢ€å‡šįš„ PAT äŧĪį‰ŒčĶ†į›–é…į―Ū。朚å™Ļäšš PAT äŧåŠåŊžå‡šįŧ™æ‰§čĄŒæŽĻ送或写å…Ĩ Release įš„æ­ĨéŠĪ。
  • 新åĒžå·Ĩä―œæĩå…ƒæĩ‹čŊ•锁åŪšåŠčŊŧæĢ€å‡šä―ŋį”ĻčŋčĄŒčŒƒå›īäŧĪį‰ŒïžŒåđķ锁åۚ PAT įŧ§įŧ­į”Ļ䚎æŽĻ送äļŽåˆ›åŧš Release įš„æ­ĨéŠĪé˜ēæ­Ē后įŧ­æ”đåŠĻ悄悄æĒåĪåĪąčīĨ配į―Ū。

驌čŊ

  • npm run build —— 通čŋ‡
  • npm run typecheck —— 通čŋ‡
  • npm run lint —— 通čŋ‡
  • vitest čŋčĄŒ release-workflow、workflow-size、package-scripts、ai-release-notes-workflow、install-script、runtime-archive-cache —— 6 äļŠæ–‡äŧķ通čŋ‡ïžŒ555 äļŠæĩ‹čŊ•通čŋ‡ïžŒ20 äļŠč·ģčŋ‡
  • bash .github/scripts/check-workflow-size.sh —— 通čŋ‡ïžˆrelease.yml äļš 40032 å­—čŠ‚ïžŒåœĻ 4096 å­—čŠ‚åĒžé•ŋéĒåšĶ内
  • 变垂æŽĒ针把 token: '${{ secrets.CI_BOT_PAT }}' 加回发åļƒäŧŧåŠĄæĢ€å‡šåŽïžŒæ–°æĩ‹čŊ•åĪąčīĨ1 failed | 102 skippedæĒåĪæ”đåŠĻ后重新变äļš 103 passed
  • 原始åĪąčīĨæ˜Ŋ GitHub 托įŪĄįŽŊåĒƒįš„é‰ī权抖åŠĻ无æģ•åœĻ朎地åĪįŽ°ïž›å·Ĩä―œæĩå…ƒæĩ‹čŊ•æ˜Ŋæ›ŋäŧĢæ€§čŊæŪã€‚į”ąäšŽæœŽäžščŊæēĄæœ‰į”Ÿäš§å‡­æŪ朊åŪžé™…æīū发 dry-run 发åļƒã€‚
--- 🧠 Handled by **Qwen Code** · model/æĻĄåž‹ `deepseek-v4.1-flash-1017` · CLI `0.26.1`

@qwen-code-review-bot

qwen-code-review-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Qwen Triage finished — view run. See the stage comments in this thread for the result.

✅ Qwen Triage å·ēåŪŒæˆ —— æŸĨįœ‹čŋčĄŒã€‚įŧ“æžœč§æœŽįšŋįĻ‹äļ­įš„各é˜ķæŪĩčŊ„čŪšã€‚

@qwen-code-review-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR — I dug into this one properly, because a change to release credential plumbing is exactly the kind of thing that should not be waved through. The diagnosis holds up, and I could confirm it from the failed run rather than taking the description's word for it. I'm escalating to a maintainer rather than approving, for the policy reason at the bottom — not because I found a defect.

Template looks good ✓ — every required heading is present, the Risk & Scope bullets are filled in rather than boilerplate, and the Chinese section is a real translation.

Problem: observed, with hard evidence. #15726 is an open, machine-filed release failure. I pulled run 40486976670 attempt 2 and the Publish Release job did fail at the Checkout step with fatal: could not read Username for 'https://github.com': terminal prompts disabled and git exit 128 — three times in a row across the retry ladder (14:25:54, 14:26:14, 14:26:25). Attempt 3 then passed the identical step. So this is a real, reproduced transient failure, not theoretical hardening.

Direction: aligned, and I verified the mechanism independently rather than trusting the write-up.

  • The publish job declares contents: 'write', so the run-scoped token can certainly read a public repo. The capability argument in the description checks out.
  • The strongest evidence is inside the same failing run: six other jobs fetch the identical needs.prepare.outputs.release_sha through the checkout_release_validation anchor with no token override. On attempt 2, Quality Checks (Static) succeeded fetching SHA 39076719e221c096dd039ae5aa023d608ef397fb while the PAT-authenticated publish checkout failed beside it.
  • I checked whether the difference might be the credential plumbing rather than the token — it isn't. Both jobs' logs show the byte-identical auth setup: credentials written to a temp file plus four git config --local includeIf.gitdir:... entries. Same mechanism, same SHA, same run; only the token differed. That rules out persist-credentials: false as the culprit and leaves the PAT as the differentiator.
  • The specific symptom supports that reading too. A valid-but-unauthorized token yields 403; could not read Username means git was left with no usable credential and fell back to prompting, which is what a rejected PAT looks like under GIT_TERMINAL_PROMPT=0.
  • The change also moves this checkout toward the workflow's own stated design: push-release-branch already says "Keep the write credential unavailable until the push step needs it" and exports GH_TOKEN="${CI_BOT_PAT}" only at write time. The read-only checkout carrying the PAT was the outlier.

CHANGELOG: no direct reference — the only hit for these keywords upstream is an unrelated footer-badge change. Expected, since this is internal CI plumbing; the area is clearly relevant regardless.

Size: not applicable — neither file is under a Stage 0 core path. 2 production lines (release.yml +1/−1) and 18 test lines (release-workflow.test.js +18/−0).

Approach: minimal and correctly scoped. One wiring line plus a meta-test that pins both halves of the split, which satisfies the AGENTS.md rule about updating workflow meta-tests when CI definitions change. I checked the change against every existing pin that could have caught it, and none break:

  • uses shallow history only for validation jobs asserts publish fetch-depth: 0 — retained.
  • The checkout.with.ref pin over eight jobs asserts release_sha — retained.
  • package-scripts.test.js asserts the publish Checkout step contains persist-credentials: false — retained.
  • The exact-equality scriptCheckout.with assertion targets the scripts checkout, not this one.

No existing test pinned the removed token, so the new test is genuinely additive rather than a rewrite of a failing expectation.

Three non-blocking notes, none of which I'd hold the PR for:

  1. The description is wrong on one point, though the code is right. It states the workflow "still receives secrets.CI_BOT_PAT at job level for the write steps." There is no job-level PAT — it is passed per-step, and package-scripts.test.js explicitly forbids the PAT in the publish job header (publishJob.slice(0, indexOf('steps:')).not.toContain('CI_BOT_PAT')). Worth correcting so a future reader doesn't "restore" a job-level PAT and trip that pin.
  2. release-workflow.test.js (~line 296) justifies the full workspace wipe with "release checkouts carry CI_BOT_PAT and the npm OIDC id-token". After this change no checkout in the workflow carries the PAT, so that half of the rationale goes stale — the OIDC half still holds, so the wipe stays correct.
  3. The new test pins the push step and release creation, but not Trigger ECS runner qwen update, the third PAT consumer the description mentions.

Risk: no elevated Stage 1e signals — neither file matches the revert-correlated path set.

Why I'm escalating instead of approving. This is the gate's release/auth rule, not a judgement call about quality: changes touching the release pipeline or credential wiring go to a maintainer rather than being auto-approved. .github/CODEOWNERS agrees independently — /.github/workflows/release.yml sits under "Primary npm release workflows require core maintainer approval" and is owned by @pomelo-nwu and @wenshao. There's also a real verification ceiling here: the end-to-end proof is a production dry-run release, which neither the author nor this gate can run. The same-run evidence above is as far as static review can honestly go.

So: I think the change is correct and well-targeted, and I'd be comfortable with it — but the call belongs to the release owners. @pomelo-nwu (lower current load of the two owners; cc @wenshao), assigning to you. Two things would make this a quick yes: a dry-run release to confirm the publish checkout succeeds on the run-scoped token, and the one-line description fix in note 1.

Note on CI at the time of writing: nothing red, but the unit and integration suites (Test (ubuntu-latest), Integration Tests (no-AK, No Sandbox), web-shell E2E Smoke, TUI parity snapshots) were still running on 4bb937c9632e7c7226093d47b9793a70ddea4179, with the Installed npm matrix queued. Lint & Static and Managed Agent gate had passed. I have not waited on them and am not reporting a result that does not exist yet.

Stopping at the gate here — no code-review or verdict comment will follow from this run.

äļ­æ–‡čŊŽ

感č°ĒčīĄįŒŪčŋ™äļŠ PR 我čŪĪįœŸįœ‹čŋ‡äš†ïžŒå› äļšå‘åĩįĻ‹įš„å‡­æŪæŽĨįšŋæ­Ģæ˜Ŋäļčƒ―随äūŋæ”ūčŋ‡įš„æ”đåŠĻ。čŊŠæ–­æˆįŦ‹ïžŒč€Œäˆ‘æ˜ŊäŧŽåĪąčīĨįš„ run 里į›īæŽĨįĄŪčŪĪįš„ïžŒäļæ˜Ŋ及采äŋĄ PR 描čŋ°ã€‚我æēĄæœ‰į›īæŽĨæ‰đå‡†ïžŒč€Œæ˜Ŋč―ŽäšĪįŧĪč€…â€”â€”åŽŸå› æ˜Ŋäļ‹éĒé‚ĢæĄį­–į•Ĩč§„åˆ™ïžŒäļæ˜Ŋ因äļšå‘įŽ°äš†įžšé™·ã€‚

æĻĄæŋåیæ•ī ✓ —— 所有åŋ…需标éĒ˜éƒ―åœĻRisk & Scope äĄæ˜ŊåŪžåĄŦ而非æĻĄæŋåĨ—čŊïžŒäļ­æ–‡éƒĻ分æ˜Ŋᜟæ­Ģįš„įŋŧčŊ‘。

é—ŪéĒ˜ïžšå·ē观æĩ‹ïžŒäœ‰įĄŽčŊæŪ。 #15726 æ˜Ŋäļ€äļŠåž€åŊįŠķæ€įš„ã€æœšå™Ļ提äšĪįš„å‘åļƒåĪąčīĨ issue。我拉取䚆 run 40486976670 įš„įŽŽ 2 æŽĄå°čŊ•Publish Release äŧŧåŠĄįĄŪåŪžåœĻ Checkout æ­ĨéŠĪåĪąčīĨæŠĨ错 fatal: could not read Username for 'https://github.com': terminal prompts disabled、git é€€å‡šį  128 —— åœĻ重čŊ•é˜ķæĒŊäļŠčŋžįŧ­äļ‰æŽĄïžˆ14:25:54、14:26:14、14:26:25ïž‰ã€‚įŽŽ 3 æŽĄå°čŊ•同ä­ĨéŠĪ通čŋ‡äš†ã€‚所äŧĨčŋ™æ˜ŊᜟåŪžäļ”å·ēåĪįŽ°įš„åķ发åĪąčīĨäļæ˜ŊᐆčŪšæ€§åŠ å›šã€‚

æ–đ向åŊđé―ïžŒåđķäˆ‘į‹ŽįŦ‹éЌčŊäš†æœšåˆķæēĄæœ‰į›īæŽĨ采äŋĄæčŋ°ã€‚

  • publish äŧŧåŠĄåĢ°æ˜Žäš† contents: 'write'因æ­ĪčŋčĄŒčŒƒå›īäŧĪį‰Œå―“į„ķčƒ―čŊŧ取兎垀äŧ“嚓描čŋ°é‡Œįš„čƒ―力čŪščŊæˆįŦ‹ã€‚
  • æœ€æœ‰åŠ›įš„čŊæŪåœĻ同äļ€äļŠåĪąčīĨ run 内éƒĻïžšåĶåĪ–å…­äļŠäŧŧåŠĄé€ščŋ‡ checkout_release_validation 锚į‚đ拉取åیå…Ļį›ļåŒįš„ needs.prepare.outputs.release_shaäļ”æēĄæœ‰č͆ᛖ token。åœĻįŽŽ 2 æŽĄå°čŊ•äļ­ïžŒQuality Checks (Static) 成功拉取䚆 SHA 39076719e221c096dd039ae5aa023d608ef397fbïžŒč€Œæ—čūđä―ŋį”Ļ PAT é‰īæƒįš„ publish æĢ€å‡šåĪąčīĨ䚆。
  • 我æ ļæŸĨčŋ‡å·Ūåž‚æ˜ŊåĶ凚åœĻ凭æŪįŪĄé“č€ŒéžäŧĪį‰ŒæœŽčšŦ —— äļæ˜Ŋ。äļĪäļŠäŧŧåŠĄįš„æ—Ĩåŋ—æ˜ūįĪšåیå…Ļį›ļåŒįš„é‰ī权配į―Ū凭æŪ写å…Ĩäļīæ—ķæ–‡äŧķ加äļŠå››æĄ git config --local includeIf.gitdir:...。朚åˆķį›ļ同、SHA į›ļ同、run į›ļ同及有äŧĪį‰ŒäļåŒã€‚čŋ™æŽ’é™Ī乆 persist-credentials: false æ˜Ŋ元å‡ķå·Ūåž‚åŠčƒ―æĨ臩 PAT。
  • å…·ä―“į—‡įŠķäđŸæ”Ŋ持čŋ™äļŠåˆĪ断。äŧĪį‰Œæœ‰æ•ˆä―†æƒé™äļčķģ侚æŠĨ 403ïž›č€Œ could not read Username 意å‘ģį€ git 手里æēĄæœ‰åŊį”Ļ凭æŪ、退回到䚆äšĪ䚒垏提įĪšïžŒčŋ™æ­Ģæ˜Ŋ PAT čĒŦ拒įŧæ—ķåœĻ GIT_TERMINAL_PROMPT=0 äļ‹įš„čĄĻįŽ°ã€‚
  • čŋ™äļŠæ”đåŠĻäđŸčŪĐčŊĨæĢ€å‡šæ›īčīīčŋ‘å·Ĩä―œæĩč‡ŠčšŦæ—ĒåŪšįš„čŪūčŪĄïžšpush-release-branch å·ēįŧå†™æ˜Ž"Keep the write credential unavailable until the push step needs it"及åœĻ写å…Ĩæ—ķ才åŊžå‡š GH_TOKEN="${CI_BOT_PAT}"。及čŊŧæĢ€å‡šæšåļĶ PAT 才æ˜Ŋé‚ĢäļŠäū‹åĪ–ã€‚

CHANGELOGïžšæ— į›īæŽĨåŊđåš” —— äļᅩ‰čŋ™äš›å…ģé”ŪčŊåŠå‘―äļ­äļ€åĪ„æ— å…ģįš„ footer badge æ”đåŠĻ。čŋ™åœĻ意料äđ‹äļ­ïžŒå› äļščŋ™æ˜Ŋ内éƒĻ CI æŽĨįšŋ无čŪšåĶ‚ä―•čŊĨéĒ†åŸŸæ˜ūį„ķį›ļå…ģ。

**规æĻĄïžš**äļé€‚į”Ļ —— äļĪäļŠæ–‡äŧķéƒ―äļåœĻ Stage 0 æ ļåŋƒč·Ŋåū„äļ‹ã€‚2 čĄŒį”Ÿäš§äŧĢį ïžˆrelease.yml +1/−118 行æĩ‹čŊ•äŧĢį ïžˆrelease-workflow.test.js +18/−0。

æ–đæĄˆïžšæœ€å°äļ”čŒƒå›ī准įĄŪ。 äļ€čĄŒæŽĨįšŋæ”đåŠĻ加äļ€äļŠé”åŪšåˆ†å·ĨäļĪįŦŊįš„å…ƒæĩ‹čŊ•įŽĶ合 AGENTS.md äļ­"äŋŪæ”đ CI åۚäđ‰éœ€åŒæ­Ĩæ›ī新å·Ĩä―œæĩå…ƒæĩ‹čŊ•"įš„čĶæą‚ã€‚æˆ‘æŠŠæ”đåŠĻåŊđį…§äš†æ‰€æœ‰åŊčƒ―æ‹Ķä―åŪƒįš„æ—Ē有断čĻ€ïžŒéƒ―äļäžščĒŦį ī坏

  • uses shallow history only for validation jobs 断čĻ€ publish įš„ fetch-depth: 0 —— äŋį•™ã€‚
  • č͆ᛖå…ŦäļŠäŧŧåŠĄįš„ checkout.with.ref 断čĻ€ release_sha —— äŋį•™ã€‚
  • package-scripts.test.js 断čĻ€ publish įš„ Checkout æ­ĨéŠĪ包åŦ persist-credentials: false —— äŋį•™ã€‚
  • é‚ĢæĄå…Ļį­‰æŊ”čūƒįš„ scriptCheckout.with 断čĻ€é’ˆåŊđįš„æ˜Ŋč„šæœŽæĢ€å‡šïžŒäļæ˜Ŋčŋ™äļŠã€‚

æēĄæœ‰äŧŧä―•æ—Ē有æĩ‹čŊ•锁åۚčĒŦį§ŧé™Īįš„ token所äŧĨ新æĩ‹čŊ•æ˜Ŋᜟæ­Ģįš„åĒžé‡ïžŒč€Œäļæ˜Ŋæ”đ写äļ€äļŠåŽŸæœŽåĪąčīĨįš„æœŸæœ›ã€‚

äĄäļé˜ŧåĄžįš„æ„č§ïžŒæˆ‘éƒ―äļäžšäļšæ­ĪåĄä―čŋ™äļŠ PRïžš

  1. 描čŋ°æœ‰äļ€åĪ„äļå‡†įĄŪïžŒä―†äŧĢ᠁æ˜ŊåŊđįš„ã€‚ 描čŋ°į§°å·Ĩä―œæĩ"åœĻäŧŧåŠĄįš§åˆŦäŧäžšæ”ķ到 secrets.CI_BOT_PAT äū›å†™å…Ĩæ­ĨéŠĪä―ŋį”Ļ"。åŪžé™…äļŠåđķäļå­˜åœĻäŧŧåŠĄįš§ PAT —— åۃæ˜Ŋ逐æ­ĨéŠĪäž å…Ĩįš„ïžŒč€Œäļ” package-scripts.test.js 明įĄŪį́æ­Ē publish äŧŧåŠĄåĪīéƒĻå‡šįŽ° PATpublishJob.slice(0, indexOf('steps:')).not.toContain('CI_BOT_PAT')。åŧščŪŪæ›īæ­ĢäŧĨ免后æĨ者"æĒåĪ"äļ€äļŠäŧŧåŠĄįš§ PAT č€Œæ’žäļŠčŋ™æĄæ–­čĻ€ã€‚
  2. release-workflow.test.jsįšĶ 296 čĄŒïž‰į”Ļ"release checkouts carry CI_BOT_PAT and the npm OIDC id-token"æĨčŪščŊæ•īį›Ūå―•æ“Ķé™Īįš„åŋ…čĶæ€§ã€‚æœŽæ”đåŠĻäđ‹åŽå·Ĩä―œæĩé‡Œå·ēæēĄæœ‰æĢ€å‡šæšåļĶ PAT因æ­Īčŋ™æĄį†į”ąįš„äļ€åŠäžšåĪąæ•ˆ —— OIDC é‚Ģäļ€åŠäŧį„ķ成įŦ‹ïžŒæ‰€äŧĨæ“Ķé™Īé€ŧčū‘朎čšŦäūį„ķæ­ĢįĄŪ。
  3. 新æĩ‹čŊ•锁åŪšäš†æŽĻ送æ­ĨéŠĪ和创åŧš Release æ­ĨéŠĪïžŒä―†æēĄæœ‰é”åۚ Trigger ECS runner qwen updateäđŸå°ąæ˜Ŋ描čŋ°äļ­æåˆ°įš„įŽŽäļ‰äļŠ PAT æķˆčīđæ–đ。

éĢŽé™Đïžšæ—  Stage 1e å‡įš§äŋĄå· —— äļĪäļŠæ–‡äŧķéƒ―äļåŒđ配äļŽå›žæŧšį›ļå…ģįš„č·Ŋåū„集合。

äļšäŧ€äđˆé€‰æ‹ĐäŠĨ而äļæ˜Ŋæ‰đ准。 čŋ™æ˜Ŋå…ģåĄįš„ release/auth č§„åˆ™ïžŒäļæ˜ŊåŊđčīĻé‡įš„äļŧ观åˆĪæ–­ïžšč§Ķ及发åĩįĻ‹æˆ–å‡­æŪæŽĨįšŋįš„æ”đåŠĻåš”äšĪį”ąįŧĪ者å†ģåŪšïžŒč€Œéžč‡ŠåŠĻæ‰đ准。.github/CODEOWNERS äđŸį‹ŽįŦ‹å°čŊäš†čŋ™äļ€į‚đ —— /.github/workflows/release.yml ä―äšŽ "Primary npm release workflows require core maintainer approval" äđ‹äļ‹ïžŒå―’åąž @pomelo-nwu 和 @wenshao。čŋ™é‡Œčŋ˜å­˜åœĻᜟåŪžįš„éŠŒčŊäļŠé™ïžšįŦŊ到įŦŊčŊæŪæ˜Ŋäļ€æŽĄį”Ÿäš§ dry-run 发åļƒïžŒä―œč€…和朎å…ģåĄéƒ―æ— æģ•æ‰§čĄŒã€‚äļŠéĒé‚Ģäŧ―同 run čŊæŪå·ēįŧæ˜Ŋ静态åŪĄæŸĨčƒ―čŊšåŪžæŠĩčūūįš„čūđį•Œã€‚

įŧ“čŪšïžšæˆ‘čŪĪäļščŋ™äļŠæ”đåŠĻæ­ĢįĄŪäļ”į›Ū标įēū准我äļŠäššæ˜ŊåŊäŧĨæŽĨå—įš„ —— ä―†čŋ™äļŠå†ģåŪšåąžäšŽå‘åĩįĻ‹įš„ owner。@pomelo-nwuäļĪä― owner äļ­å―“前čīŸč――čūƒä―Žįš„äļ€ä―ïž›æŠ„送 @wenshaoå·ē把 PR 指æīūįŧ™ä― ã€‚有äļĪäŧķäš‹čƒ―čŪĐåۃåūˆåŋŦ通čŋ‡ïžšäļ€æŽĄ dry-run 发åļƒäŧĨįĄŪčŪĪ publish æĢ€å‡šåœĻčŋčĄŒčŒƒå›īäŧĪį‰ŒäˆåŠŸïžŒäŧĨåŠįŽŽ 1 æĄé‚Ģäļ€čĄŒæčŋ°æ›īæ­Ģ。

å…ģ䚎撰写æ—ķįš„ CI įŠķ态æēĄæœ‰įšĒč‰ēïžŒä―†å•å…ƒäļŽé›†æˆæĩ‹čŊ•åĨ—äŧķTest (ubuntu-latest)、Integration Tests (no-AK, No Sandbox)、web-shell E2E Smoke、TUI parity snapshotsåœĻ 4bb937c9632e7c7226093d47b9793a70ddea4179 äļŠäŧåœĻčŋčĄŒïžŒInstalled npm įŸĐé˜ĩåĪ„äšŽæŽ’é˜Ÿäļ­ã€‚Lint & Static äļŽ Managed Agent gate å·ē通čŋ‡ã€‚我æēĄæœ‰į­‰åū…åۃäŧŽïžŒäđŸäļäžšåŽŧæą‡æŠĨäļ€äļŠå°šäļå­˜åœĻįš„įŧ“果。

æœŽæŽĄčŋčĄŒåˆ°æ­Īäļšæ­Ē —— äļäžšå†æœ‰äŧĢ᠁åŪĄæŸĨ或įŧ“čŪščŊ„čŪšã€‚

— Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 4bb937c9632e7c7226093d47b9793a70ddea4179 · re-run with @qwen-code /triage

@wenshao

wenshao commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

Verdict: merge-ready — 47/47 scripted assertions passed at head 4bb937c9632e7c7226093d47b9793a70ddea4179.

äļ­æ–‡æ‘˜č́

įŧ“čŪšïžšmerge-ready47/47 č„šæœŽåŒ–æ–­čĻ€é€ščŋ‡ïž‰ïžŒéЌčŊåŊđ蹥äļš head 4bb937c9č―ŪæŽĄåž€å§‹äļŽå‘åļƒå‰äļĪæŽĄčŊŧ取äļ€č‡ī。čŋ™æ˜Ŋ朎地įŧĪ者č―Ūäļæ˜Ŋ CI verify äš§į‰Đ。

äļ­åŋƒäļŧ垠成įŦ‹ïžšPublish Release įš„åŠčŊŧæĢ€å‡šäļå†ä―ŋį”Ļé•ŋ期 bot PATæ”đį”Ļ actions/checkout įš„éŧ˜čŪĪ github.tokenïž›PAT 及äŋį•™åœĻäļ‰äļŠå†™å…Ĩæ­ĨéŠĪpush release branch、Create GitHub Release、äļļ dispatch。

  • A/Bïžšbase å·Ĩä―œæĩ + PR 臩åļĶįš„æ–°æĩ‹čŊ• → įšĒåĪąčīĨäŋĄæŊæ­Ģæ˜Ŋ expected '${{ secrets.CI_BOT_PAT }}' to be undefinedïž›head å·Ĩä―œæĩ → 102 passed / 1 skipped。trial merge åˆ°å―“å‰ main6f53133后同样 102/1 skipped。
  • 变垂įŸĐé˜ĩ 8/8ïžšM1–M4把 PAT 加回æĢ€å‡šã€persist-credentials æ”đ true、æŽĻ送æ­ĨåŽŧ掉 PAT、Release 创åŧšæ”đį”Ļ github.tokenå…ĻéƒĻčĒŦ杀同文äŧķæ­ĢæŽ§ïžˆæ”đ concurrency groupäđŸčĒŦ杀。M5 存æīŧ——把äļļ dispatch æ”đį”Ļ github.tokenåĨ—äŧķäŧå…Ļįŧŋïžˆč§åŧščŪŪ 1。M6æ˜ū垏 token: ${{ github.token }}䞚čĒŦåˆĪįšĒčŋ™æ˜Ŋæĩ‹čŊ•åŊđ"äļåū—č͆ᛖ token"įš„åˆŧ意æ”ķįī§ã€‚
  • ᜟåŪžįŽŊåĒƒïžšå–å›žčĒŦ pin įš„ actions/checkout@df4cb1c= v6.0.3dist/index.js blob SHA æ ĄéŠŒäļ€č‡īåŪžé™…æ‰§čĄŒäļ‰č‡‚无效 PAT → 䚋故同æŽū could not read Username ... terminal prompts disabled + git 128 + 2 æŽĄé‡čŊ•ïž›įĐš tokensecret 朊配į―Ūæ—ķįš„å―Ēæ€ïž‰â†’ į›īæŽĨ拒æĢ€ïž›åŒŋ名 ls-remote → æˆåŠŸã€‚į”Ÿäš§ run 40486976670ïžšattempt 2 及åĪąčīĨåœĻ Checkoutattempt 3 同äļ€ job 里 7 æŽĄéŧ˜čŪĪ token æĢ€å‡šå…ĻéƒĻ成功。
  • äļé˜ŧåĄžįš„åŧščŪŪïžš1新æĩ‹čŊ•æēĄé’‰ä―įŽŽäļ‰äļŠ PAT æķˆčīđį‚đ Trigger ECS runner qwen updateïž›2finalize-release.yml äŧæœ‰åŒæŽū「及čŊŧæĢ€å‡š + é•ŋ期 PAT」å…Ļäŧ“ 131 äļŠ checkout 里及å‰Đčŋ™ 1 äļŠïž›3æĩ‹čŊ•æ–‡äŧķ ~296 行æģĻ释å·ēčŋ‡æ—ķïž›4PR 描čŋ°å†™ PAT æ˜Ŋ "job level" äļ‹å‘åŪžé™…æ˜Ŋ逐 step įš„ env。
  • 朊čĶ†į›–ïžšæēĄæœ‰æīūå‘įœŸåŪž dry-run 发åļƒïž›æœŽæœšæ— æģ•é“ļ造 runner įš„ github.tokenhead äū§é  action 臩čšŦéŧ˜čŪĪ倞 + į”Ÿäš§éŧ˜čŪĪ token æĢ€å‡šå–čŊïž‰ïž›åĨ—äŧķ及åœĻ macOS č·‘ïžˆGNU-realpath į”Ļäū‹č·ģčŋ‡ïž‰ïž›æœŠæ‰§čĄŒ release å·Ĩä―œæĩįŦŊ到įŦŊ。

Central claim and A/B

The PR's central claim is narrow: the publish job's first checkout is a read-only fetch, it already disables credential persistence, and it is the last read-only fetch in release.yml authenticated with the long-lived bot PAT. Removing that token: override should leave the fetch on the run-scoped github.token without changing what the fetch can reach, while every writing step keeps the PAT.

Two structural facts make the claim testable rather than rhetorical, both scripted:

  • actions/checkout@df4cb1c069e1 is exactly the v6.0.3 tag commit, and its own action.yml declares token with default: ${{ github.token }} — so an absent with.token is the run-scoped token, not an empty credential.
  • On base, release.yml had exactly one PAT checkout (this one); after the change it has zero, and the three write steps still receive the PAT. The publish job has no job-level env block in either revision.
Cell Oracle Base 32f6713ce6 Head 4bb937c963
A1 PR's new meta-test, run against each workflow expected '${{ secrets.CI_BOT_PAT }}' to be undefined → 1 failed pass
A2 full focused suite (scripts/tests/release-workflow.test.js) 101 passed, 1 skipped (102) 102 passed, 1 skipped (103)
A3 trial merge: git diff base..head applied to current origin/main 6f53133d6d — 102 passed, 1 skipped (103)
A4 YAML assertions (token placement, ref/depth unchanged, census) — 16/16

Witness for A1–A3: 04-suite-counts.png; the A1 failure text and the matrix in 03-mutation-matrix.png.

Mutation matrix

Each row applies exactly one literal edit to the head workflow, runs the selected test through the repo's own vitest config, then restores with git checkout HEAD -- and asserts a clean tree. The selected-test status comes from the JSON reporter, and every row reports matched=1 (no zero-collection false green).

Row Mutation Expected Observed
BASE-YAML base workflow + head test test fails failed ✔
M1 re-add token: secrets.CI_BOT_PAT to the publish checkout fails failed ✔
M2 persist-credentials: true on the publish checkout fails failed ✔
M3 drop CI_BOT_PAT from push_release_branch fails failed ✔
M4 Create GitHub Release uses github.token fails failed ✔
M5 downstream dispatch uses github.token passes passed — coverage gap
M6 explicit token: ${{ github.token }} on the checkout fails failed (strict-by-design)
CTRL drop is_dry_run from the publish concurrency group (different test, same file) fails failed ✔

M5 is the one survivor, and it is the same point the triage comment raised: the PR body says the PAT stays for the downstream dispatch, but the new test only pins the push step and release creation.

Witness: 03-mutation-matrix.png.

Real-action arms and production evidence

The pinned action was downloaded at its exact SHA and executed with a scrubbed HOME and no host credentials. dist/index.js hashes to 906b59aff44f1df11c154e1e25397c7fafc12da5, the same blob the SHA serves.

Arm Input Result
A no credential, anonymous git ls-remote exit 0, refs/heads/main returned
B empty token (what an unset CI_BOT_PAT secret renders to) exit 1, ::error::Input required and not supplied: token
C rejected PAT through the real action exit 1, fatal: could not read Username for 'https://github.com': terminal prompts disabled, git 128, 2 retries

Arm C is the incident mechanism reproduced locally; the retry cadence (18 s, 16 s) matches the production log (20 s, 11 s).

Production run 40486976670:

  • attempt 2's Publish Release failed only at Checkout, three times with the same message and exit 128;
  • attempt 3 completed the same step and ran seven Checkout release workflow scripts steps — the workflow's other checkout, which has no token override and therefore already used the default token — all green. That is the run-scoped token fetching this repository inside the same job, on the same commit range.

Witness: 01-actions-checkout-arms.png and 02-production-run-logs.png.

Corrections

These correct descriptions, not code. The triage stage-1 comment raised the first three; this round confirms each independently and adds the measurement.

  1. "Still receives secrets.CI_BOT_PAT at job level" is inaccurate. The publish job has no job-level env block in base or head; the PAT is per-step env on the three write steps only. package-scripts.test.js already forbids a job-level PAT in the publish header, so a future "restore" based on the sentence would trip that pin.
  2. The rationale comment above canonicalWipe in the test file (~line 296) is now half stale. It says "release checkouts carry CI_BOT_PAT and the npm OIDC id-token". After this PR, zero checkouts in release.yml carry the PAT (census). The OIDC half still holds, so the full wipe remains correct — only the rationale needs a word.
  3. The test does not pin the third PAT consumer. See M5 above.

One measurement note: the PR body's "103 tests pass" is the Linux figure. On this macOS host the same file is 102 passed + 1 skipped, because one case is gated on GNU realpath (test.skipIf(!hasGnuRealpath ...)). Nothing is wrong with the count; the platform differs.

Findings

All findings are non-blocking; none argues against merging.

S1 — the new test leaves the downstream dispatch unpinned. Trigger ECS runner qwen update still carries GITHUB_TOKEN: secrets.CI_BOT_PAT, and the PR body says the PAT remains for it, but the test asserts only push_release_branch and Create GitHub Release and Tag. Mutation M5 (switching that step to github.token) keeps the suite green, so a future edit could silently drop the PAT there and stop the downstream release-event workflows from firing.

Suggested addition (one assertion, same shape as the existing two):

expect(
  steps.find((step) => step.name === 'Trigger ECS runner qwen update').env
    .GITHUB_TOKEN,
).toBe('${{ secrets.CI_BOT_PAT }}');

S2 — the same read-only-PAT pattern survives in finalize-release.yml. A census over all 131 actions/checkout steps leaves exactly one PAT checkout: finalize-release.yml → finalize → Checkout release branch, with persist-credentials: false. Its later write re-authenticates independently (export GH_TOKEN="${CI_BOT_PAT}"; gh auth setup-git before the changelog push), so the PAT on that checkout is fetch-only — the same "auth dependency without added access" the issue describes. A transient PAT rejection would fail finalization the same way. This is a follow-up, not a defect in this PR.

N1 (note, not a request). M6 shows the test fails on any explicit token, including token: ${{ github.token }}. That matches the stated contract ("must not override the token"), so it looks deliberate; flagging it only so the next author is not surprised.

Not covered

  • No production dry-run release was dispatched (needs release credentials; a local round should not mint them).
  • The runner-minted github.token cannot be reproduced locally. The head side rests on the action's own default (${{ github.token }}) plus the production default-token checkouts above, not on executing with a real run-scoped token.
  • The release workflow was not executed end to end; only the checkout credential path was driven.
  • Suite runs are macOS/arm64 (Node 24.18.1) only; the GNU-realpath case is skipped there, and Windows/Linux counts are not measured.
  • finalize-release.yml was inspected, not modified or run.
  • Everything in A4 is YAML/AST-level; no GitHub-side permission evaluation was performed beyond the production observations.

Methodology

Three detached worktrees in a shallow clone: base 32f6713ce6 (gh pr view baseRefOid), head 4bb937c963, and a trial tree at origin/main 6f53133d6d with git diff base..head applied (git apply --check clean; main had not touched either changed file since base, verified by an empty git diff --stat). No dependency changes, so the root node_modules was reused and each tree resolved upward. The workflow meta-test was run with the repo's own scripts/tests/vitest.config.ts and JSON reporter; mutation rows apply one literal replacement with an exactly-one-match guard and restore via git checkout HEAD -- with a clean-tree assertion. Gates are actionlint, prettier --check, and eslint on the changed files, each paired with a planted violation. The prettier liveness check caught a real vacuity trap worth recording: running it from the repository root on an absolute path under tmp/ matches .gitignore's tmp/ and checks zero files, so the gate is run from inside the arm tree. Raw logs, harnesses, and the mutation/suite JSON are in this round's artifact directory; the four figures are hosted on wenshao/qwen-code branch assets/pr15728-verify, path r1-4bb937c/.

Figures

Pinned actions/checkout v6.0.3 credential arms

Production run 40486976670: failed PAT checkout vs default-token checkout

Mutation matrix at head 4bb937c

Focused meta-test suite: base / head / trial merge

@qwen-code-dev-bot

qwen-code-dev-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

✅ AutoFix round 1 finished — view run. See this round's report below.

äļ­æ–‡čŊŽ

✅ AutoFix įŽŽ 1 č―Ūå·ēåŪŒæˆ —— æŸĨįœ‹čŋčĄŒã€‚朎č―ŪæŠĨå‘Šč§ä–đ。

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator Author

ðŸĪ– Addressed the latest review feedback (round 1/10). What changed, and what I pushed back on: · å·ēåĪ„į†æœ€æ–°čŊ„åŪĄåéĶˆïžˆįŽŽ 1/10 č―Ū。æ”đåŠĻ内åŪđäļŽæˆ‘反éĐģäŋį•™äđ‹åĪ„åĶ‚äļ‹ïžš

Round summary — PR #15728 (head 4bb937c963)

Feedback in this window is one issue-level comment from @wenshao (verdict merge-ready). There were no review bodies and no inline comments, so nothing was resolved or replied to on a thread; every point is answered here.

I re-verified each point at head 4bb937c963 before acting on it.

S1 — downstream dispatch unpinned: refuted by a mutation probe, escalated as an open question

The finding says mutation M5 (switching Trigger ECS runner qwen update to github.token) "keeps the suite green", so a future edit could silently drop the PAT there. That premise does not hold for the full suite at this head.

Probe: in .github/workflows/release.yml, replace only that step's GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}' with '${{ github.token }}', then run the whole scripts/tests/release-workflow.test.js (not just the new test). Result: 1 failed / 108 passed — release workflow > keeps a dispatch failure from failing an already-published release fails at release-workflow.test.js:3682, because it pins the exact continue-on-error: true + env: + GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}' triplet. Deleting that GITHUB_TOKEN line outright fails the same test the same way (1 failed / 108 passed), so both a swap and a drop are caught. The tree was restored with git checkout -- .github/workflows/release.yml, and git status is clean.

So the new test is deliberately narrow, but the third PAT consumer is not unpinned: the existing test already kills M5. I did not add the suggested assertion because it would only duplicate that coverage. Open question for the author: was the intent to make the new test self-contained, or to close a real suite-level gap? If it is the former, I will add the five-line assertion next round; if the latter, no change is needed.

S2 — same read-only-PAT pattern in finalize-release.yml: verified, deferred

Confirmed at line 53: the finalize job's Checkout release branch passes CI_BOT_PAT to a persist-credentials: false fetch, and its Regenerate CHANGELOG.md step re-authenticates with gh auth setup-git before pushing, so the PAT on the checkout is fetch-only. A census over the 131 actions/checkout steps in .github/workflows finds exactly one CI_BOT_PAT override: this one. It is outside this PR's footprint, so it is recorded in deferred-findings.json for the follow-up queue rather than changed here.

N1 — explicit token: ${{ github.token }} fails: acknowledged

That is the intended contract ("must not override the token"), so no change.

Corrections

  1. PR body: "job level" is wrong. The publish job has no job-level PAT; it is per-step env on push_release_branch, Create GitHub Release and Tag, and Trigger ECS runner qwen update. I cannot edit the PR body from this run (no GitHub credentials); the corrected sentence is: "The PAT stays exported per step to the three steps that write: push release branch, Create GitHub Release, and the downstream dispatch."
  2. Stale canonicalWipe rationale: fixed. The comment now says release checkouts persist credentials and the publish job can mint the npm OIDC id-token, so the full wipe stays justified without claiming a checkout carries the PAT. This is the round's only code change.
  3. Third PAT consumer: same as S1 (escalated above).

Net diff for the round: 0 added/removed lines (a 6-line comment reflow in a test file the PR already modifies).

Verification

  • HOME=/tmp/fakehome npm run build — passed. (The sandbox HOME is read-only, so corepack's cache was redirected to a writable directory.)
  • HOME=/tmp/fakehome npm run typecheck — passed.
  • HOME=/tmp/fakehome npm run lint — passed.
  • HOME=/tmp/fakehome npx vitest run --config ./scripts/tests/vitest.config.ts release-workflow — 2 files / 109 tests passed (release-workflow.test.js: 103).
  • HOME=/tmp/fakehome npx prettier --check scripts/tests/release-workflow.test.js — passed.
  • Mutation probes described above (swap and drop) — 1 failed / 108 passed each, then reverted; the final focused run above is green.
  • Checkout census (Node + YAML parse over .github/workflows) — 131 actions/checkout steps, 1 CI_BOT_PAT override (finalize-release.yml), 1 explicit github.token override (qwen-triage.yml).
äļ­æ–‡čŊŽ

朎č―Ūæ€ŧįŧ“ —— PR #15728head 4bb937c963

æœŽįŠ—åĢįš„åéĶˆåŠæœ‰ @wenshao įš„äĄ issue įš§čŊ„čŪšïžˆįŧ“čŪš merge-ready。æēĄæœ‰ review bodyäđŸæēĄæœ‰ inline comment因æ­ĪæēĄæœ‰įšŋįĻ‹éœ€č́č§Ģå†ģ或回åĪïž›æŊäļŠį‚đéƒ―åœĻčŋ™é‡Œä―œį­”。

åŠĻå·Ĩ前我åœĻ head 4bb937c963 äļŠé€æĄåĪæ ļ乆æŊäļŠį‚đ。

S1 —— äļļ dispatch 朊čĒŦ锁åŪšïžšå˜åž‚æŽĒ针čŊäžŠïžŒč―Žäļšå…Žåž€é—ŪéǘäŠĨ

čŊĨå‘įŽ°į§°å˜åž‚ M5把 Trigger ECS runner qwen update æ”đį”Ļ github.token"æ•īåĨ—æĩ‹čŊ•äŧį„ķå…Ļįŧŋ"因æ­Ī朊æĨæŸæŽĄäŋŪæ”đåŊčƒ―æ‚„æ‚„äļǿމ PAT。čŋ™äļŠå‰æåœĻå―“å‰ head įš„åیæ•īåĨ—äŧķäļŠäļæˆįŦ‹ã€‚

æŽĒ针及把 .github/workflows/release.yml äļ­čŊĨæ­ĨéŠĪįš„ GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}' æ”đ成 '${{ github.token }}'į„ķ后čŋčĄŒæ•īäļŠ scripts/tests/release-workflow.test.jsäļæ˜ŊåŠč·‘æ–°æĩ‹čŊ•。įŧ“果1 åĪąčīĨ / 108 通čŋ‡ —— release workflow > keeps a dispatch failure from failing an already-published release åœĻ release-workflow.test.js:3682 åĪąčīĨ因äļšåŪƒé’‰ä―äš† continue-on-error: true + env: + GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}' čŋ™įŧ„原文。į›īæŽĨ删掉čŊĨ GITHUB_TOKEN 行äđŸäžščŪĐ同äļ€äļŠæĩ‹čŊ•äŧĨ同样æ–đ垏åĪąčīĨ1 åĪąčīĨ / 108 通čŋ‡ïž‰ïžŒæ‰€äŧĨ无čŪšæ˜Ŋæ›ŋæĒčŋ˜æ˜Ŋ删é™Īéƒ―äžščĒŦæ‹Ķä―ã€‚éšåŽå·ēį”Ļ git checkout -- .github/workflows/release.yml čŋ˜åŽŸïžŒgit status åđē净。

所äŧĨ新æĩ‹čŊ•įĄŪåŪžåŠč͆ᛖäļĪäļŠå†™å…Ĩæ­ĨéŠĪïžŒä―†įŽŽäļ‰äļŠ PAT æķˆčīđæ–đåđķäļæ˜Ŋ无䚚锁åۚ —— æ—Ē有æĩ‹čŊ•å·ēįŧčƒ―杀掉 M5。我æēĄæœ‰æ·ŧ加åŧščŪŪé‡Œįš„æ–­čĻ€ïžŒå› äļšåŪƒåŠäžšé‡åĪé‚Ģäŧ―čĶ†į›–ã€‚į•™įŧ™ä―œč€…įš„é—Ūéǘæ˜Ŋ意å›ūæ˜ŊčŪĐæ–°æĩ‹čŊ•臊čšŦåیæ•īčŋ˜æ˜ŊčĄĨäļ€äļŠįœŸåŪžįš„åĨ—äŧķįžšåĢåĶ‚æžœæ˜Ŋå‰č€…ïžŒäļ‹äļ€č―Ūæˆ‘čĄĨäļŠé‚Ģäš”čĄŒæ–­čĻ€ïž›åĶ‚æžœæ˜ŊåŽč€…ïžŒåˆ™æ— éœ€æ”đåŠĻ。

S2 —— finalize-release.yml é‡ŒåŒæ ·įš„åŠčŊŧ PAT æĻĄåžïžšå·ēæ ļåŪžïžŒč―ŽäļšåŽįŧ­éĄđ

å·ēåœĻįŽŽ 53 行įĄŪčŪĪïžšfinalize äŧŧåŠĄįš„ Checkout release branch äŧæŠŠ CI_BOT_PAT äž įŧ™äļ€äļŠ persist-credentials: false įš„åŠčŊŧæ‹‰å–ïžŒč€ŒåŪƒįš„ Regenerate CHANGELOG.md æ­ĨéŠĪ侚åœĻæŽĻ送前į”Ļ gh auth setup-git 重新é‰ī权因æ­ĪæĢ€å‡šäļŠįš„ PAT 及į”Ļ䚎拉取。åŊđ .github/workflows äļ­ 131 äļŠ actions/checkout æ­ĨéŠĪ做æ™ŪæŸĨ及æ‰ū到äļ€åĪ„ CI_BOT_PAT čĶ†į›–ïžšå°ąæ˜Ŋčŋ™äļ€åĪ„ã€‚åۃäļåœĻ朎 PR įš„æ”đåŠĻ范å›ī内因æ­ĪčŪ°å―•åˆ° deferred-findings.json äšĪįŧ™åŽįŧ­é˜Ÿåˆ—朎č―Ūäļæ”đ。

N1 —— æ˜ū垏 token: ${{ github.token }} 侚įšĒïžšå·ēįŸĨ悉

čŋ™æ­Ģæ˜Ŋåˆŧ意æ”ķįī§įš„åĨ‘įšĶ"äļåū—č͆ᛖ token"无需æ”đåŠĻ。

æ›īæ­ĢéĄđ

  1. PR 描čŋ°é‡Œįš„"job level"äļå‡†įĄŪ。 publish äŧŧåŠĄæēĄæœ‰äŧŧåŠĄįš§ PATïž›åۃæ˜Ŋ逐æ­ĨéŠĪ envïžŒåŠå‡šįŽ°åœĻ push_release_branch、Create GitHub Release and Tag 和 Trigger ECS runner qwen update ä­Ĩ。朎č―ŪæēĄæœ‰ GitHub 凭æŪ无æģ•įž–čū‘ PR 描čŋ°ïž›æ›īæ­ĢåŽįš„åĨ子æ˜Ŋïžš"The PAT stays exported per step to the three steps that write: push release branch, Create GitHub Release, and the downstream dispatch."
  2. canonicalWipe įš„čŋ‡æ—ķį†į”ąïžšå·ēäŋŪæ­Ģ。 æģĻé‡ŠįŽ°åœĻå†™įš„æ˜Ŋ release æĢ€å‡šäžšæŒäđ…化凭æŪ、publish äŧŧåŠĄåŊäŧĨé“ļ造 npm OIDC id-token因æ­Īæ•īį›Ūå―•æ“Ķé™Īäūį„ķ成įŦ‹ïžŒåŒæ—ķäļå†å̰᧰æĢ€å‡šæšåļĶ PAT。čŋ™æ˜Ŋ朎č―Ūå”Ŋäļ€įš„äŧĢ᠁æ”đåŠĻ。
  3. įŽŽäļ‰äļŠ PAT æķˆčīđæ–đ同 S1å·ēåœĻä–‡äŠĨ。

朎č―Ū净 diffïžšæ–°åĒž/删é™Ī 0 čĄŒïžˆåŠæ˜ŊåœĻ PR å·ēįŧäŋŪæ”đįš„æĩ‹čŊ•æ–‡äŧķ里重排䚆 6 行æģĻ释。

驌čŊ

  • HOME=/tmp/fakehome npm run build —— 通čŋ‡ã€‚æē™įŪąé‡Œįš„ HOME 及čŊŧ因æ­Ī把 corepack įž“å­˜æŒ‡å‘äš†åŊ写į›Ūå―•ã€‚ïž‰
  • HOME=/tmp/fakehome npm run typecheck —— 通čŋ‡ã€‚
  • HOME=/tmp/fakehome npm run lint —— 通čŋ‡ã€‚
  • HOME=/tmp/fakehome npx vitest run --config ./scripts/tests/vitest.config.ts release-workflow —— 2 äļŠæ–‡äŧķ / 109 äļŠæĩ‹čŊ•通čŋ‡ïžˆrelease-workflow.test.jsïžš103。
  • HOME=/tmp/fakehome npx prettier --check scripts/tests/release-workflow.test.js —— 通čŋ‡ã€‚
  • äļŠčŋ°å˜åž‚æŽĒ针æ›ŋæĒäļŽåˆ é™ĪäļĪč‡‚ïž‰â€”â€” 各äļš 1 åĪąčīĨ / 108 通čŋ‡ïžŒéšåŽčŋ˜åŽŸïž›äļŠéĒ最后äļ€æŽĄčšį„ĶčŋčĄŒå…Ļįŧŋ。
  • æĢ€å‡šæ™ŪæŸĨNode + YAML č§Ģ析 .github/workflows—— 131 äļŠ actions/checkout æ­ĨéŠĪ1 åĪ„ CI_BOT_PAT čĶ†į›–ïžˆfinalize-release.yml1 åĪ„æ˜ū垏 github.token čĶ†į›–ïžˆqwen-triage.yml。
Base-conflict check · 埚分æ”Ŋå†ēįŠæĢ€æŸĨ: no conflict with main. · äļŽ main 无å†ēįŠã€‚

Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有įĐščŊ·åĪåŪĄïž›įŽŽ 10 č―Ū后朎 bot 停æ­Ēåđķ将 PR äšĪįŧ™äššå·Ĩ。


🧠 Handled by Qwen Code · model/æĻĄåž‹ deepseek-v4.1-flash-1017 · CLI 0.26.2

@wenshao
wenshao enabled auto-merge (squash) October 28, 2026 01:59

@qqqys qqqys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical-only scan at head d1632c1a. Verdict: APPROVE — no prior blocking findings exist on this PR, and the current scan found no provable blocking defect.

What the change does

One production line: the publish job's read-only Checkout in .github/workflows/release.yml:656-662 drops token: '${{ secrets.CI_BOT_PAT }}' and falls back to the run-scoped GITHUB_TOKEN, keeping ref, fetch-depth: 0 and persist-credentials: false unchanged.

Why the run-scoped token is sufficient here

  • The publish job declares permissions: contents: 'write' (:646-647), so the default token can read this repository; the checkout names no repository: override, so it fetches from the same repo the token is scoped to.
  • ref is needs.prepare.outputs.release_sha, a commit of this repository — the same value, fetch-depth and persistence behaviour as before, so only the authenticating identity changed.
  • The workflow already proves the default token can fetch here: the checkout_release_workflow_scripts anchor (:161-180) has no token override and is reused seven times inside publish itself (:693, :703, :725, :733, :744, :756, :764), and prepare's own full-depth checkout (:156-160) uses it too. Those paths are exercised on every release.
  • Triggers are schedule and workflow_dispatch (:3-10), never pull_request, so there is no restricted fork-token context in which the default token would be read-only.

No write path lost its credential

CI_BOT_PAT appears exactly three times after the change, and all three are writes:

  • :713 — Conditionally push release branch (push_release_branch), which also receives GITHUB_TOKEN: github.token;
  • :780 — Create GitHub Release and Tag, with the in-file reason that a GITHUB_TOKEN event cannot trigger downstream release-event workflows;
  • :795 — Trigger ECS runner qwen update (repository_dispatch).

No step's env block was touched, and the removed token was never persisted (persist-credentials: false predates this PR), so nothing downstream could have inherited git credentials from that checkout.

Test

scripts/tests/release-workflow.test.js:3479-3495 pins both halves of the split: the publish checkout has no token and keeps persist-credentials: false, while push_release_branch still gets CI_BOT_PAT and Create GitHub Release and Tag still gets it as GITHUB_TOKEN. The lookups fail loudly rather than passing vacuously — a missing step makes .with / .env throw. The reworded canonicalWipe rationale at :295-301 leaves the pinned body byte-identical, so the equality pin still holds.

CI at this head

Everything reported is green — Test (ubuntu-latest, Node 22.x) (35m37s), Lint & Static, Integration Tests (no-AK), web-shell E2E Smoke, the TUI gates and all four Installed npm global and npx lanes. Nothing red or pending is attributable to this change.

One non-blocking number worth knowing, since it bears on the next PR that touches this file: release.yml is 40032 bytes at this head against a recorded .size-baseline:58 of 35955 — an overage of 4077 bytes, inside the growth allowance of 4096 that check-workflow-size.sh:33 sets and the workflow-size.test.js "is within its baseline allowance" case reads back from it, with 19 bytes of headroom left. The ratchet's stale-baseline leniency would not cover this file, because it only applies when the file is byte-identical to the PR's base and this PR edits it; the allowance is what keeps the gate green here.

@wenshao
wenshao merged commit e305a77 into main Oct 28, 2026
66 checks passed
@qwen-code-review-bot

Copy link
Copy Markdown
Collaborator

Released in v0.26.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release Failed for v0.26.2-preview.1 on 2026-10-27

5 participants