Skip to content

fix(scripts): 删除 check-doc-links 测试中已失效的 @ts-expect-error(修复净 main 上的 TS2578) - #3505

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3504-hotfix-ts2578
Aug 7, 2026
Merged

fix(scripts): 删除 check-doc-links 测试中已失效的 @ts-expect-error(修复净 main 上的 TS2578)#3505
yinlianghui merged 1 commit into
mainfrom
claude/issue-3504-hotfix-ts2578

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#3504

症状

origin/main(f995a45)上 pnpm type-check:scripts 红:

scripts/__tests__/check-doc-links.test.ts(7,1): error TS2578: Unused '@ts-expect-error' directive.
ELIFECYCLE Command failed with exit code 2.

CI 的 type-check job 跑的就是这条命令,因此当前所有开着的 PR 的 Type Check 全部误红 —— 红的不是它们的改动。

成因:两个各自绿的 PR 合并后的语义冲突

这是典型的 semantic conflict:两个 PR 在文本上毫不相干、各自 CI 全绿,合并结果为红。

  1. PR fix(docs): check-doc-links 解析相对链接,并修掉它现在能看见的 16 个失效目标 (#3479) #3489(449227d) 新增 scripts/__tests__/check-doc-links.test.ts,导入行上带
    @ts-expect-error — plain-JS CI helper, intentionally untyped
    写下时是正确的:彼时仓库里没有任何 tsc program includescripts/(根 tsconfig.json 只含 packages/examples/apps),该 .mjs 导入确实无类型可用,压制注释既无害也无人检验。

  2. PR ci(scripts): 用独立 tsconfig.scripts.json 给 scripts/ 补上类型门 (#3494) #3498(f995a45) 引入 tsconfig.scripts.json,首次把 scripts/**/*.ts 纳入类型检查,并且经过实测选择了 allowJs: true(见该文件内的长篇注释)。allowJs.mjs 助手的类型从助手自身推断得到,于是所有这类压制注释在同一瞬间集体变成 Unused。ci(scripts): 用独立 tsconfig.scripts.json 给 scripts/ 补上类型门 (#3494) #3498 已经修掉了它 base 上的 5 处同类。

  3. fix(docs): check-doc-links 解析相对链接,并修掉它现在能看见的 16 个失效目标 (#3479) #3489 的这第 6 处恰好落在 ci(scripts): 用独立 tsconfig.scripts.json 给 scripts/ 补上类型门 (#3494) #3498 开分支与合并之间的窗口里 —— 对 ci(scripts): 用独立 tsconfig.scripts.json 给 scripts/ 补上类型门 (#3494) #3498 不可见,而 fix(docs): check-doc-links 解析相对链接,并修掉它现在能看见的 16 个失效目标 (#3479) #3489 那边根本没有任何东西 type-check 它。两边 CI 都绿,merge base 之后才红。

换句话说:压制注释没有"错",是它脚下的地基(类型门的覆盖面)变了。

改动

只删掉那一行,不动其它任何东西:

-// @ts-expect-error — plain-JS CI helper, intentionally untyped
import { collectBrokenLinks, routeExists, stripCode } from '../check-doc-links.mjs';

一个文件,一行,1 file changed, 1 deletion(-)。无 changeset —— 改动在 scripts/ 下的 CI 测试文件里,不是任何已发布包的一部分,对用户不可见。

验证

先在净 origin/main(未做任何改动、git status 干净)上复现,再改,再复跑:

阶段命令结果
改动前(净 main)pnpm type-check:scripts❌ exit 2,TS2578 at (7,1)
改动后pnpm type-check:scripts✅ exit 0,无输出
改动后pnpm exec vitest run scripts/__tests__/check-doc-links.test.ts --maxWorkers=2Test Files 1 passed (1) / Tests 22 passed (22)
改动后node scripts/check-control-bytes.mjsOK (scanned 3686 tracked text file(s); skipped 85 binary)

运行期行为从不依赖该指令:@ts-expect-error 是纯 tsc 编译期构造,vitest 经 esbuild 转译时把它当普通注释剥离,对 collectBrokenLinks / routeExists / stripCode 的实际导入与执行零影响。所以这里 22 个用例保持全绿是预期方向,而不是"测试没覆盖到"的空绿 —— 真正把这次改动钉住的是 type-check:scripts 由红转绿这一条。

另在被改文件上做了超出门禁扫描面的控制字符自查(grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]',无命中;file 报 UTF-8 text)。

后续(不在本 PR 范围)

本 PR 是止血,刻意只删这一行。这类"新类型门上线 + 并行 PR 在窗口期落地旧式压制"的碰撞会重演;是否需要一条更结构化的防线(例如让 #3498 那类覆盖面扩张 PR 在合入前对 origin/main 最新态复跑一次,或把 type-check:scripts 纳入 merge queue 的必需检查),留给维护者判断,未在此另开 issue。


Generated by Claude Code

净 main 上 `pnpm type-check:scripts` 红:
scripts/__tests__/check-doc-links.test.ts(7,1):
error TS2578: Unused '@ts-expect-error' directive.
这是两个各自绿的 PR 合并后产生的语义冲突:
- #3489(449227d)新增该测试时带 `@ts-expect-error`,当时正确 ——
彼时没有任何 tsc 程序 include `scripts/`,该导入确实无类型。
- #3498(f995a45)引入 `tsconfig.scripts.json`(`allowJs: true`),
首次把 `scripts/**/*.ts` 纳入类型检查。`allowJs` 让 `.mjs` 助手的
类型可被推断,于是所有这类压制注释同时变成 Unused。#3498 修掉了它
base 上的 5 处同类,而 #3489 的第 6 处恰好落在其窗口之间。
两 PR 的 CI 各自为绿,合并结果为红 —— 当前所有 PR 的 Type Check 全部误红。
仅删除该行。运行期行为从不依赖该指令(它只影响 tsc,不影响 vitest),
`check-doc-links.test.ts` 22 个用例保持全绿。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercelBot commented Aug 7, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 7, 2026 1:29am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 7, 2026 02:10
@yinlianghui
yinlianghui added this pull request to the merge queueAug 7, 2026
Merged via the queue into main with commit 15352bfAug 7, 2026
17 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3504-hotfix-ts2578 branch August 7, 2026 02:11
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Aug 10, 2026
…t path filtering into the jobs (objectstack-ai#3523 steps 1-2) (objectstack-ai#3722)
* ci(step 1): subscribe ci/lint/control-bytes/docs-links to merge_group (objectstack-ai#3523)
Step 1 of objectui#3523, on its own commit as the issue's ruling requires:
this is a pure addition and changes nothing about which pull requests or
pushes run CI.
The merge queue is enforced on this repository by a ruleset (objectstack-ai#3243 measured a
direct push to `main` returning 405 `Changes must be made through the merge
queue`), but not one of the 22 workflows subscribed `merge_group` — repo-wide
`event=merge_group` runs stood at total_count = 0. A queue nothing subscribes
to can only carry an empty required-check set, so it rebuilt each PR on the
current `main` and let it through without validating anything. On 2026-08-07
that was cashed in: objectstack-ai#3503 / objectstack-ai#3510 / objectstack-ai#3516 merged between 02:11Z and 02:15Z
with `Type Check` at conclusion=failure, on a `main` poisoned by objectstack-ai#3498, and
objectstack-ai#3505 had to hot-fix it.
The four workflows named by the issue now carry the trigger. It is spelled
`merge_group: types: [checks_requested]`; `checks_requested` is the only
activity type GitHub defines for this event today, so this is equivalent to
objectstack's bare `merge_group:` and merely refuses to inherit a second type
added later.
Two `ci.yml` jobs additionally needed to be told the third event exists —
both changes are no-ops for `pull_request` and `push`:
- `test` moves from `if: github.event_name == 'pull_request'` to
`!= 'push'`, so a queue build actually runs the suite instead of skipping
every shard.
- `docs`'s `should_run` treated anything that is not `push` as a pull request
and diffed `github.event.pull_request.*`, which is null on a queue build —
an empty revision range, i.e. the site build silently skipped on the last
check before `main`.
Verified rather than assumed: `concurrency` needs no merge-queue case. On
`merge_group` the group expression falls back to `github.ref`, which is the
queue's own generation — measured on objectstack, whose 3552 queue runs report
head_branch `gh-readonly-queue/main/pr-6594-251e888a…`. That can collide with
neither a PR group (a bare number) nor a push group (`refs/heads/main`).
Refs objectstack-ai#3523
* ci(step 2): move ci/lint path filtering out of the PR trigger into the jobs (objectstack-ai#3523)
Step 2 of objectui#3523, deliberately a separate commit from step 1: step 1
adds runs that did not exist, this one changes which pull requests start CI,
and mixing them would make that impossible to review apart.
`on.pull_request.paths-ignore` skips the WHOLE workflow when every changed
file matches, and GitHub has no per-job path filter. A docs-only or
changeset-only PR therefore started neither `ci.yml` nor `lint.yml` — objectstack-ai#3509
measured zero check runs from them. A check that is never *created* does not
fail a required-status-check rule, it leaves the pull request pending; inside
the merge queue it fails on the ruleset's 60-minute status-check timeout. So
none of `Lint`, `Type Check`, `Test (shard N/4)`, `Build & E2E` or
`Changeset Fixed Group Check` could be made required while the filter lived
on the trigger — which is why the queue's required set was empty to begin
with.
The filter is not deleted, it moves. `type-check`, `test`, `e2e` (ci.yml) and
`lint` (lint.yml) each open with a `Decide whether this change needs a full
run` step, and every step after it carries
`if: steps.relevant.outputs.should_run == 'true'`. The job always runs and
always reports; the paths decide only whether it does any work. That is the
shape `ci.yml`'s `docs` job has used since objectstack-ai#3450 — not a new mechanism — and
the exclusion lists are byte-for-byte the `paths-ignore` they replace, so
which PRs pay for a full run is exactly as before.
Two deliberate narrowings, both stated so they can be argued with:
- `paths-ignore` stays on the `push` trigger. Branch protection and the merge
queue judge pull requests and queue builds, never pushes to `main`, so the
push lane gains nothing from losing it and would cost a full CI run on
every docs merge. It also gives the ignore list one authored home, which
`merge-queue-reporting.test.ts` then pins the in-job copies against.
- `changeset-check` is not gated. It is a checkout and one `node` call, so
short-circuiting it would cost more in complexity than it saves.
The gate fails OPEN: an unresolvable diff runs everything rather than
reporting green having built nothing (objectstack#4928's filter contract).
Measured both ways against a fixture repository — as shipped an unreachable
base sha yields should_run=true; rewritten with the `|| echo ""` spelling
`ci.yml`'s older `docs` gate still uses, the same input yields
should_run=false, i.e. a silent full skip. That pre-existing `docs` gate is
reported separately rather than changed here.
`scripts/__tests__/merge-queue-reporting.test.ts` pins both steps; against
the pre-change workflows 9 of its 10 assertions go red, each naming its own
regression. `lint-workflow.test.ts`'s TypeScript-exclusion tripwire now reads
the in-job list as well as the trigger, which is where that list now lives.
Refs objectstack-ai#3523
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

main 的 Type Check job 全红:#3489 与 #3498 的语义冲突,check-doc-links.test.ts@ts-expect-error 在 allowJs 下变成 TS2578

2 participants

@yinlianghui@claude