Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

spec-symbol-parity.test.ts 的「编译期」断言从未被编译 —— 整个 tripwire 是哑的 #3181

Closed

Description

@os-zhuang

注:本 issue 正文里的泛型都写成 Assert< Equal< … > >(尖括号后带空格)。这是因为 GitHub 会把 <Equal 这类序列当成 HTML 标签吞掉;加空格后仍是合法 TypeScript,可直接复制使用。

结论

packages/app-shell/src/__tests__/spec-symbol-parity.test.ts 里所有 Assert< Equal< … > > 形式的断言没有任何 gate 会编译它们。它们既不会在 tsc 里报错,也不会在 vitest 里失败(类型在运行时被擦除)。这个文件的类型部分目前等价于注释。

根因

packages/app-shell/tsconfig.json:

"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]

测试文件被 exclude 掉了,而 CI 的类型 gate(.github/workflows/ci.yml → pnpm type-check → turbo → 每包 tsc --noEmit)只跑包级 tsconfig。仓库里绝大多数包的 tsconfig 都有同样的 exclude,所以这不是 app-shell 独有。

复现(已实测)

在 spec-symbol-parity.test.ts 末尾追加一个必然为假的断言:

type _ProbeShouldFail = Assert< Equal< 1, 2 > >;

然后:

$ cd packages/app-shell && pnpm type-check
> tsc --noEmit
$ echo $?
0

退出码 0。连 Assert< Equal< 1, 2 > > 都不报错,说明文件根本没进编译单元。

反过来,用一个把测试文件包含进来的临时 tsconfig(include: ["src"],去掉 test 的 exclude)编译,断言确实会生效并报 TS2344。也就是说断言本身写得没错,只是没人跑。

为什么这条值得单独修

这个文件的自己的文件头就写着这一类失败:

…objectui#3009 —— where a guard file's own header falsely claimed its checks were "the real enforcement" for the entire interval during which nothing compiled them.

以及 scripts/check-spec-symbol-derivation.mjs 头部的:

This guard lands the rule AS THE CHECK, not as an AGENTS.md paragraph. A prose-only rule is precisely the "declared ≠ enforced" landmine…

所以这是同一个 landmine 又踩了一次:一份声称是编译期 tripwire 的文件,实际处于「declared ≠ enforced」状态。对 AI 主导的开发尤其贵 —— 下一个 agent 读到这些断言会当成已被强制的地面真值,并在其上继续构建。

具体已经发生的后果见 objectstack-ai/objectstack#4562:该 issue 的前提是「parity 断言会在 spec 采纳 required 那天失败」,而实际上它永远不会失败,因为它不被编译。

已知的修复代价(不是一行改动)

直接把 **/*.test.ts 从 exclude 拿掉会立刻暴露大量既有错误。实测在 app-shell 上用包含测试的 tsconfig 编译:

189 个 error TS

例如:

src/console/ai/__tests__/AiChatPage.hydration.test.ts(25,16): error TS2339: Property 'toolInvocations' does not exist on type 'ChatMessage'.
src/console/ai/__tests__/deriveBoundPackageId.test.ts(20,33): error TS2345: Argument of type 'ChatMessage[]' is not assignable to parameter of type 'readonly PackageBearingMessage[]'.
src/context/__tests__/FavoritesProvider.test.tsx(165,7): error TS2578: Unused '@ts-expect-error' directive.
src/hooks/__tests__/useConsoleActionRuntime.test.tsx(464,25): error TS2339: Property 'onModal' does not exist on type '{ … }'.
src/utils/decisionOutputParams.test.ts(130,55): error TS2322: Type 'string' is not assignable to type '"user" | "text" | "position" | "team" | "department" | undefined'.

(注:spec-symbol-parity.test.ts 本身是干净的 —— 0 error。)

可选方案

  1. 最小、够用:给「以类型断言为唯一价值」的文件单独建一个 tsconfig.typetests.json(只 include 这几个 parity/tripwire 文件),在 CI 里加一步 tsc --noEmit -p。代价小,立刻把 tripwire 变成真 gate。
  2. 彻底:全仓测试文件纳入 type-check,按包分批消化那 189 个错误。收益更大但是一个独立的大工程,建议按包拆子 issue,并配一个类似 check-type-check-coverage.mjs 的 shrink-only DEBT 台账,避免边修边漏。
  3. 顺带考虑:让 check-type-check-coverage.mjs 也校验「测试文件是否被某个 tsconfig 覆盖」,否则方案 1 建的那个配置将来同样会悄悄失效。

倾向方案 1 先落地(把 tripwire 救活),方案 2 作为后续。


发现于 objectstack-ai/objectstack#4562 的实施过程中,属于该 issue 范围外,故单独记录,未指派。

🤖 Generated with Claude Code

Activity

  1. self-assigned this
    on Aug 2, 2026
  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 3 轮(按新升级判据:恢复不变量类——「gate 必须真的编译它声称检查的东西」——直接派发,不再请示;维护者持否决权)
    分支:claude/issue-3181-typetests-gate
    Worktree:objectui-issue-3181
    如另有会话已在做本 issue,以更早的认领评论为准,本认领让行。


    Generated by Claude Code

  3. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    PM 验收:ACCEPT → PR #3187(已转正并启用 merge-when-ready,入队自动合并)。

    最小路线落地且门禁自身也被钉住:专用 tsconfig.typetests.json(显式列文件不,用 glob,避免把 53/189 个既有测试类型错误扫进来)、链入既有 type-check 脚本从而直接进 CI 的 Type Check job(零 workflow 改动)、check-type-check-coverage.mjs 增加配套棘轮——typetests 配置存在但没人跑/会 emit/不含测试文件,即硬失败。双层负向对照均实测:必假断言让新门禁 TURBO_EXIT=2(旧门禁同树 exit 0,复现 issue);撤掉链式调用让棘轮 exit 1。CI 实况已核验(Type Check job 日志可见新命令)。

    自此 #3185 反转的 DecisionOutputDef parity 钉子正式通电;全量修复既有测试类型错误明确不在范围内,继续由本 issue 正文与 objectstack#4118 跟踪。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions