Skip to content

fix(spec): packages/spec/scripts/** 进入类型检查程序 —— 新增 tsconfig.scripts.json,修掉全部 43 条错误 - #6398

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5475-scripts-typecheck-coverage
Aug 7, 2026
Merged

fix(spec): packages/spec/scripts/** 进入类型检查程序 —— 新增 tsconfig.scripts.json,修掉全部 43 条错误#6398
os-zhuang merged 1 commit into
mainfrom
claude/issue-5475-scripts-typecheck-coverage

Conversation

@claude

@claude claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #5475

缺陷

packages/spec/tsconfig.jsoninclude["src/**/*"]#5286 新增的 tsconfig.test.json 同样止于 src。于是整个 packages/spec/scripts/** 不在本包任何 tsconfig 的 program 里 —— 不是「被 exclude 排除」,而是压根没被 include 选进来,所以连 check:type-check-coverageTESTS_COVERED 都数不到它(该计数只统计 PRIMARY 配置 include 根之下的测试文件)。

这批文件不是死代码:29 个 scripts/**/*.test.tsvitest.config.ts 真的在每次 pnpm test 里跑;另有约 48 个文件就是门禁本体 —— build-schemas.tsgen:schema,本包全部 JSON Schema 与 #4650 authorable-surface 删除门禁的判定都由它产出)、build-openapi.tsliveness/check-liveness.mtscheck-strictness-ledger.mts 等。tsx 只转译,不做类型解析。

同一失效类的第三种形态:#4311 是「包级没有 typecheck 任务」,#5286 是「测试层被 exclude 出唯一的 program」,本单是「根本没被 include 进来」。

重测:原单基线已过时

原单基线 01c0baef9(08-05)记录 16 文件 / 33 错误。在当前 origin/main 上重测为 16 文件 / 43 错误。逐层对照:

原单 实测 说明
config TS5097 x13 x15 .mts 后缀 import,需 allowImportingTsExtensions
config TS2593/TS2304 x2 x0 已消失:29 个 scripts 测试文件现在全部显式 from 'vitest' 导入,无一依赖全局
config TS7016 x1 x3 无声明的 .mjs import,消费者从 1 个涨到 3 个
code TS2339 x5 x13 build-schemas.tsnever 簇,比原单大一倍多
code TS2345 x1(新) build-schemas.ts 今天由 #6360 引入
code TS2345 x0 x2(新) authorable-defaults.test.tsreadonly 元组
noise TS6133 x7 x7 未使用局部/导入
code TS7006 x1 x2 隐式 any 参数

原单引用的 build-schemas.ts:1208-1228 行号确已失效(真实位置在 1733–1954 区间)。

方向选择:单开 tsconfig.scripts.json(原单选项 2)

原单给了两条路且未定。选 2,两条实测理由:

  1. allowImportingTsExtensions 只有这里需要。 scripts/liveness/ 下的 .mts 模块互相以 TS 后缀导入(./evidence.mts),tsx 能解析、tsc 不能。把这个开关打到 tsconfig.test.json 上,等于额外允许 src/**/*.test.tsimport './x.ts' —— 而发射产物的 build 配置仍然拒绝这种拼写。一个对 src 毫无收益、却在那里开出新分歧的开关,应该只装在需要它的 program 上。
  2. test-typecheck-debt.json 是每包唯一且精确的台账。 check-test-typecheck.mts 双向判定(增长红、缩小红、归零红),第二个 project 共用同一台账文件会让 79 条 src/** 条目立刻全部报「graduated」。而 scripts/**零台账条目进入的 —— 合并后暴露的 43 条全部在本 PR 修完,所以 plain tsc 反而是更严的门禁:台账允许「加一行」来增长,这里一条也不允许。

tsconfig.scripts.json 不放松任何 strictnessstrict / noUnusedLocals / noUnusedParameters / noImplicitReturns 全部从根配置继承,只动 module 语义与 program 根(rootDir 放宽到 .,因为 scripts/src/ 之外;noEmit 开着,不写任何东西)。

never 簇:是类型谎言,不是潜伏的运行时 bug

本单最有价值的产出。build-schemas.ts 里:

let gitResolvedAnchor: { rev: string; keys: string[] } | null = null;

function resolveSurfaceBase() {
  ...
  gitResolvedAnchor = { rev, keys: doc.keys };   // 唯一赋值,在函数体内
  ...
}

TypeScript 的控制流分析不跟踪函数体内对模块级 let 的赋值,所以在之后每一个顶层读取点,它仍被收窄为初始化式的 null。后果不是「少检查了一个变量」,而是:

最小复现已单独验证:

let anchor: { rev: string; keys: string[] } | null = null;
function resolve(): void { anchor = { rev: 'abc', keys: [] }; }
resolve();
console.log(anchor?.rev);          // TS2339: Property 'rev' does not exist on type 'never'
if (anchor) { const a = anchor; console.log(a.keys.length); }  // 同上

修法是契约式的:把这个模块级旁路改成显式返回值 SurfaceBaseResolution.gitAnchor,赋值回到调用方自己的控制流里,CFA 就能看见。git 路径设值、in-tree 路径留 null,运行时行为逐字不变

⚠️ 一条预测落空,如实记录:我预期这约 100 行恢复检查后会浮出 1–2 条新错误。实测零条。所以结论必须写实 —— 这不是一个潜伏的运行时 bug,而是一段恰好写对、但从未被验证过的代码。它的真实代价是「不可证伪」:见下方 RV-3,一条真实的类型错误放进那个块里,也只会被 never 吞掉。

另一条实打实的漂移:readSurfaceKeysAtRev 的参数类型

scripts/build-schemas.ts(2126,5): error TS2345:
  Argument of type '"defaults"' is not assignable to parameter of type '"keys" | "schemas"'.

sharded-artifacts.ts 导出 ShardArrayField = 'keys' | 'schemas' | 'defaults',而 build-schemas.tsreadSurfaceKeysAtRev 手抄了一份更窄的 'keys' | 'schemas'今天 #6360 给这个联合加了 'defaults' 成员并新增了一个传 'defaults' 的调用点,手抄的那份没跟上 —— 运行时无害(值只是转发给接受宽类型的 readShardedKeysAtRev),但正是这个 program 存在的意义。修法是消费导出的 ShardArrayField,而不是加宽手抄件。

逆向验证(预测先写、后跑)

预测原件:/tmp/.../issue-5475/predictions.md,六条全部在执行前写定。

# 场景 预测 实测 结果
RV-1a 非测试脚本(build-schemas.ts)植入类型错误 → 新门禁 TS2322 ... 1580,7
RV-1b 同上 → build 配置 tsc --noEmit 绿(不在其 include) exit 0
RV-1c 同上 → origin/main 的整条 typecheck 链 绿 —— 缺陷本身 exit 0,check:test-typecheck: OK
RV-3 后 在原 never 块内植入 const probe: number = anchor.rev 红,且报真实类型 TS2322: Type 'string' is not assignable to type 'number'
RV-3 前 同一行,配 origin/main 的 never 版本 never,看不见真实类型 TS2339: Property 'rev' does not exist on type 'never'
RV-5 前 给 scripts 测试文件加 @ts-expect-errorcheck:type-check-coverage 红(幽灵 pin) 红,指名该文件
RV-5 后 同上 绿,且 pin 真被求值 门禁绿;check:scripts-typecheckTS2578: Unused '@ts-expect-error' directive
RV-6 check:type-check-coveragetestFiles 计数 不变 前后同为 556

RV-3 那对是本 PR 最锋利的一处证据:同一行代码,改前被 never 吞掉(never 可赋给任何类型),改后报出真实的 string / number 不匹配。这就是「那 100 行不可证伪」的直接演示。

RV-5 同样值得单独读:那条 @ts-expect-error 的判定换了门而不是消失了 —— 从「PINS_CHECKED:没有任何 program 编译它,指令永不求值」变成「真的编译了,而且这条指令是多余的(TS2578)」。指令被求值,正是「真进了 program」的定义。

覆盖是真的 —— 但要如实说清它数了什么

check:type-check-coverage 仍然通过,且它的 testFiles 计数没有变(前后皆 556)。这不是遗漏,是该门禁的设计:它只统计 PRIMARY tsconfig.json 的 include 根(src)之下的测试文件,因为 exclude 只能从 include 选中的集合里减,scripts/ 从来就没被选中过。所以本 PR 不能声称「TESTS_COVERED 多数了 29 个文件」。

真正变了的是两件事,都已实测:

  1. 29 个 scripts 测试文件 + 约 48 个生成器/门禁脚本,第一次进入一个被脚本指名的 tsc program(RV-1、RV-3);
  2. PINS_CHECKED 的覆盖判定scripts/**/*.test.ts 从「无 program 覆盖的幽灵 pin 候选」变成「被真实编译」(RV-5)。

其余修复

  • build-schemas.tspropbuild-schemas.ts 检查 (c) 的「墓碑已满 2 个 major」证明仍用叶名匹配 —— 无关簇的登记可以替一次退休提前起算 #5898 移除 leaf-name 匹配后留下的死局部;AUTHORABLE_SURFACE_DESCRIPTION 导入与 MANIFEST_DESCRIPTION 别名是 spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837 把描述移进 sharded-artifacts.ts 后的残留。
  • build-openapi.tsimport * as Data 绑定后从未读取。已实测:TS import elision 本就在 tsx 求值前丢弃未使用的 namespace import,所以删的是名字不是副作用 —— json-schema/openapi.json 前后逐字节相同(已 diff 验证)。若它曾是 load-bearing,正确写法是 import '../src/data';,那是另一条语句。
  • check-liveness.mtsreport 是刻意的 any,导致 v.* 全部退化为 any —— 这正是 stale 工作列表被隐式 any 迭代、而它的邻居们各自手写 : string 的由来。在边界处标注一次生产者自己的 VerificationReport,四处读取一起变真。
  • authorable-defaults.test.ts:两个 as const fixture 造出 readonly 元组,根本不是 Iterable,即这两条断言从来没类型检查过。改标注为 collector 自己的参数类型。
  • build-docs.tsgeneratedFiles 是死累加器(唯一引用是它自己的声明);_zodFile 下划线化而非删除,以把改动压到渲染函数的一行(当时 fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226) #6377 在该面上,现已落地并已 rebase 通过)。
  • scripts/check-regen-pending.d.mts(新增):.mjs 保持 .mjspre-commitcheck:merge-driver 用裸 node 调它)。声明的动机是具体的 —— 无类型时谓词退化为 anyif (distIsStale)(漏掉调用,正是这个 guard 要防的错误)会类型检查通过。
  • check-generated.ts:新 check:* 脚本必须在 check:generated 台账里归类(该门禁在跑测试时红了,如实分类)。归入 NO_GENERATOR 而非 check:test-typecheck 旁边的 GATED —— 差别就是本 PR 的设计:那条比对签入产物、有生成器和方向棘轮;这条两者皆无,没有可重新生成的文件,也没有能让它变绿的 --fix

变更集:skip-changeset

scripts/tsconfig*.json 都不在 packages/specfiles 白名单里。按提示实测而非假设

  • gen:schema A/B(我的 build-schemas.ts vs origin/main 的),1623 个 schema 的 json-schema/ 树哈希逐字节相同 —— 177f4a81ab3a... 两侧一致;
  • 三个签入投影(json-schema.manifest/authorable-surface/authorable-defaults/)跑完 gen:schemagit status 干净;
  • json-schema/openapi.json 前后 diff 为空。

发布物零变化,故取 skip-changeset

验证

pnpm --filter @objectstack/spec typecheck     # exit 0(tsc --noEmit && check:scripts-typecheck && check:test-typecheck)
pnpm --filter @objectstack/spec test          # 338 files / 8644 tests passed
node scripts/check-type-check-coverage.mjs    # OK, exit 0
node scripts/check-nul-bytes.mjs              # OK, 6048 files, no raw control bytes
pnpm exec tsx scripts/check-generated.ts --reconcile-only   # 19 check: + 13 gen:, all classified
pnpm --filter @objectstack/spec check:liveness              # exit 0
pnpm exec eslint <改动的 7 个源文件>                          # exit 0

Generated by Claude Code

…son,修掉全部 43 条错误

`packages/spec/tsconfig.json` 的 include 是 `["src/**/*"]`,#5286 的
`tsconfig.test.json` 同样止于 `src`。于是 `scripts/**` 不在本包任何 tsconfig 的
program 里 —— 不是「被 exclude 排除」,而是压根没被 include 选进来。29 个
`scripts/**/*.test.ts` 由 vitest 真的在跑,另有约 48 个文件就是 `gen:schema` /
`gen:openapi` / `check:liveness` / `check:strictness-ledger` 等门禁本体;tsx 只转译。

新增 `tsconfig.scripts.json`(strictness 全部继承,未放松任何一项),由
`check:scripts-typecheck` 指名,并接入 `typecheck` 脚本链。合并后实测 43 条错误,
本 PR 全部修完,`scripts/**` 以零台账条目进入 —— 因此 plain tsc 比 shrink-only
台账更严:台账允许加一行来增长,这里一条也不允许。

其中 `build-schemas.ts` 的 13 条 `Property 'keys'/'rev' does not exist on type
'never'` 是本单最有价值的产出:`gitResolvedAnchor` 是模块级 `let ... = null`,
唯一赋值在 `resolveSurfaceBase()` 函数体内,而 TypeScript 的控制流分析不跟踪函数体
内的赋值,于是每个顶层读取处它仍被收窄为 `null` —— `if (gitResolvedAnchor)` 的整个
块体类型为 `never`,即 #5235/#5358/#5370/#5847 那约 100 行 in-tree anchor 写入逻辑
从未被类型检查过。改为经返回值 `SurfaceBaseResolution.gitAnchor` 传出,赋值回到调用方
自己的控制流里。运行时行为不变。

Fixes #5475

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

vercel Bot commented Aug 7, 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 7, 2026 4:48pm

Request Review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

@github-actions github-actions Bot added the size/m label Aug 7, 2026
@os-zhuang os-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 7, 2026 — with Claude
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file tests tooling labels Aug 7, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 7, 2026 17:12
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 7, 2026

Copy link
Copy Markdown
Contributor

PM 验收:ACCEPT — 已 ready + auto-merge。唯一一处红不是本 PR 的,详见末节。

CI:32 个 check,除 Validate Package Dependencies 外全部 success/skipped。ESLint success、TypeScript Type Check success(17:03:47)、Test Core 1..3/3 + 聚合 success、Check Changeset success(17:02:07)、Build Core / Temporal / Dogfood ×4 / Spec property liveness / Console Pin Freshness 均 success。名单在场守卫已过。

你纠正了我的判断,而且是往更保守的方向

我在派发令里写「若是真潜伏 bug,这是本单最大产出」,并把它设成期待。你的结论是:不是潜伏 bug,是类型谎言 —— 「correct code that was never verifiable」。

支撑这个结论的是一条落空的预测,你如实报了:你预期那 ~100 行脱离 never 后会浮出 1–2 条新错误,实测零条。正因为零,诚实的定性才是「一直正确、只是从来无法被验证」,而不是「藏了个 bug」。把一个漂亮的结论主动降级成一个朴素的结论,比反过来难得多。

RV-3 是我今天见过最锋利的一条验证

同一行 const probe: number = anchor.rev:

  • 改后TS2322: Type 'string' is not assignable to type 'number'
  • 改前TS2339: Property 'rev' does not exist on type 'never'

也就是说,那个区域里一条真实的类型错误此前会被吞掉,因为 never 可赋给任何类型。这不是「没检查」的抽象论证,是「检查器在那里会说错话」的实物证据。

RV-1c(把同一个植入错误放在 origin/main 的完整 typecheck 链下 → exit 0 绿)正是我要求的「演示覆盖是真的」:⛔ 断言不算,你给的是缺陷本身的现场。

RV-5 也很细:scripts 测试文件里的 @ts-expect-error 改前是 PINS_CHECKED 红(幽灵 pin,从未被求值),改后是覆盖绿但真的被编译,以 TS2578 Unused @ts-expect-error directive 现身 —— 这个发现是换了门禁,不是消失了。分清「问题没了」和「问题换了地方报」,是这条值钱的原因。

新覆盖当天就抓到了当天的漂移

readSurfaceKeysAtRev 手抄了 ShardArrayField 的一个更窄版本 'keys' | 'schemas',而 #6360(今天落地的 #4666)把它扩到了含 'defaults',并且已经有调用点在传。运行时无害、对一切不可见 —— 改用导出的联合即修。这道门上线第一天就抓到一次几小时前引入的漂移,比任何论证都能说明它值。

两个方向性判断,理由都是测出来的后果而非偏好

选独立 sibling config 而非并入 tsconfig.test.json:

  1. allowImportingTsExtensions 只有这里需要,并进去会允许 src/**/*.test.tsimport './x.ts',而发射用的 build config 仍然拒绝它 —— 即放宽了一个本不该放宽的面;
  2. test-typecheck-debt.json每包一份的精确台账,其 79 条 src/** 条目在第二个 project 下会全部报「graduated」。

43 条全修、零台账条目,并给出了比我更好的理由:plain tsc 比 shrink-only 台账更严,因为台账允许「加一行」来增长,这里一条也不允许。

一处主动的不邀功,我要点名

check:type-check-coverageTESTS_COVERED 仍然没有把 29 个 scripts 测试文件算进去(前后都是 556,对着确切分支基线量过)。这是设计使然 —— 它只统计主 tsconfig.json include 根之下的文件,而 exclude 只能从 include 选中的集合里减,scripts/ 从来没被选中过。

你在 PR 正文里明说了这一点,而不是宣称计数涨了。 我在派发令里要的是「说清它现在数到了什么以前数不到的」—— 诚实的答案是「没有;真正变的是真实编译 + PINS_CHECKED 覆盖」。答一个不讨好的真话,比答一个讨好的假话有用。

首跑那 6 条红(check-generated-ledger.test.ts 报新脚本未分类)也处理对了:归入 NO_GENERATOR 而非 GATED,理由是它无产物无台账、--fix 不可能让它变绿 —— 与 check:test-typecheck 的棘轮性质不同。

⚠️ 唯一的红:Validate Package Dependencies,主干级,与本 PR 无关

OSV 新公告 GHSA-55q2-fjhq-7xh7,dompurify@3.4.12(经 mermaid 传递引入),修复版 3.4.13。本 PR 不动任何依赖、lockfile、workspace 或 osv-scanner.toml;该 job 今天早些时候在本车道其它 PR 上还是绿的(#6258#6342)。你已另立 #6407,并记下两个「付过学费」的坑:override 只在 pnpm-workspace.yaml 生效;⛔ 上界不可写成排他的 fixed version。按 osv-scanner.toml 自己的抬头,有修复版就该走 override 而非 exemption —— 判断正确。

已挂 auto-merge:dompurify 一 bump、该 check 一转绿,本 PR 自动落地,无需人工再推。⛔ 本席按维护者暂停指示不做这个 bump —— pnpm-lock.yaml 不在本车道文件面,且它是全仓共享依赖,交接期单方面动它对其他在飞的人不负责。已向维护者报备。


Generated by Claude Code

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 skip-changeset PR has no user-facing published change; bypasses the changeset gate tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/spec/scripts/** 在任何 tsconfig 的 include 之外:9 个 vitest 测试文件 + 全部构建/gate 脚本无类型检查覆盖

2 participants