ci(autofix): run the schema gate from a trusted staged copy, not the branch tree - #7076
Conversation
…branch tree Live-run failure on the first post-merge address run (PR #7072): the verify gate invokes `bash .github/scripts/check-settings-schema.sh`, but by then the working tree is the PR BRANCH (switched in "Prepare branch and feedback"), and a branch that predates the script's merge does not contain it — bash exits 127, the gate dies with no outcome, and only the always-handoff kept the loop from going silent. Every bot branch created before the script's merge hits this. Stage the script from the trusted-base checkout into ${RUNNER_TEMP} before any branch switch (both jobs, for symmetry) and invoke the staged copy in both verify gates. This also means the gate logic always comes from the trusted base, never from the branch under verification. Test asserts: staged invocation in both gates (and no working-tree invocation), exactly two staging cp lines, review-address staging ordered before the branch switch, and the structural before-no-op ordering unchanged. 49/49.
|
Thanks for the PR! Template looks good ✓ Problem: Observed bug with clear evidence — CI run 29552946622 shows Direction: Aligned. This fixes a structural flaw introduced by #6998 — the gate resolves from the working tree after branch switch, which is wrong on two counts (missing file on old branches, and branch code defining its own gate). Staging from the trusted base before any switch is the right fix. Size: Not applicable — CI-only change, no core modules touched. Approach: Minimal and focused. Two Moving on to code review. 🔍 中文说明感谢 PR! 模板完整 ✓ 问题:已观测到的 bug,证据明确 — CI 运行 29552946622 显示 方向:对齐。修复 #6998 引入的结构性缺陷 — gate 在分支切换后从工作区解析,有两个问题(旧分支缺少文件、分支代码定义自己的 gate)。从可信 base 在任何切换前暂存是正确的修法。 规模:不适用 — 仅 CI 变更,未触及核心模块。 方案:最小且聚焦。两个 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewThe diff is exactly what the problem demands — no more, no less. Two staging No correctness issues, no security concerns, no AGENTS.md violations. Verified statically:
Testing49/49 pass, including the new assertions:
N/A for tmux real-scenario testing — this is a GitHub Actions workflow change that can only run end-to-end on a live runner. 中文说明代码审查diff 完全对应问题需求 — 不多不少。两个暂存 无正确性问题,无安全隐患,无 AGENTS.md 违规。 静态验证:
测试49/49 全部通过,含新断言:
tmux 真实场景测试不适用 — 这是 GitHub Actions 工作流变更,只能在真实 runner 上端到端运行。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — Clean structural fix for a real CI failure, minimal diff, well-tested. This is a textbook bugfix: observed failure with linked evidence, root cause clearly identified, fix is the smallest possible change that addresses the structural problem (not just the symptom), and the tests cover the new contract precisely. The side benefit of always running the gate from the trusted base rather than branch code is a nice hardening bonus. No concerns. Ships it. ✅ 中文说明置信度: 5/5 — 对真实 CI 故障的干净结构性修复,最小 diff,测试充分。 这是一个教科书式的 bugfix:有链接证据的已观测故障,根因明确定位,修复是解决结构性问题(而非症状)的最小变更,测试精确覆盖了新契约。始终从可信 base 而非分支代码运行 gate 的附带收益是一个很好的加固加分。 无顾虑。合入。✅ — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
— qwen3.7-max via Qwen Code /review
…kout
Review: the lastIndexOf ordering assertion only protected the review-address
job. The issue job's agent commits can touch .github/scripts, so staging after
the verify gate's checkout would copy the agent's gate instead of the trusted
base's. Assert the first staging occurrence (issue job) precedes the first
git checkout "${BRANCH}" (also the issue job's).
wenshao
left a comment
There was a problem hiding this comment.
— Codex GPT-5 via Qwen Code /review
| # trusted checkout's copy of the schema gate, never a working-tree copy. | ||
| - name: 'Stage trusted schema gate' | ||
| run: |- |
There was a problem hiding this comment.
[Critical] This copy remains writable by branch-controlled host commands before it is invoked. In issue-autofix, verification checks out the agent-created branch and runs its npm run build, npm run typecheck, and npm run lint entry points with RUNNER_TEMP inherited before executing this file. A prompt-injected change to any of those entry points can replace the staged script with a successful no-op, so a stale schema passes and the later publish step pushes the unverified branch. Run branch-controlled commands inside a boundary that cannot mutate the gate, then materialize the trusted script from the pinned base immediately before invocation and verify its integrity instead of exposing a long-lived same-user-writable copy.
— Codex GPT-5 via Qwen Code /review
| # define its own gate. | ||
| - name: 'Stage trusted schema gate' | ||
| run: |- |
There was a problem hiding this comment.
[Critical] Staging before checkout does not keep this destination trusted: after switching to the PR branch, Triage and address directly executes that branch's .qwen/skills/autofix/scripts/run-agent.mjs on the host with RUNNER_TEMP inherited. A bot-owned branch can replace this script with a no-op and create no-action.md; verification then executes the replacement and records outcome=noop, bypassing the freshness gate. Invoke a runner staged from the trusted base, isolate all PR-controlled execution from the gate path, and materialize or integrity-check the trusted gate immediately before use.
— Codex GPT-5 via Qwen Code /review
doudouOUC
left a comment
There was a problem hiding this comment.
Approve ✅
Reviewed the final state (through 4c0e996) against the checked-out PR branch, not just the diff.
Root cause & fix — correct. The verify gates invoked bash .github/scripts/check-settings-schema.sh from the working tree. Once the tree is switched to the PR branch (review-address) or after the agent commits (issue-autofix), that path can be absent — a bot branch predating #6998 does not contain the script, so bash exits 127 and the gate dies before writing an outcome. Staging the script from the trusted-base checkout into ${RUNNER_TEMP} before any branch switch, and running the staged copy in both gates, is the right structural fix rather than a per-branch workaround.
Verified locally:
- Both jobs check out trusted code before staging: review-address pins
ref: default_branchwithpersist-credentials: false; issue-autofix stages right after its initial checkout, beforeDevelop fix. So the staged copy is genuinely the trusted-base version. - No working-tree invocation remains (
bash .github/scripts/...count = 0); exactly 2cplines and 2bash "${RUNNER_TEMP}/..."invocations. - Workflow parses as valid YAML; all new test assertions hold when run against the branch.
Bonus: having the gate's decision logic always come from the trusted base (not the branch under verification) is a genuine security improvement, since the review-address job carries the bot PAT — a branch could otherwise define its own PASS/FAIL gate.
One non-blocking suggestion (optional, test-tightness only): in the issue-autofix job the ordering assertion anchors on git checkout "${BRANCH}", which sits after the Develop fix step where the agent can touch .github/scripts. The stated intent ("a post-checkout copy would stage the agent's gate") would be enforced more tightly by anchoring on - name: 'Develop fix' (or 'Check bot credentials'). The workflow itself is correct — staging is the first step — so this is purely about the guard matching its rationale, not a defect.
LGTM.
|
@qwen-code /triage |
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with clear evidence — run 29552946622 shows the verify gate dying with Direction: aligned. CI self-infrastructure fixing its own breakage — the gate resolving from the working tree was a latent structural flaw exposed by branch-age skew. Staging from the trusted base checkout is the right fix. Size: not applicable (CI workflow + contract test, no core paths). Approach: minimal and focused — two Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测的 bug,有明确证据 —— 运行 29552946622 显示 verify gate 因 方向:对齐。CI 自身基础设施的修复 —— gate 从工作区解析是一个潜在的结构性缺陷,被分支年龄偏斜暴露。从可信 base 检出暂存是正确的修复方式。 规模:不适用(CI 工作流 + 契约测试,未触及核心路径)。 方案:最小且聚焦 —— 两处 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
|
Code review: The diff is clean and well-reasoned. Four things stood out:
No correctness bugs, no security holes, no scope creep. The comments are justified (the why is non-obvious for future maintainers) without being excessive. Testing: This is a GitHub Actions workflow change — not user-facing CLI behavior, so tmux testing doesn't apply. The verification evidence: Contract test resultsYAML validationStep ordering verification (from workflow file)No remaining working-tree invocations of 中文说明代码审查: diff 干净且考虑周全。四点值得指出:
无正确性 bug、无安全漏洞、无范围蔓延。注释合理(对未来维护者来说 why 并不明显),没有过度注释。 测试: 这是 GitHub Actions 工作流变更——不涉及用户可见的 CLI 行为,因此 tmux 测试不适用。验证证据:契约测试 49/49 通过,YAML 解析正确,步骤顺序验证确认暂存在两个 job 中均先于分支切换。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — clean across every stage; ship it. This is exactly what a CI self-fix should look like. The bug was caught live on the first post-merge run, the root cause is well-understood (branch-age skew means the working tree doesn't contain the gate script), and the fix is structural — stop resolving the gate from the working tree entirely, stage it from the trusted base before any branch switch. My independent proposal would have been identical. The implementation is minimal (two No concerns. Approving. 中文说明置信度:5/5 — 每个阶段都干净,可以合并。 这正是 CI 自修复应有的样子。bug 在合并后的首次运行中实战抓到,根因清楚(分支年龄偏斜导致工作区不包含 gate 脚本),修复是结构性的——彻底停止从工作区解析 gate,在任何分支切换前从可信 base 暂存。我的独立方案也会完全一样。 实现最小化(两处 无顾虑。批准。 — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
What this PR does
Fixes a branch-age skew bug in the autofix verify gates introduced by #6998: the gates invoke the shared schema-freshness script via its repo path (
bash .github/scripts/check-settings-schema.sh), but by the time the review-phase gate runs, the working tree has been switched to the PR branch — and any bot branch created before that script was merged does not contain it, so bash exits 127 and the gate dies without writing an outcome. This PR stages the script from the trusted-base checkout into${RUNNER_TEMP}before any branch switch (in both the issue and review jobs) and makes both verify gates invoke the staged copy. As a side benefit, the gate logic now always comes from the trusted base rather than from whatever the branch under verification contains.Why it's needed
Caught live on the first post-merge address run. The enhanced loop targeted PR #7072 (branch created at 02:53, before #6998 merged at 03:27), the agent produced a legitimate no-action evaluation, and then the verify gate crashed with
bash: .github/scripts/check-settings-schema.sh: No such file or directory(exit 127) — see the failing run. The always-post-handoff behavior from #6998 kept the loop from going silent (it posted a round-1 handoff with an eval marker), but the gate itself is broken for every bot branch that predates the script, which at the time of writing was all of them. Syncing each branch with main works around it per-PR, but the structural fix is to stop resolving the gate from the working tree entirely.Reviewer Test Plan
How to verify
review-address (7072, …)job's Verification gate step logsbash: .github/scripts/check-settings-schema.sh: No such file or directoryfollowed byProcess completed with exit code 127, and the step summary showsoutcome=unknown— the gate died before writing an outcome.bash "${RUNNER_TEMP}/check-settings-schema.sh", and both jobs contain aStage trusted schema gatestep that copies the script out of the trusted-base checkout; in the review job that step is ordered beforePrepare branch and feedback(the branch switch). Because the copy is taken before any switch, the gate works regardless of the PR branch's age and always runs the trusted version.npx vitest run scripts/tests/qwen-autofix-workflow.test.js— 49/49, including new assertions: staged invocation in both gates and no working-tree invocation remaining, exactly two stagingcplines, and the staging step ordered before the branch switch.run:block passesbash -n.Evidence (Before & After)
Tested on
Environment (optional)
Workflow-only change; runs on
ubuntu-latestGitHub-hosted runners. Verified locally under bash: contract test 49/49, YAML parse,bash -non every run block.Risk & Scope
${RUNNER_TEMP}, which is job-scoped and wiped between runs.Linked Issues
Follow-up to #6998 (caught during its live validation on #7072).
中文说明
本 PR 做了什么
修复 #6998 引入的 verify gate「分支年龄偏斜」bug:两个 gate 通过仓库路径调用共享的 schema 新鲜度脚本(
bash .github/scripts/check-settings-schema.sh),但 review 阶段的 gate 执行时工作区已切换到 PR 分支 —— 任何早于该脚本合入的 bot 分支都不包含它,于是 bash 以 127 退出,gate 在写入 outcome 之前就死掉。本 PR 在任何分支切换之前,把脚本从可信 base 检出暂存到${RUNNER_TEMP}(issue 和 review 两个 job 都做),两个 verify gate 改为调用暂存副本。附带收益:gate 逻辑始终来自可信 base,而不是被验证分支里的任意内容。为什么需要
在合并后的第一次 address 运行中实战抓到。增强回路盯上了 PR #7072(分支创建于 02:53,早于 #6998 的 03:27 合并),agent 给出了合理的 no-action 评估,随后 verify gate 崩溃:
bash: .github/scripts/check-settings-schema.sh: No such file or directory(exit 127)—— 见失败的运行。#6998 的「失败必交接」让回路没有静默(发了 round-1 交接和 eval 标记),但 gate 本身对所有早于该脚本的 bot 分支全部失效 —— 当时是全部四个。逐 PR 与 main 同步只是绕过,结构性修复是让 gate 彻底不从工作区解析。评审验证方案
如何验证
review-address (7072, …)job 的 Verification gate step 日志出现bash: .github/scripts/check-settings-schema.sh: No such file or directory和Process completed with exit code 127,step summary 显示outcome=unknown—— gate 在写 outcome 前死亡。bash "${RUNNER_TEMP}/check-settings-schema.sh";两个 job 都有Stage trusted schema gate步骤从可信 base 检出复制脚本;review job 中该步骤排在Prepare branch and feedback(分支切换)之前。由于副本在任何切换前取得,gate 对任意年龄的 PR 分支都有效,且始终运行可信版本。npx vitest run scripts/tests/qwen-autofix-workflow.test.js—— 49/49,含新断言:两个 gate 均调用暂存副本且不再有工作区路径调用、恰好两处暂存cp、暂存步骤先于分支切换。run:块通过bash -n。证据(前后对比)
测试情况
环境(可选)
仅工作流改动;运行于
ubuntu-latestGitHub 托管 runner。本地 bash 下验证:契约测试 49/49、YAML 解析、每个 run 块bash -n。风险与范围
${RUNNER_TEMP},job 级隔离、运行间清理。关联 Issue
#6998 的后续(在其对 #7072 的实战验证中发现)。