Skip to content

build(deps-dev): bump vitest from 4.1.11 to 5.0.3 - #731

Closed
dependabot[bot] wants to merge 4 commits into
devfrom
dependabot/npm_and_yarn/dev/vitest-5.0.3
Closed

dependabot[bot] wants to merge 4 commits into
devfrom
dependabot/npm_and_yarn/dev/vitest-5.0.3

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Bumps vitest from 4.1.11 to 5.0.3.

Release notes

Sourced from vitest's releases.

v5.0.3

   🐞 Bug Fixes

    View changes on GitHub

v5.0.2

   🐞 Bug Fixes

... (truncated)

Commits

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Oct 5, 2026
@dependabot
dependabot Bot requested a review from 0xPabloLI as a code owner October 5, 2026 05:24
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Oct 5, 2026
@dependabot
dependabot Bot force-pushed the dependabot/npm_and_yarn/dev/vitest-5.0.3 branch from 4bae45a to 775adf6 Compare October 5, 2026 11:46
@dependabot
dependabot Bot force-pushed the dependabot/npm_and_yarn/dev/vitest-5.0.3 branch from 775adf6 to c541577 Compare October 5, 2026 11:56
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>
@vercel

vercel Bot commented Oct 6, 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 6, 2026 11:44am UTC

@0xPabloLI

Copy link
Copy Markdown
Owner

由 #748 取代。

本 PR 合不进去的根因不是 rebase 问题:Dependabot 只升了 vitest,而 vitest 5 的 peer 要求 @vitest/coverage-v8: 5.0.3(仓库仍钉 ^4.1.11)⇒ 严格 npm ci 失败,而 peer-dep-check 是 dev 与 main 都必填的检查,所以无论如何 rebase 都是红的。

renovate.json 的 npm 侧没有 vitest 族分组规则,换成 Renovate 只会提出同样半截的 PR,因此 #748 把两个包一起升,并带上推送门要求的 postcss-selector-parser osv 修复。验证证据(v4 基线同机对照:3793 单测、npm ci rc=0、typecheck/lint 0 error、coverage 阈值门 rc=0 且数字与 v4 差 ≤0.06pp、pre-push e2e 64 passed)写在 #748 正文里。

@0xPabloLI 0xPabloLI closed this Oct 6, 2026
auto-merge was automatically disabled October 6, 2026 16:22

Pull request was closed

@dependabot @github

dependabot Bot commented on behalf of github Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

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 @dependabot ignore this major version or @dependabot ignore this minor version.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot
dependabot Bot deleted the dependabot/npm_and_yarn/dev/vitest-5.0.3 branch October 6, 2026 16:22
0xPabloLI added a commit that referenced this pull request Oct 9, 2026
* 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>
0xPabloLI added a commit that referenced this pull request Oct 9, 2026
… 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>
0xPabloLI added a commit that referenced this pull request Oct 9, 2026
…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>
0xPabloLI added a commit that referenced this pull request Oct 9, 2026
)

* 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>
0xPabloLI added a commit that referenced this pull request Oct 9, 2026
* 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),
而它上游没有…

This branch was successfully deployed

1 active deployment
Preview — d72c7105 Deployed Oct 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code manual-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant