Skip to content

fix(metadata-core): 让 test/ 层真的进 tsc,@ts-expect-error 不再是幽灵检查 (#5476) - #5592

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5476-metadata-core-test-tsc
Aug 5, 2026
Merged

fix(metadata-core): 让 test/ 层真的进 tsc,@ts-expect-error 不再是幽灵检查 (#5476)#5592
os-zhuang merged 1 commit into
mainfrom
claude/issue-5476-metadata-core-test-tsc

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5476

前提复核(origin/main @ e900015

前提成立,两条事实都在:

后果也如实复现:typecheck 是裸 tsc --noEmit,读的就是那份 config,所以 test/ 整棵树从没被任何类型检查器读过;TESTS_COVERED 只统计 include 根之下,本包 testFiles 计数为 0,既不报警也不进 TEST_DEBT

反向验证:常规方向(before green / after red),已跑

指令在 test/types.test.ts:51,钉的是 refKey 的入参形状。

  • 新 program 下删掉那一行 → 红:

    test/types.test.ts(51,7): error TS2353: Object literal may only specify known properties,
    and 'version' does not exist in type 'Pick< { org: string; type: ...; name: string;
    version?: string | undefined; }, "type" | ... 1 more ... | "name" >'.
    
  • origin/main 的旧 program(裸 tsc --noEmit)下删掉同一行 → 退出码 0,全绿。

也就是说:指令钉的是真事实refKey 收的是 Pick< MetaRef, 'org' | 'type' | 'name' >version 是多余属性),缺的只是编译它的程序。所以修法是保留指令、补上程序,而不是像 #5449 / PR #5546 那样删指令(那一例编译后报 TS2578 unused,方向是镜像的)。

路线选择:路线 2(sibling config),因为路线 1 在本包会撞 emit 约束

先量后定。直接把 include 扩成 ["src/**/*", "test/**/*"](issue 的路线 1)实测报 TS6059 x6

error TS6059: File '.../test/cache.test.ts' is not under 'rootDir' '.../src'.

本包 tsconfig.jsonrootDir: "src" + outDir: "dist",而这份 config 是要 emit 的dev: "tsc --watch")。把 rootDir 放宽到包根会把 dist/index.js 挪成 dist/src/index.js(打断 main / exports),并开始写 dist/test/**/*.test.js——后者正是 ci.yml「No compiled test files in any dist」拦的东西。emit 约束属于 build config,所以路线 1 只能连 rootDir 一起改,代价不可接受。

于是照两先例的既定形状走路线 2。

改动

packages/metadata-core/tsconfig.test.json(新增)

build config 的 sibling,package.jsontypecheck 指名它。相对 build config 只改两项,其余全部继承:

  • rootDirsrc 放宽到包根 —— 只影响 emit 布局,而这个 program 不 emit(noEmit: true);继承 src 时那六个 TS6059 是检查配错,不是测试写错;
  • noEmit: true

module 语义刻意不动,这是与 spec / client 两先例的唯一实质差异:那两个包没有 "type": "module",build config 的 NodeNext 把 ESM 测试当 CJS 编,报出来的是关于「检查」的错(TS2835 / TS1470 / TS2550),换 esnext / bundler 是对齐 vitest 的保真。@objectstack/metadata-core "type": "module",NodeNext 本就按 ESM 读这些文件,而且比 bundler 更严——它守住本包必须发布的 .js 导入后缀(bundler 下漏后缀能编过、Node ESM 运行时才炸)。没有要修的,就不改。strictness 一律继承,不放松

没有 test-typecheck-debt.json,这是刻意的

整个 test 层在新 config 下 0 error。逐文件 EXACT 收缩台账(scripts/check-test-typecheck.mts,spec 691 / client 6 才需要)在这里什么也装不下,却要给本包多加一个 tsx 依赖和两个脚本。零残余时裸 tsc --noEmit -p tsconfig.test.json更强的门:任何错误立即红,没有台账可加。将来真攒下改不动的残余,那时再接共享脚本。check-type-check-coverage.mjs 自己的 self-test fixture 就把 tsc --noEmit && tsc --noEmit -p tsconfig.test.json 列为合法形状。

(为佐证不是偷懒:共享脚本对本包也跑得通,输出 0 file(s) / 0 error(s) held in test-typecheck-debt.json——它确实只是空转。)

scripts/check-type-check-coverage.mjs

仅删 PHANTOM_PIN_DEBT 对应条目(RECONCILED 本来也会强制删),台账就此清空,并留一句说明「空是终态、下一例的入口是 PINS_CHECKED 变红而不是新增条目」。另有一处注释时态修正:testCoverage 的文档说「packages/metadata-core/test/ is outside include」——本 PR 之后这句变成假的,改为过去时并标注 #5476

验证(全部前台跑完,真实输出)

$ (cd packages/metadata-core && npx tsc -p tsconfig.test.json --showConfig)
include = ['src/**/*', 'test/**/*']
rootDir = ./  noEmit = True  module = nodenext
test/ files in program: ['./test/cache.test.ts', './test/canonicalize.test.ts',
 './test/errors.test.ts', './test/in-memory-repository.test.ts',
 './test/layered-repository.test.ts', './test/types.test.ts']

$ pnpm --filter @objectstack/metadata-core typecheck
> tsc --noEmit && tsc --noEmit -p tsconfig.test.json
TYPECHECK_EXIT=0

$ pnpm --filter @objectstack/metadata-core test
 Test Files  8 passed (8)
      Tests  103 passed (103)

$ node scripts/check-type-check-coverage.mjs --self-test
OK — 22 semantic case(s) + 11 observation case(s) hold.

$ node scripts/check-type-check-coverage.mjs
check-type-check-coverage: OK — 62/77 workspace packages type-checked (plus the root),
15 in the DEBT ledger (358 frozen raw errors), 1 exempt.

$ node scripts/check-nul-bytes.mjs
check-nul-bytes: OK (scanned 5520 tracked text file(s); ... no raw ASCII control bytes).

$ npx eslint scripts/check-type-check-coverage.mjs --no-inline-config   # exit 0
$ pnpm --filter @objectstack/metadata-core build                        # exit 0,dist 无 *.test.js

门是承重的,不是装饰:把 typecheck 改回裸 tsc --noEmit(即台账已删、配置没被指名)后重跑 gate:

check-type-check-coverage: 1 problem(s)
  • packages/metadata-core/test/types.test.ts: carries a `@ts-expect-error` directive but no
    tsc program the `typecheck` script runs compiles it, ...

无 changeset

照两先例的取舍:PR #5478 与 PR #5546 都没有 changeset。本 PR 只动 build / test 配置与仓库门,发布产物(dist)逐字节不变,对使用者不可见。

关联:#5286(不变式与台账来源)、#4311(typecheck 覆盖棘轮)、#5449 / PR #5546(client,镜像方向的那一例)、PR #5478(spec,机制来源)。


Generated by Claude Code

`packages/metadata-core/tsconfig.json` 的 `include` 是 `["src/**/*"]`,而六个测试
文件住在 sibling 的 `test/` 树里——没有任何 `exclude` 指向它们,它们只是落在 include
根之外。于是 `typecheck`(裸 `tsc --noEmit`,读的就是这份 config)从没读过它们,
`TESTS_COVERED` 也看不见(只统计 include 根之下,本包 testFiles 计数为 0)。
`test/types.test.ts:51` 那条 `@ts-expect-error` 因此从不被求值。

反向验证走的是常规方向:在新 program 下删掉该指令行,`test/types.test.ts(51,7)` 报
TS2353 —— `version` 不在 `Pick< MetaRef, 'org' | 'type' | 'name' >` 里;而在
origin/main 的旧 program(裸 `tsc --noEmit`)下删掉同一行,退出码仍是 0。指令钉的是
真事实,缺的只是编译它的程序,所以保留指令、补程序。

- `packages/metadata-core/tsconfig.test.json`:build config 的 sibling,
  `package.json` 的 `typecheck` 指名它(`tsc --noEmit -p tsconfig.test.json`)。
  只改 `rootDir`(`src` → 包根)和 `noEmit`:继承的 `rootDir: src` 会对全部六个
  `test/**` 报 TS6059,那是检查本身配错,不是测试写错;而在 BUILD config 里放宽
  `rootDir` 不行 —— 那份 config 是要 emit 的(`dev: tsc --watch`、`outDir: dist`),
  包根 rootDir 会把 `dist/index.js` 挪成 `dist/src/index.js` 并开始写
  `dist/test/**/*.test.js`,后者正是 ci.yml 拦的东西。
- 与 spec/client 两先例不同,module 语义**不动**:本包有 `"type": "module"`,
  NodeNext 已按 ESM 读这些文件,且比 `bundler` 更严(保住必须发布的 `.js` 导入
  后缀)。strictness 一律继承,不放松。
- 无 `test-typecheck-debt.json`:整个 test 层在新 config 下 0 error,逐文件 EXACT
  棘轮(spec 691、client 6 才需要)在这里什么也不装,却要多一个 `tsx` 依赖。零残
  余时裸 `tsc -p` 是更强的门:任何错误立即红,没有台账可加。
- 同 PR 删除 `scripts/check-type-check-coverage.mjs` 的 `PHANTOM_PIN_DEBT` 条目,
  RECONCILED 本来也会强制删。台账就此清空——把 `typecheck` 改回裸 `tsc --noEmit`
  验证过:PINS_CHECKED 立刻红,这条门是承重的。

Fixes #5476

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7WetGmnfoXNn8cLieKKmx
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 8:02pm

Request Review

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file size/s and removed dependencies Pull requests that update a dependency file labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-core.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-core)
  • content/docs/plugins/packages.mdx (via @objectstack/metadata-core)
  • content/docs/releases/v12.mdx (via @objectstack/metadata-core)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Aug 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 20:13
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 7e58212 Aug 5, 2026
27 of 28 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5476-metadata-core-test-tsc branch August 5, 2026 20:24
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 size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/metadata-core/test/types.test.ts@ts-expect-error 是幽灵检查:文件在 include: ["src/**/*"] 之外,没有任何 exclude 指向它

2 participants