Repository navigation
build(deps-dev): bump vitest from 4.1.11 to 5.0.3 - #731
dependabot[bot] wants to merge 4 commits into
Conversation
4bae45a to
775adf6
Compare
775adf6 to
c541577
Compare
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.11 to 5.0.3. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 5.0.3 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
c541577 to
c4d8363
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
由 #748 取代。 本 PR 合不进去的根因不是 rebase 问题:Dependabot 只升了
|
Pull request was closed
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
* docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * build(deps): vitest 4→5 连同 @vitest/coverage-v8 一起升(取代 #731) #731 只升 vitest、没升 @vitest/coverage-v8,而 vitest 5 的 peer 要求 coverage-v8 同版本 ⇒ 严格 npm ci 失败,peer-dep-check 是 dev 的必填项,故该 PR 合不进去。 验证:3793 单测 + 137 scripts 测试全绿;npm ci 严格安装通过;typecheck/lint 0 error; coverage 门 rc=0(All files 73.17/66.79/64.36/75.13 vs v4 基线 73.23/66.82/64.36/75.13, 阈值 72/66/63/74 均在上方)。 * build(deps): overrides 钉 postcss-selector-parser 7.1.6 以过 pre-push 的 osv 门 6.1.4 命中 GHSA-rj75-hqrm-r3gf(5.9,dev-only);6.x 无修复版(最高 6.1.4),修复只在 7.1.6, 而 postcss-nested / tailwindcss 均声明 ^6.1.x ⇒ 正常升级路径不存在,只能像 fast-uri/js-yaml/ws 那样用 overrides 钉。 跨大版本强钉的验证:npm run build(Tailwind 实际编译)rc=0,且产出的 CSS 逐字节一致 (index-DyVJKc4h.css / vendor-blockchain-DR00Q9RG.css 内容哈希同名,拼接 md5 相同), osv-scanner rc=0。备选是 osv-scanner.toml 的 ignoreUntil 条目(AAV-1328 对 braces 的做法)。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com>
… renovate vitest 族分组 (#753) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(hardcode): sync hardcoded assets and mappings (#744) * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * chore(session-board): qoder-1006a 注销(#748/#747 交付 + worktree 缺 .husky/_ 导致 pre-push 整条门静默不跑 的机制发现) * build(deps): overrides 钉 postcss-selector-parser 7.1.6 以过 pre-push 的 osv 门 6.1.4 命中 GHSA-rj75-hqrm-r3gf(5.9,dev-only);6.x 无修复版(最高 6.1.4),修复只在 7.1.6, 而 postcss-nested / tailwindcss 均声明 ^6.1.x ⇒ 正常升级路径不存在,只能像 fast-uri/js-yaml/ws 那样用 overrides 钉。 跨大版本强钉的验证:npm run build(Tailwind 实际编译)rc=0,且产出的 CSS 逐字节一致 (index-DyVJKc4h.css / vendor-blockchain-DR00Q9RG.css 内容哈希同名,拼接 md5 相同), osv-scanner rc=0。备选是 osv-scanner.toml 的 ignoreUntil 条目(AAV-1328 对 braces 的做法)。 * chore(harness): 把 pre-commit/pre-push 钉成绝对 hooksPath,worktree 不再静默跳门 husky 写的 core.hooksPath 是相对值 .husky/_,而该目录只在 npm install 的 prepare 里生成(且自我忽略:.husky/_/.gitignore 内容是 *)。新 worktree 没这个目录 ⇒ git 找不到任何 hook ⇒ pre-commit/pre-push 整条不执行,而 commit/push 仍退出 0。 本仓有四道门只存在于 pre-push(osv-scanner / semgrep / knip / dup:check),CI 里 没有对应 job,所以这条路会产出「PR 全绿但门从没跑过」。#747 就是这样推上去的。 prepare 改为 `npx husky && node scripts/husky-hooks-path.mjs`:把共享 config 里的 hooksPath 指向主 checkout 的绝对路径,任何 worktree 都解析得到;配套在两个 hook 开头加 node_modules 守卫,缺依赖时明确报错。opt-out: HUSKY_HOOKS_PATH_ABSOLUTE=0; 自查: npm run check:hooks-path。附 17 条 node:test 覆盖判定表(不覆盖非本仓所有的 hooksPath、幂等、只认字面量 0 才关闭)。 另在 renovate.json 给 vitest 族加分组规则:vitest 5 的 peer 要求 @vitest/* 同版本, 只升一半会让严格 npm ci 在 peer-dep-check(dev/main 均必填)上失败——#731 即此病, 换成 Renovate 也会重犯,除非同批评审(照 node runtime 那条的先例)。 * fix(e2e): E2E_PROXY 也作用于 prewarm 浏览器,直连变慢时不再在第一条测试前整条中断 playwright.config 只把 proxy 放进 project 的 use,覆盖的是"测试用的浏览器"; e2e/global-setup.ts 自己 chromium.launch() 起的那个始终走直连。直连吞吐一差, warm-up 就卡在 portfolio-mode-toggle 的 120s 预算上,整轮在任何一条测试开始之前 中止——于是文档里的 E2E_PROXY 看起来完全失效。 实测判据(2026-10-07):/markets 三次直连 ttfb 1.4–5.0s 但 total 82s/100s/150s+ 都还没传完(HTTP 都 200),而本机 mixed 口 7891 用 4.2s。所以旧注释里那句 "API 已在 ~1s 返回 200"正是被证伪的前提:200 不说明传输时间。 改法是把代理判定抽成 e2e/browserProxy.ts 的单一来源,两个消费者都从这里取, 避免再次各写各的;src/test/browserProxy.test.ts 的 R8 是 wiring guard,直接断言 两处都调用 browserProxyArgs 且不再手写 process.env.E2E_PROXY。 验证:8 条用例绿、typecheck rc=0、e2e 显式 tsc rc=0(e2e 不在 lint/typecheck 覆盖内)、 E2E_PROXY 下 test:e2e:pre-push 63 passed / 1 flaky(既存:标题由实盘数据生成, retry 后标题变化报 Test not found)/ rc=0;改前同一命令在 warm-up 处 TimeoutError 中止。 * fix(harness): pre-commit 先生成 icon/token manifest,再跑 typecheck 三个 *.generated 模块是 gitignore 的生成物:npm test 的第一步会生成它们,但 pre-commit 把 typecheck 排在 npm test **之前** ⇒ 在一个刚 npm install、还没跑过测试的 linked worktree 里, 任何提交都死在 TS2307 Cannot find module './chainIconManifest.generated',读起来完全像代码回归。 对照实验(在 /tmp 新 worktree 里做的):删掉生成物 → typecheck 报 3 条 TS2307; 跑 node scripts/generate-icon-manifests.mjs → 0 条。命令与 npm test 用的是同一个, 生成物本身仍被忽略,不会污染提交。 注:这条改动要等它进了主 checkout 的工作树才会真正被执行——绝对 hooksPath 下 git 跑的是 主 checkout 的 .husky/* 内容(PR 正文里已记这条限制)。 * build(deps): handlebars 4.7.9→4.7.10 以过 pre-push 的 osv 门 2026-10-08 新公布的通告把 openapi-zod-client 的传递依赖 handlebars 4.7.9 标成 2 条 Critical(GHSA-8r5x-fm3f-whwj 9.8 / GHSA-p8wg-vrv2-v86f 9.2)+ 1 条 Medium, 修复只有 4.7.10。这个版本在 lovable 与 dev 上早就存在,是今天才被判 vulnerable ⇒ 所有人从 lovable/dev 派生的分支都推不动(osv 只在 pre-push,CI 无对应 job)。 走正常解析路径 `npm update handlebars`(父节点声明的范围允许),不新增 override; lock 只动这一项。7 天等待期门按 direct 判定,本仓 check:dep-age 通过(0 direct、 3 transitive),所以这里没有绕策略——是 osv 与等待期此刻的方向相反,选安全侧。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com>
…ior.md 改写为现状 (#747) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * ci(deps): 删 dependabot peer-conflicts 报告 workflow,dependabot-behavior.md 改写为现状 dependabot.yml 随 #736 离开默认分支后 Dependabot 不再产版本 PR,该 workflow 失去输入; peer 冲突判定已由 ci.yml 的 peer-dep-check(严格 npm ci,dev/main 均必填)覆盖。 保留 dependabot-auto-triage.yml:security updates 仍启用,它是真出现 security PR 时唯一的分类器。 dependabot-behavior.md 原文整份指向已不存在的 .github/dependabot.yml,改写为现状 + `dependabot/*`→main 被 branch-flow-guard 挡死的成因与两条真解(本次实测 #746 用改 base 解决)。 * docs(dependabot): 订正「删配置=停管道」——#752 实证 Dependabot 仍会开版本 PR #736 把 dependabot.yml 带离 main 约 24 小时后,#752 于 2026-10-07T11:16Z 创建, 标题与 commit trailer(dependency-type: indirect + dependency-group)都是版本升级 PR 的 形状,且复用 #746 的分支与 head commit。本文件上一版写的「不再开版本升级 PR」是错的推论。 机制不下断言(配置缓存生效延迟、或对未满足的更新继续提案都有可能),改成给判据: diff vs dev 为空 ⇒ 关闭(内容已随 #746 进 dev,等 dev→main 上线即满足);非空 ⇒ base 改 dev 走发布流;不要加 ignore 规则去压制告警面与后续合法提案。 保留 auto-triage 的理由也换成实证:#752 带着 manual-review 且 triage 检查 pass。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com>
) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(hardcode): sync hardcoded assets and mappings (#744) * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * chore(session-board): qoder-1006a 注销(#748/#747 交付 + worktree 缺 .husky/_ 导致 pre-push 整条门静默不跑 的机制发现) * build(deps): overrides 钉 postcss-selector-parser 7.1.6 以过 pre-push 的 osv 门 6.1.4 命中 GHSA-rj75-hqrm-r3gf(5.9,dev-only);6.x 无修复版(最高 6.1.4),修复只在 7.1.6, 而 postcss-nested / tailwindcss 均声明 ^6.1.x ⇒ 正常升级路径不存在,只能像 fast-uri/js-yaml/ws 那样用 overrides 钉。 跨大版本强钉的验证:npm run build(Tailwind 实际编译)rc=0,且产出的 CSS 逐字节一致 (index-DyVJKc4h.css / vendor-blockchain-DR00Q9RG.css 内容哈希同名,拼接 md5 相同), osv-scanner rc=0。备选是 osv-scanner.toml 的 ignoreUntil 条目(AAV-1328 对 braces 的做法)。 * chore(hardcode): sync hardcoded assets and mappings (#751) * chore(hardcode): sync hardcoded assets and mappings (#754) * fix(ci): eslint 上限补上 eslint 本体,并加 renovate.json 漂移守卫 AAV-1301 的裁定是"eslint 工具链等插件跟上之前不要 10",但落到 renovate.json 时 matchPackageNames 只写了 @eslint/js ⇒ eslint 本体仍可被升。Dependency Dashboard #715 现在排着 `update dependency eslint to v10` 就是这条漏口的直接后果。 实测 peer:eslint-plugin-import 的 peer 是 ^2..^9(没有 ^10)⇒ 升 eslint 会让严格 npm ci 失败,而 peer-dep-check 是 dev 与 main 都必填的检查;另两个 (eslint-plugin-react-hooks@7、typescript-eslint@8)已经接受 ^10,所以拦点就在 eslint-plugin-import。 同时新增 scripts/renovate-config.test.mjs:8 条只读配置的断言,逐条对应"这个字段 曾经被编辑掉过一次"的裁定(manager-scoped 的 automerge、runtime 永不 automerge、 lockFileMaintenance 关、vitest 族分组、node 两处真值分组、timestamp-optional)。 两组正对照实测会变红:上限退回只钉 @eslint/js;抽掉 npm automerge 规则的 matchManagers。 注意生效路径:Renovate 只读默认分支的配置,所以这笔要走到 main 才被读到,挡不住 下个周一窗口的 eslint v10(那笔只会红在 peer-dep-check,人工关闭即可)。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com>
* docs(agents): 记录 Droid 自动 PR 审查约定 * docs: add droid-review.md * docs: add setup-github-actions-secret.md * fix(schema): 同步后端 OpenAPI(移除 Merit)并让 merit 契约脱离 generated 后端已下线 Merit:/markets 不再返回 merit 字段,OpenAPI 里 ApiMeritCampaign* schema 也删了。前端 openapi-sync 因此长期红(codegen 出的 schemas.ts 少了 generated.ApiMeritCampaignBreakdown / Group,而手写契约与 generated types 还在引用)。 - public/openapi.json + src/generated/api/schemas.ts:取自 staging 的权威 spec - src/shared/market-contract/schemas.ts:Merit 两个 schema 改手写 base,字段逐条 对齐移除前的 generated 形状,三处既有放宽(campaignType 放宽、aprCap 可 null、 group link 可选 + 递归 message + wrapper breakdowns)原样保留 - src/generated/api/types.ts:删掉两条指向已消失 schema 的别名(全仓无其他引用) 契约保留 merit 定义是有意的:灰度/回滚窗口内响应仍可能带回 merit 字段,删定义会让 它们被 zod strip 掉而静默丢数据。运行时语义零变化。 * test(schema): 给 merit 契约补第一份 parse 覆盖(13 例矩阵) 排查 AAV-1303 时发现:MeritCampaignGroupSchema / BreakdownSchema 全仓零运行时解析 测试,只有 apiSchemas.ts 引用它。契约这次从 generated 改成手写 base,没有测试就 等于「抄错字段只会在运行时静默丢数据」。 矩阵按旧契约的宽松度写:四个必填、campaignType 任意字符串、aprCap 可 null 但不可 字符串、positionCapUsd 可选但不可 null、link 可缺、递归 message、嵌套 breakdowns 走 wrapper、netPositionConstraint / crossAssetPairing 的 enum 与类型校验、未知键 strip、 空数组。其中 message 的「标量只能当 record 叶子、不能当数组元素」是既有不对称, 一并钉住(差分探针验证过新旧同拒绝,非本次引入)。 * fix(deps): override axios 到 ^1.20.0(清掉 osv-scanner 门) axios 1.18.1 被 12 条 OSV 命中(7 High),修复版 1.20.0。它是 openapi-zod-client → @zodios/core 的传递 dev 依赖,不是直接依赖,所以按仓里既有形状用 npm overrides 抬。 影响面不止本分支:任何工作树从当前 lockfile 推 lovable 都会被 .husky/pre-push 的 osv-scanner 挡下,这一条同时解锁其他人。 验证:osv-scanner --lockfile package-lock.json → No issues found (exit 0); npm run schema:check 仍 0(codegen 工具在 1.20.0 下产出逐字不变);npm test 170 files 全过。 * build(deps-dev): bump @testing-library/user-event from 14.6.1 to 14.6.7 (#705) Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 14.6.1 to 14.6.7. - [Release notes](https://github.com/testing-library/user-event/releases) - [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md) - [Commits](testing-library/user-event@v14.6.1...v14.6.7) --- updated-dependencies: - dependency-name: "@testing-library/user-event" dependency-version: 14.6.7 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: 修正安全审查阻断表述(Droid P3 发现) * chore(hardcode): sync hardcoded assets and mappings (#714) * ci: remove Dependabot config (superseded by Renovate) (#716) * chore(hardcode): sync hardcoded assets and mappings (#712) * ci: remove Dependabot config (superseded by Renovate) * build(deps): bump i18next from 26.4.0 to 26.4.2 (#703) Bumps [i18next](https://github.com/i18next/i18next) from 26.4.0 to 26.4.2. - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](i18next/i18next@v26.4.0...v26.4.2) --- updated-dependencies: - dependency-name: i18next dependency-version: 26.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pablo <77742839+0xPabloLI@users.noreply.github.com> * ci(renovate): enable npm manager with 7-day release cool-down (#717) * chore(deps): update actions/github-script digest to f28e40c (#719) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update dependabot/fetch-metadata digest to 21025c7 (#720) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update zaproxy/action-baseline digest to 66042c8 (#722) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update zaproxy/action-baseline action to v0.15.0 (#723) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update reviewdog/action-eslint digest to 3ee2a45 (#721) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(hardcode): sync hardcoded assets and mappings (#737) * build(deps-dev): bump typescript-eslint from 8.67.0 to 8.71.0 (#730) Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.67.0 to 8.71.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-version: 8.71.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump jscpd from 5.2.0 to 5.4.0 (#732) Bumps [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd) from 5.2.0 to 5.4.0. - [Release notes](https://github.com/kucherenko/jscpd/releases) - [Commits](https://github.com/kucherenko/jscpd/commits/v5.4.0/rust/jscpd) --- updated-dependencies: - dependency-name: jscpd dependency-version: 5.4.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci(renovate): 以 dev 的接管方向为基底合并配置,并收紧 npm 自动放行面 (#735) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com> * chore(deps): update actions/checkout action to v7 (#724) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update dependabot/fetch-metadata action to v3 (#726) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update actions/github-script action to v9 (#725) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update dependency node to v24 (#727) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): bump @aave/react from 6.5.0 to 6.6.0 (#728) Bumps [@aave/react](https://github.com/aave/aave-v4-sdk/tree/HEAD/packages/react) from 6.5.0 to 6.6.0. - [Release notes](https://github.com/aave/aave-v4-sdk/releases) - [Changelog](https://github.com/aave/aave-v4-sdk/blob/main/packages/react/CHANGELOG.md) - [Commits](https://github.com/aave/aave-v4-sdk/commits/@aave/react@6.6.0/packages/react) --- updated-dependencies: - dependency-name: "@aave/react" dependency-version: 6.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump sonner from 2.0.7 to 2.0.8 (#729) Bumps [sonner](https://github.com/emilkowalski/sonner) from 2.0.7 to 2.0.8. - [Release notes](https://github.com/emilkowalski/sonner/releases) - [Commits](emilkowalski/sonner@v2.0.7...v2.0.8) --- updated-dependencies: - dependency-name: sonner dependency-version: 2.0.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pablo <77742839+0xPabloLI@users.noreply.github.com> * test(e2e): 两处「拿假设当守卫」的排序前提改成发现式(修 #736 的 e2e-desktop 2/2) (#742) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com> * chore(hardcode): sync hardcoded assets and mappings (#745) * build(deps-dev): bump the npm_and_yarn group across 1 directory with 2 updates (#746) Bumps the npm_and_yarn group with 2 updates in the / directory: [smol-toml](https://github.com/squirrelchat/smol-toml) and [source-map-js](https://github.com/7rulnik/source-map-js). Updates `smol-toml` from 1.8.0 to 1.9.0 - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](squirrelchat/smol-toml@v1.8.0...v1.9.0) Updates `source-map-js` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/7rulnik/source-map-js/releases) - [Changelog](https://github.com/7rulnik/source-map-js/blob/main/CHANGELOG.md) - [Commits](7rulnik/source-map-js@v1.2.1...v1.2.2) --- updated-dependencies: - dependency-name: smol-toml dependency-version: 1.9.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: source-map-js dependency-version: 1.2.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(hardcode): sync hardcoded assets and mappings (#749) * chore(hardcode): sync hardcoded assets and mappings (#755) * build(deps): vitest 4→5 连同 @vitest/coverage-v8 一起升(取代 #731) (#748) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * build(deps): vitest 4→5 连同 @vitest/coverage-v8 一起升(取代 #731) #731 只升 vitest、没升 @vitest/coverage-v8,而 vitest 5 的 peer 要求 coverage-v8 同版本 ⇒ 严格 npm ci 失败,peer-dep-check 是 dev 的必填项,故该 PR 合不进去。 验证:3793 单测 + 137 scripts 测试全绿;npm ci 严格安装通过;typecheck/lint 0 error; coverage 门 rc=0(All files 73.17/66.79/64.36/75.13 vs v4 基线 73.23/66.82/64.36/75.13, 阈值 72/66/63/74 均在上方)。 * build(deps): overrides 钉 postcss-selector-parser 7.1.6 以过 pre-push 的 osv 门 6.1.4 命中 GHSA-rj75-hqrm-r3gf(5.9,dev-only);6.x 无修复版(最高 6.1.4),修复只在 7.1.6, 而 postcss-nested / tailwindcss 均声明 ^6.1.x ⇒ 正常升级路径不存在,只能像 fast-uri/js-yaml/ws 那样用 overrides 钉。 跨大版本强钉的验证:npm run build(Tailwind 实际编译)rc=0,且产出的 CSS 逐字节一致 (index-DyVJKc4h.css / vendor-blockchain-DR00Q9RG.css 内容哈希同名,拼接 md5 相同), osv-scanner rc=0。备选是 osv-scanner.toml 的 ignoreUntil 条目(AAV-1328 对 braces 的做法)。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com> * chore(harness): worktree 不再静默跳门(绝对 hooksPath)+ E2E_PROXY 覆盖 prewarm + renovate vitest 族分组 (#753) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(hardcode): sync hardcoded assets and mappings (#744) * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * chore(session-board): qoder-1006a 注销(#748/#747 交付 + worktree 缺 .husky/_ 导致 pre-push 整条门静默不跑 的机制发现) * build(deps): overrides 钉 postcss-selector-parser 7.1.6 以过 pre-push 的 osv 门 6.1.4 命中 GHSA-rj75-hqrm-r3gf(5.9,dev-only);6.x 无修复版(最高 6.1.4),修复只在 7.1.6, 而 postcss-nested / tailwindcss 均声明 ^6.1.x ⇒ 正常升级路径不存在,只能像 fast-uri/js-yaml/ws 那样用 overrides 钉。 跨大版本强钉的验证:npm run build(Tailwind 实际编译)rc=0,且产出的 CSS 逐字节一致 (index-DyVJKc4h.css / vendor-blockchain-DR00Q9RG.css 内容哈希同名,拼接 md5 相同), osv-scanner rc=0。备选是 osv-scanner.toml 的 ignoreUntil 条目(AAV-1328 对 braces 的做法)。 * chore(harness): 把 pre-commit/pre-push 钉成绝对 hooksPath,worktree 不再静默跳门 husky 写的 core.hooksPath 是相对值 .husky/_,而该目录只在 npm install 的 prepare 里生成(且自我忽略:.husky/_/.gitignore 内容是 *)。新 worktree 没这个目录 ⇒ git 找不到任何 hook ⇒ pre-commit/pre-push 整条不执行,而 commit/push 仍退出 0。 本仓有四道门只存在于 pre-push(osv-scanner / semgrep / knip / dup:check),CI 里 没有对应 job,所以这条路会产出「PR 全绿但门从没跑过」。#747 就是这样推上去的。 prepare 改为 `npx husky && node scripts/husky-hooks-path.mjs`:把共享 config 里的 hooksPath 指向主 checkout 的绝对路径,任何 worktree 都解析得到;配套在两个 hook 开头加 node_modules 守卫,缺依赖时明确报错。opt-out: HUSKY_HOOKS_PATH_ABSOLUTE=0; 自查: npm run check:hooks-path。附 17 条 node:test 覆盖判定表(不覆盖非本仓所有的 hooksPath、幂等、只认字面量 0 才关闭)。 另在 renovate.json 给 vitest 族加分组规则:vitest 5 的 peer 要求 @vitest/* 同版本, 只升一半会让严格 npm ci 在 peer-dep-check(dev/main 均必填)上失败——#731 即此病, 换成 Renovate 也会重犯,除非同批评审(照 node runtime 那条的先例)。 * fix(e2e): E2E_PROXY 也作用于 prewarm 浏览器,直连变慢时不再在第一条测试前整条中断 playwright.config 只把 proxy 放进 project 的 use,覆盖的是"测试用的浏览器"; e2e/global-setup.ts 自己 chromium.launch() 起的那个始终走直连。直连吞吐一差, warm-up 就卡在 portfolio-mode-toggle 的 120s 预算上,整轮在任何一条测试开始之前 中止——于是文档里的 E2E_PROXY 看起来完全失效。 实测判据(2026-10-07):/markets 三次直连 ttfb 1.4–5.0s 但 total 82s/100s/150s+ 都还没传完(HTTP 都 200),而本机 mixed 口 7891 用 4.2s。所以旧注释里那句 "API 已在 ~1s 返回 200"正是被证伪的前提:200 不说明传输时间。 改法是把代理判定抽成 e2e/browserProxy.ts 的单一来源,两个消费者都从这里取, 避免再次各写各的;src/test/browserProxy.test.ts 的 R8 是 wiring guard,直接断言 两处都调用 browserProxyArgs 且不再手写 process.env.E2E_PROXY。 验证:8 条用例绿、typecheck rc=0、e2e 显式 tsc rc=0(e2e 不在 lint/typecheck 覆盖内)、 E2E_PROXY 下 test:e2e:pre-push 63 passed / 1 flaky(既存:标题由实盘数据生成, retry 后标题变化报 Test not found)/ rc=0;改前同一命令在 warm-up 处 TimeoutError 中止。 * fix(harness): pre-commit 先生成 icon/token manifest,再跑 typecheck 三个 *.generated 模块是 gitignore 的生成物:npm test 的第一步会生成它们,但 pre-commit 把 typecheck 排在 npm test **之前** ⇒ 在一个刚 npm install、还没跑过测试的 linked worktree 里, 任何提交都死在 TS2307 Cannot find module './chainIconManifest.generated',读起来完全像代码回归。 对照实验(在 /tmp 新 worktree 里做的):删掉生成物 → typecheck 报 3 条 TS2307; 跑 node scripts/generate-icon-manifests.mjs → 0 条。命令与 npm test 用的是同一个, 生成物本身仍被忽略,不会污染提交。 注:这条改动要等它进了主 checkout 的工作树才会真正被执行——绝对 hooksPath 下 git 跑的是 主 checkout 的 .husky/* 内容(PR 正文里已记这条限制)。 * build(deps): handlebars 4.7.9→4.7.10 以过 pre-push 的 osv 门 2026-10-08 新公布的通告把 openapi-zod-client 的传递依赖 handlebars 4.7.9 标成 2 条 Critical(GHSA-8r5x-fm3f-whwj 9.8 / GHSA-p8wg-vrv2-v86f 9.2)+ 1 条 Medium, 修复只有 4.7.10。这个版本在 lovable 与 dev 上早就存在,是今天才被判 vulnerable ⇒ 所有人从 lovable/dev 派生的分支都推不动(osv 只在 pre-push,CI 无对应 job)。 走正常解析路径 `npm update handlebars`(父节点声明的范围允许),不新增 override; lock 只动这一项。7 天等待期门按 direct 判定,本仓 check:dep-age 通过(0 direct、 3 transitive),所以这里没有绕策略——是 osv 与等待期此刻的方向相反,选安全侧。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com> * ci(deps): 删除 Dependabot peer-conflicts 报告 workflow + dependabot-behavior.md 改写为现状 (#747) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有修复版(npm 最新就是 3.0.3;OSV ranges 只有 last_affected、没有 fixed 事件), 所以不能像三天前 axios 那次用 overrides 抬版本解决。它是 tailwindcss@3 → chokidar/micromatch 的 dev 传依赖,不在生产产物里。 只豁免这一条、带理由与 ignoreUntil=2027-01-31(实测:未来日期放行、过期后重新报错, 是真执行的时间闸)。到期若仍无修复版,走根治:换掉 tailwind v3 的 watch/glob 依赖链。 AAV-1328。 * docs(lessons): 记 osv-scanner 配置的四个反直觉点 自动发现的文件名不能带点(.osv-scanner.toml 实测不生效);未知键是整份配置拒绝 加载而非忽略该条,写错等于豁免全废;ignoreUntil 需 RFC3339 且真的会过期(两个方向 都实测过);osv 只在 pre-push、CI 的 security-audit 是 --omit=dev + continue-on-error, 所以存在「CI 全绿但所有人都推不动」的形态。 * chore(deps): renovate 配置改为 Renovate 自身惯用法,并纳入 Dev Container 相对上一版的四处调整,依据都取一手源码: 1. 取消 weekly schedule,回到引擎默认 at any time,改由 prHourlyLimit=2 + prConcurrentLimit=5 节流。按小时滚动才是 Renovate 的模型;把一批更新攒到周一 是 Dependabot 的成批观感,代价是双轨期看不到任何动作、出问题也发现得晚。 prConcurrentLimit 5 仍对齐 dependabot.yml 的 open-pull-requests-limit。 2. 纳入 devcontainer manager:.devcontainer/devcontainer.json 的 image(mcr.microsoft.com/devcontainers/typescript-node:22)与 features(ghcr.io/devcontainers/features/github-cli:1)此前无人看守—— dependabot.yml 只配了 npm 与 github-actions。该 manager 同时提取 image 与 features 两处版本(源码 manager readme),正好覆盖我们 Codespaces 的两个 版本点。typescript-node 与 CI 的 node-version 是跨消费者契约,故一律人工评审, 不进 automerge 标签。 3. minimumReleaseAge=7 days 只加在 github-actions 的 non-major 规则上,不是全局。 它复刻 scripts/check-dep-release-age.mjs 的供应链等待期,而那道门的判据是 package-lock 的 diff,看不见 workflow 里的 action 钉版 ⇒ actions 此前没有任 何等待期保护,这里是唯一能落地该策略的位置。 4. 同时设 minimumReleaseAgeBehaviour=timestamp-optional,因为等待期依赖 release timestamp,而源码注明 Docker 源只在 Docker Hub 取 tag_last_pushed; 我们两个引用都在 mcr.microsoft.com 与 ghcr.io,无时间戳时默认的 timestamp-required 会把更新判成 pending 永久搁置(util/minimum-release-age.ts 的 isPending 分支)。devcontainer 规则因此不加等待期,只设人工评审。 automerge 仍保持 false:合并决策继续走现有 automerge.yml 的 PAT 路径,避免开出 第二条合并通道(也保住 lovable→dev MERGE / 其余 SQUASH 的策略)。 生效前提:Renovate 只读默认分支的配置,本文件需再经 lovable→dev→main 一轮。 * docs(roadmap): 10-05 盘点——osv 门恢复已关 AAV-1328;faq-anchor 判为负载争用 AAV-1320 在 Linear 已 Done 却还留在「仅列 open」表里,按文件自身规则移出。 * docs(board): qoder-0929b 补第二段交付(AAV-1328 关票、AAV-1327 建票、faq-anchor 判负载) * docs(board): qoder-0929a 第五段——dev 已被 #716/#717 改写方向,改为以 dev 为基底合并并修两处放行面 * ci(renovate): 把 node 的两处真值绑成同一批,并让 pre-push e2e 单 worker node 版本在本仓有两个家:workflow 的 node-version(走 node 数据源)与 Dev Container 镜像 typescript-node(走 docker 数据源),二者之间没有任何机制保证同步。正在开的 #727 就是这个裂口的形状——它只改 6 个 workflow 到 v24,不碰 .devcontainer,合下去 CI 就跑 Node 24、Codespaces 仍是 22(本仓无 .nvmrc、package.json 无 engines,所以 这两处就是全部真值)。 做法是跨 manager 分组而不是手工拼一个 PR:后者只修这一次,前者让"必须同批"成为 配置约束——matchDatasources [node, docker] + matchPackageNames [node, /typescript-node/] → groupName "node runtime",标签 manual-review。分组只在 devcontainer manager 生效后才有意义,故依赖同一份配置里的 enabledManagers。 另:pre-push e2e 从 workers=2 降到 1。该路径跑冷启动 dev server,两条用例 (waitForTableReady、waitForLoadState('networkidle'))在别的 agent 门禁共用本机时 会超时——2026-09-29/30 两次推送被无关红挡下,安静复跑均全绿(其中 faq-anchor 本地 9/9、board 里另一 session 独立判为负载争用)。选择降并发而不是把用例加进 grep-invert 排除名单:排除等于让门禁少测东西,而慢一点不减覆盖。CI 侧的 2 分片 不变(跑 built preview,不受影响)。 验证:renovate.json 全部键经官方 schema 校验(含 matchDatasources/matchPackageNames/ groupName),7 条规则;prettier 干净;check:e2e-market-names rc=0。 * docs(board): qoder-0929a 第六段——Renovate 确认激活、Node 漂移用跨 manager 分组绑住、pre-push 降单 worker * docs(csp): 订正 AAV-1327 根因——被拦的是 CF bot 检测注入,不是 analytics beacon 建票时把 CF 边缘注入的 __CF$cv 块误归为 Web Analytics beacon。真浏览器复现 + 逐 inline script 扫字面量证明它加载 challenge-platform/scripts/jsd,且站内 已无 cloudflareinsights 引用;zone 在 Free 计划,该检测按一手文档不可关。 * docs(board,roadmap): 订正 faq-anchor 判定——非负载而是 networkidle 耦合实盘,建 AAV-1329 * ci(renovate): 给 @eslint/js 的 v10 上限找新家,并删两条假配置文档 #716 删 .github/dependabot.yml 时,AAV-1301 的 ignore(@eslint/js >= 10)没有去处: npm manager 已开、10.0.1 在 npm 上,下一个窗口会重新提出那个两次让 dev 的 peer-dep-check 与 lint 变红的 bump。用 allowedVersions "<10" 复刻原裁定,9.x 照旧流动。 droid-review.md 里 review_depth / security_scan_schedule 两条 workflow 实际并未设置, 删掉;后者改写为 `on: pull_request` 无 schedule 这种文件可证的说法。 * chore(hardcode): sync hardcoded assets and mappings (#738) * test(e2e): 排序前提改成发现式挑行,别让实盘数据决定 market filter 用例跑不跑 reserves-table-market-filter-pin (5) 用"第 3 行"当作"筛选后会改序"的保证。CI 在 2026-10-05 的快照里整页都是同一个 market,筛选什么都没动,于是 pin 断言量到行仍停在 第三位,两次尝试 y 值一致 —— 确定性,不是抖动。旧选择器面对同一份数据也点到同一颗 chip,所以这不是 #735 的改动引入的。 改成扫可见行的 market chip label,取第一个与首行不同的行;整页同 market 时按本仓 discovery+条件 skip 的既有形状跳过并写明原因。纯函数进 e2e/reserveDiscovery.ts, 配 8 行矩阵单测(去掉 reference 条件会红 4 条,确认断言真咬住实现)。 * test(e2e): 让 scenario-pin 的 Clear 路径等到"真的改序",别拿行数冒充排序守卫 :393 Path B 的等待是 poll tbody 行数 > 0,消息写着"等表格重新排序",实际只证明表格存在。 supply=900 在 CI 那份快照上没让列表移动,于是"按 Clear 必然触发 pin scroll"量到 0,首跑与 retry 同因,反复挡推送门。 改成用本文件已有的 getVisibleReserveOrder + didReorder(:259 早就这么守):候选场景值逐个试 到确实改序才继续,全都不改序就 skip 并写明原因。实测 :393 从 15.9s 失败变 9.7s 通过。 * test(e2e): Clear 路径不再要求"必须滚过",只守"结束后停在锚点带" 位置断言与"模拟子行仍可见"两次都通过,唯独 window.scrollBy 探针读 0:重新排序把目标行 上方移走之后,仅靠布局它就已经落进锚点带,不需要滚动。那条探针断言的是机制而不是契约, 且机制归 :259 那条用例专门拥有(改了序就滚、没改序就不滚),删掉重复不减少覆盖。 如实记:这是一次断言放宽,依据写在代码注释与看板第十一行段。 * chore(session-board): 登记 qoder-1006a(远端 open PR 收口:#731 vitest 5 迁移 + #741 收窄清理) * ci(deps): 删 dependabot peer-conflicts 报告 workflow,dependabot-behavior.md 改写为现状 dependabot.yml 随 #736 离开默认分支后 Dependabot 不再产版本 PR,该 workflow 失去输入; peer 冲突判定已由 ci.yml 的 peer-dep-check(严格 npm ci,dev/main 均必填)覆盖。 保留 dependabot-auto-triage.yml:security updates 仍启用,它是真出现 security PR 时唯一的分类器。 dependabot-behavior.md 原文整份指向已不存在的 .github/dependabot.yml,改写为现状 + `dependabot/*`→main 被 branch-flow-guard 挡死的成因与两条真解(本次实测 #746 用改 base 解决)。 * docs(dependabot): 订正「删配置=停管道」——#752 实证 Dependabot 仍会开版本 PR #736 把 dependabot.yml 带离 main 约 24 小时后,#752 于 2026-10-07T11:16Z 创建, 标题与 commit trailer(dependency-type: indirect + dependency-group)都是版本升级 PR 的 形状,且复用 #746 的分支与 head commit。本文件上一版写的「不再开版本升级 PR」是错的推论。 机制不下断言(配置缓存生效延迟、或对未满足的更新继续提案都有可能),改成给判据: diff vs dev 为空 ⇒ 关闭(内容已随 #746 进 dev,等 dev→main 上线即满足);非空 ⇒ base 改 dev 走发布流;不要加 ignore 规则去压制告警面与后续合法提案。 保留 auto-triage 的理由也换成实证:#752 带着 manual-review 且 triage 检查 pass。 --------- Co-authored-by: 0xPabloLI <0xPabloLI@users.noreply.github.com> * fix(ci): eslint 上限补上 eslint 本体(AAV-1301 只做了一半)+ renovate.json 漂移守卫 (#758) * docs(roadmap): AAV-1320 已上线并生产复验;AAV-1303 第一段交付,next pick 转第二段 * chore(hardcode): sync hardcoded assets and mappings (#713) * docs(csp): 记 AAV-1327(CF beacon 内联块被拦)并在 1320 spec 补第三类指针 生产每次加载有 1 条 CSP console error:Cloudflare 自动注入的 Web Analytics beacon bootstrap 被 script-src 拦。已实测证伪「加 hash」这条路——内联块里的 r/t 每请求都变 (两次拉取值不同),Chrome 建议的那个 hash 只对当次响应有效。 不是 #693/1320 引入的回归:1320 之前 script-src 就没有 unsafe-inline 也没有 hash, 这段一直被拦;1320 的 spec 只枚举了 consent 块与 gtag.js 两类。现在把第三类写进 spec,避免下个改 CSP 的 session 再当成新发现。 * chore(security): 豁免 braces 3.0.3 的 GHSA-vfj7-8cjw-p6xm,恢复被阻断的 pre-push 门 osv-scanner 从 10-04 起阻断所有本地推送:braces 3.0.3 命中 CVE-2026-93687(8.7 High), 而它上游没有…
Bumps vitest from 4.1.11 to 5.0.3.
Release notes
Sourced from vitest's releases.
... (truncated)
Commits
33cadeachore: release v5.0.3 (#11409)346d389fix(vm): don't reuse scripts across vite environments (#11395)f6c9a49fix(deps): pinwhy-is-node-runningto3.2.1to avoid users running into `...062c75dchore: fix standalone docs build, update exports maps (#11394)caf2887fix(vm): do not optimize deps from index.html (fix #11329) (#11360)50312ebfix(pool): preserve unique pool ids whengroupOrderis set (#11392)7c36748fix(browser): ignore page crash while cancelling (#11386)92ba7fcfix: scope cache key generators to projects (fix #11281) (#11301)38f9885fix(cache): revalidate imports of cached modules (#11381)b24585ffix: don't retry whentest.failsexpectedly failed (#11219)