Skip to content

fix(gate): pre-push 假绿(AAV-1330) + sync 工作流 auto-merge 的 GraphQL 字段错 - #771

Merged
0xPabloLI merged 6 commits into
devfrom
lovable
Oct 10, 2026
Merged

0xPabloLI merged 6 commits into
devfrom
lovable

Conversation

@0xPabloLI

Copy link
Copy Markdown
Owner

lovable → dev,两笔都是门禁完整性修复,不含任何运行时行为改动。

1. fix(sync): auto-merge 的 GraphQL 选择集字段名错误(803defdd)

今天 #768 上线后,我上一轮改的 ancestry 判定确实按设计工作了:run #334 自动开出 sync/main-into-dev-badd79c8(=#769,files=0 的纯 ancestry 回灌)。但它没能自动合,日志里是:

挂 auto-merge 失败:Field 'enabledUntil' doesn't exist on type 'AutoMergeRequest'

enabledUntil 是 enablePullRequestAutoMerge 的入参名,不是返回类型上的字段;GraphQL 先做整份文档校验,未知字段会让整个 mutation 被拒绝,于是 PR 开出来了、auto-merge 一步没挂、而这一步只 core.warning 就成功退出——典型的"看起来在跑、其实没跑"。

字段真值取自 introspection(不是记忆):authorEmail / commitBody / commitHeadline / enabledAt / enabledBy / mergeMethod / pullRequest。已改成 enabledAt mergeMethod。

顺带记下第二个坑:GitHub 对已经全绿的 PR 挂 auto-merge 会报 UNPROCESSABLE: Pull request is in clean status(#769 就是这样,我只能手工合)。那不是失败,是"直接合并即可",catch 里已按这条降级成 info,免得下个 session 又去追它。

守卫:scripts/lib/workflow-graphql-fields.mjs + 5 条测试。R1 先证明它不是读空(真文件里确实有 autoMergeRequest 选择集),R2 逐字段合法,R3 正对照(合成文本塞 enabledUntil ⇒ 报出该字段),R4 嵌套选择集不被压平(enabledBy { login } 不产生假阳性),R5 合法清单本身不缺字段。负对照实测:把真文件改回 enabledUntil ⇒ 该用例变红;还原后按 sha256 校验逐字节一致。

2. fix(husky): pre-push 的 ci:remote 失败会被吞成绿灯(52e5ace1,AAV-1330)

husky 的 .husky/_/h 是用 sh -e "$s" 跑 hook 的,errexit 是开的——但 errexit 有条明文例外:&& 列表里非末尾命令的失败不触发退出。而本仓第一道门正是这个形状:

npm run ci:remote && npm run test:e2e:pre-push

⇒ ci:remote 红时 e2e 被跳过、errexit 沉默、hook 的退出码改由后面的 knip/dup 决定。也就是 lint / typecheck / 单测 / build / audit 全红也能推出去。实测:sh -e -c 'false && echo hi' + 尾部管道 ⇒ 退出码 0;而末位命令失败 ⇒ 1(这与今天两次 PUSH_RC=1 被 e2e 挡下一致)。

修法就是给那一行加显式 || { ...; exit 1; }(保持单行——测试是按行抽取真实 hook 内容去执行的)。

对票面两处判据的订正(都在 .husky/_/h 的语义下才成立):

  1. 被吞的只有 ci:remote。test:e2e:pre-push 是 && 链末位,它失败会触发 errexit ⇒ e2e 红是能挡住的;票面"lint/单测/build/e2e 四段全部静默放行"多算了一段。
  2. 票里的三次 rc=0 证据是 sh .husky/pre-push 直接跑(完全没 errexit)得到的;真实推送走 shim(带 -e)。两条路径结论相同,但复现方式不同,值得在票上写清楚,否则下一个人会以为直跑 hook 等价于真实推送。

验收对照:失败段退出码非零(R3)✓;全绿时返回 0、push 行为不变(R5)✓;--no-verify 语义未动 ✓;未新增也未放宽任何门禁 ✓;rc 可观测性由 shim 自身的 husky - pre-push script failed (code N) 提供,不必在 echo 里再回显 $?。

3. 顺带更正我本 session 自己写错的一条结论

板上记过"高负载下 vitest 5 报 Errors N errors 却 exit 0 ⇒ 本地门可能假绿"。这条不成立,今天用隔离探针(临时 root,不碰本仓配置)测得:

Test Files 1 passed / Tests 1 passed / Errors 1 error   ⇒   rc=1

vitest 5.0.3 会把 unhandled error 计入退出码。假绿的洞在 hook,不在 runner;另一个可疑来源是我自己的读数方式——${PIPESTATUS[0]} 是 bash 语法,本环境是 zsh(要用 $pipestatus[1]),我当时读到的 0 其实是管道尾端 tail 的退出码。

本地门禁

pre-commit 全过(typecheck / lint / check:agents-md / check:todos / check:e2e-market-names / dep-age / npm test 4098 单测 + scripts 测试 193/193);pre-push 全门 PUSH_RC=0(e2e 63 passed / 1 flaky / 7.9m,osv No issues found,semgrep / knip / dup 全跑),并且这一轮的推送本身就是修好后的那行守卫的第一次真实执行。那条 flaky 是 reserves-table-market-filter-pin.spec.ts:161 (4) at anchor → pins to top anchor,属该 spec 已登记的负载性 flake 家族(排除表注释里记着同族用例),与本 PR 两笔无关。

随后那笔 roadmap 提交再走一次同一个门:64 passed / 0 flaky / 6.9m、osv No issues found ⇒ 守卫加上后没有把绿的路径变严。

nested-scroll:38 一度被记为「隔离复跑也红、疑似与 AAV-1329 同族的缺陷」
并列为开票候选。实测推翻:

- 同一 spec、--retries=0:带 E2E_PROXY=http://127.0.0.1:7897 → 2 passed / 16.5s
- 不带代理 → 2 passed / 31.1s(慢一倍,但不红)
- 同时刻出口:代理 ttfb 0.47s;直连 20s 超时、http_code=000、size=0

⇒ 该红属本机直连出口,不属套件,撤回开票。

顺带修正上一 session「本沙箱 e2e 门跑不出可信结果」的口径:门是可信的
(带代理 64 passed / 0 failed / 3.9m),坏的是直连出口。

固化成文档:e2e-testing-boundary.md 新增 Egress 一节,给出两条 curl 的完成
判据与一张「直连 vs 代理」分类表,避免后续 session 把出口问题当缺陷开成票。
板上那句「未合并,等用户」已不成立:#767 用 MERGE 进 dev(5829fe54),lovable 成为 dev 祖先(behind_by=0)。

两次被门挡下的判据都指向本机出口而非代码:直连 50 passed/3 failed/36.7m(三条红全是 tbody tr[data-reserve-id] 30s 等不到;直连取 412,891 字节的 /markets 要 37.9s,第二次 90s 只传完 120,920),带代理同一棵树 64 passed/0 failed/3.9m。没加排除项、没用 --no-verify。

dev→main 建成 #768 按规则不合并;main 领先的一笔 bot 同步用 update-branch 并回 dev,前后 tree SHA 同为 ee6668b 证明零内容变更。本地两条已交付分支删除。
enablePullRequestAutoMerge 选了 autoMergeRequest { enabledUntil } —— enabledUntil 是入参名,不是该类型上的字段;GraphQL 先做整份文档校验,未知字段会让整个 mutation 被拒绝,于是同步 PR 照样开出、auto-merge 一步没挂、而工作流只发了个 warning 就成功退出。#769 就是这么留下的手工一刀。

字段真值来自 introspection(__type(name: "AutoMergeRequest")),不是记忆:enabledAt / enabledBy / mergeMethod / pullRequest / authorEmail / commitBody / commitHeadline。

第二个缺陷也记进注释:GitHub 对已全绿的 PR 挂 auto-merge 会报 UNPROCESSABLE "in clean status",那不是失败而是「直接合并即可」,catch 里按这条单独降级成 info。

scripts/lib/workflow-graphql-fields.mjs + 5 条测试:R1 先证明守卫不是读空(真文件里确实有 autoMergeRequest 选择集),R2 逐字段合法,R3 正对照(把 enabledUntil 塞回合成文本 ⇒ 报出该字段;把真文件改回 bug ⇒ 实测 fail 1),R4 嵌套选择集不被压平(enabledBy { login } 不产生假阳性),R5 合法清单本身不缺字段。
husky 以 sh -e 跑 hook,但 errexit 有条明文例外:&& 列表里非末尾命令的失败不触发退出。本仓第一道门正好是这个形状 —— ci:remote 红时 test:e2e:pre-push 被跳过、errexit 沉默、hook 的退出码改由后面的 knip/dup 决定 ⇒ lint/typecheck/单测/build/audit 全红也能推出去。

修法=给那一行加显式 || { ...; exit 1; }(保持单行,测试是按行抽取执行的)。

scripts/hook-failure-propagation.test.mjs 用 stubbed npm 跑 hook 里抽出的真实那行:R0 先证明 stub 确实让 ci:remote 失败(否则整套断言都是空过)、R2 静态禁止未守护的 && 门、R3 行为断言 hook 退出码非 0、R4 正对照保留旧形状证明它仍返回 0、R5 全绿时不打扰。负对照实测:拆掉守卫 ⇒ pass 4 / fail 2。

顺带纠正本 session 的一条误判:那条「vitest 报 Errors 却 exit 0」不是 vitest —— 隔离探针里 unhandled rejection 让 vitest 5.0.3 明确 exit 1(Test Files 1 passed / Tests 1 passed / Errors 1 error ⇒ rc=1)。假绿的洞在 hook,不在 runner;另一处可疑是我自己的读数方式(${PIPESTATUS[0]} 是 bash 语法,本环境是 zsh,读数会取到 tail 的 0)。
状态格只改状态、不复述机制:真因(errexit 对 && 非末位命令的例外,而非「后四道门覆盖 0」)的正本在 Linear 评论与 .husky/pre-push 注释里。e2e 作为 && 链末位命令本来就能挡住推送,票面「四段全部静默放行」多算了一段。
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@linear-code

linear-code Bot commented Oct 10, 2026

Copy link
Copy Markdown

AAV-1330

@vercel

vercel Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
aave-protocol-explorer Ready Ready Preview Oct 10, 2026 11:30am UTC

@factory-droid

factory-droid Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Droid finished @0xPabloLI's task —— View job


LGTM — no issues found. Both fixes are well-reasoned and correctly implemented: the pre-push hook's A && B || { exit 1; } guard restores proper failure propagation under sh -e errexit, and the sync workflow's GraphQL selection set now uses only fields on the AutoMergeRequest type, with the "clean status" UNPROCESSABLE error correctly downgraded to an info and early-returned from the final block of the step. The new guard tests extract the real hook line and pin both the failure and the historical swallow behavior, and the security review found no exploitable path in the diff.

0 inline comments posted

@0xPabloLI
0xPabloLI merged commit 2a32386 into dev Oct 10, 2026
45 checks passed
0xPabloLI added a commit that referenced this pull request Oct 10, 2026
板上「vitest 报 Errors 却 exit 0 ⇒ 本地门假绿」这条是我读退出码读错了(bash 的 PIPESTATUS 在 zsh 下取到管道尾端的 0)。隔离复现证明 vitest 5.0.3 会因 unhandled error 返回 rc=1;真正的假绿洞在 .husky/pre-push 的 ci:remote && e2e 那一行,已由 52e5ace 修掉并进 dev(#771)。订正同时记下两条方法教训:取退出码要先落文件再取 0;指认某个工具假绿之前先做隔离复现。

This branch was successfully deployed

1 active deployment
Preview — f3bfc11a Deployed Oct 10, 2026 by vercel[bot]
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.

1 participant