Skip to content

fix(client): 让 client 测试层真的进 tsc,那处 @ts-expect-error 不再是幽灵检查 - #5546

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-5449-client-ghost-pin
Aug 5, 2026
Merged

fix(client): 让 client 测试层真的进 tsc,那处 @ts-expect-error 不再是幽灵检查#5546
baozhoutao merged 1 commit into
mainfrom
claude/issue-5449-client-ghost-pin

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5449

#5286 / PR #5478 定下的机制跟进,不发明第二套。

前提复核(先证后改)

在最新 origin/main(9894a723e)上两态实测:

  • 保留 packages/client/src/client.test.ts:1283// @ts-expect-error — empty string rejected at runtimepnpm --filter @objectstack/client typecheck 退出码 0
  • 删掉该指令行 → 同一条命令仍退出码 0

两态皆绿 = 该指令从未被求值。前提成立。

反向验证方向:与派发模板相反,如实申报

派发单预判「修好后删掉指令行应红 TS2578」。这个方向在本单不成立,而且是本单的核心发现:project(environmentId: string) 接受 '' —— 那就是一个合法的 string —— 所以该指令从来没有东西可压制。它第一次被编译时报的就是

src/client.test.ts(1283,9): error TS2578: Unused '@ts-expect-error' directive.

也就是说,正确的修法就是删掉这行指令(它自己的注释已经说明了这个测试真正证明的事:空 id 是在运行时被下面那道 guard 拒绝的)。因此反向验证是镜像的:把指令行加回去应该红。先判后跑,实跑结果:

✓ check:test-typecheck --self-test — 8 semantic case(s) + the parser hold.
check:test-typecheck: 1 problem(s)

  • src/client.test.ts: 1 type error(s) in a file the ledger does not cover. Fix them — this file is
    inside the checked zone, which is the point of tsconfig.test.json. (A deleted `@ts-expect-error`
    shows up exactly here, as TS2578/TS2694.)
ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL @objectstack/client typecheck: Exit status 1

修改前同样的「加回去」是绿的,修改后是红的 —— 这正是「该文件现在真的在一个 tsc program 里」的证据。

做了什么

  • packages/client/tsconfig.test.json(新增):build config 的 sibling。build 的 exclude 保留不动(ci.yml 有「编译产物不得含测试文件」的闸门,已实跑 pnpm --filter @objectstack/client build 确认 dist/ 无测试文件)。差异只在模块语义:module: esnextmoduleResolution: bundler、ES2022 lib,匹配 vitest 的真实执行方式;外加把 rootDir 放宽到工作区根 —— 有四个测试文件深引用兄弟包的 route ledger(../../runtime/src/route-ledger 等),继承来的 rootDir: ./src 会为此报 5 条 TS6059,那是检查本身配错,不是测试的问题(它们正是该包 TEST_DEBT 陈旧条目的大头)。严格性 flags 一律继承,一条都没放宽。

  • 13 条错误,修掉 8 条真实缺陷:两个未使用的 import、一个未使用的形参、两处对可选 routes 的可能为 undefined 的读取(改成可选读 + 断言,没有用 !?? {} 兜底,map 缺失照样测试失败)、一处 unknown 载荷改用 toMatchObject 结构断言(不加 cast)、一个字段 schema 根本没有的 reference_to key(那个 lookup 此前没有声明任何目标),以及那处幽灵 pin。重新拼对 reference 之后又暴露出 1 条同族错误。

  • 剩下 6 条进 EXACT ratchet 台账:三个文件、一个根因 —— objectql 的 registerObject 参数标成了 schema 的 OUTPUT 类型(z.infer)而非 INPUT(已另立 objectql registerObject 的参数标成 schema 的 OUTPUT 类型(z.infer),合法的作者态字面量一律 TS2740 —— 这是 spec/client 两份测试层欠账的同一根因 #5543)。packages/client/test-typecheck-debt.json 逐文件精确记账、只减不增;objectql registerObject 的参数标成 schema 的 OUTPUT 类型(z.infer),合法的作者态字面量一律 TS2740 —— 这是 spec/client 两份测试层欠账的同一根因 #5543 修好那天台账会自动变红要求毕业,而不是烂在那里。pin 所在文件 client.test.ts 不在台账里,必须零错误 —— 上面的反向验证就是靠这一点成立的。

  • scripts/check-test-typecheck.mts:从 packages/spec/scripts/ 提升为共享脚本,用 --package 指定包。派发单明确要求「不要发明第二套」,而把 300 行脚本复制一份进 client 恰恰就是第二套。spec 侧只改 package.json 的两行脚本;其台账文件一字未动,生成的 _comment 模板与已存字节完全一致(未来 --update 不会产生伪 diff),实跑 pnpm --filter @objectstack/spec check:test-typecheck 仍为 79 文件 / 691 错误。

  • 两处台账毕业(闸门本身会强制):@objectstack/clientTEST_DEBT 删除(陈旧条目记 15 文件 / 19 错误,其中 5 条是上面说的 rootDir 误配),PHANTOM_PIN_DEBTfix(spec): 让 spec 测试层真的进 tsc,@ts-expect-error 退役 pin 不再是幽灵检查 #5478 写明「留给 幽灵 @ts-expect-error 不止 spec:@objectstack/client 也有 1 处落在 tsconfig 排除区内(全仓横扫结果) #5449」的 seed 条目删除。

  • AGENTS.md 里那一段随之更新:台账不再是 spec 专有,闸门是一个共享脚本,接入方式是接线而不是复制。

验证

pnpm --filter @objectstack/client typecheck
  ✓ check:test-typecheck --self-test — 8 semantic case(s) + the parser hold.
  check:test-typecheck: OK — @objectstack/client's test layer compiles under
  packages/client/tsconfig.test.json; 3 file(s) / 6 error(s) held in
  test-typecheck-debt.json (shrink-only, #5286).

pnpm --filter @objectstack/client test
  Test Files  17 passed (17)
       Tests  222 passed (222)

pnpm --filter @objectstack/spec check:test-typecheck
  check:test-typecheck: OK — @objectstack/spec's test layer compiles under
  packages/spec/tsconfig.test.json; 79 file(s) / 691 error(s) held in
  test-typecheck-debt.json

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, 1 exempt.
    test layer: 19 package(s) still exclude their own tests   ← 修改前为 20

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

pnpm --filter @objectstack/client build   → dist/ 无测试文件
eslint --no-inline-config <改动文件>       → 无输出

未做 / 界外

Changeset

纯测试层 + 工装,不发布任何行为变更,按仓里同类先例(PR #5478 同样未加 changeset)走 pr-automation.yml 首选的第 2 条路径:请打 skip-changeset 标签,而不是塞一个空 changeset(空 changeset 是 changesets/action 的真实输入,全空会静默绿着停掉发布 —— #4898)。


Generated by Claude Code

…stops being a phantom check

`packages/client/tsconfig.json` excludes `**/*.test.ts` and the package's
`typecheck` script was a bare `tsc --noEmit` reading that same config, so no gate
anywhere read a client test file with a type checker. The one `@ts-expect-error`
in the package — `client.test.ts:1283` — evaluated never: on `origin/main`,
deleting the directive line left `pnpm --filter @objectstack/client typecheck`
just as green as leaving it there.

Compiled for the first time, that directive reports TS2578 "unused". It never
had anything to suppress: `project(environmentId: string)` accepts `''`, which
is a perfectly good `string`, and the directive's own comment already said what
the test proves — the empty id is rejected at RUNTIME. So the repair is to
delete the directive, not to keep it: the reverse verification is the mirror of
the usual one, and RESTORING the line is what now goes red.

- `packages/client/tsconfig.test.json`: a sibling of the build config (which
  keeps its exclusion — ci.yml gates that no test file reaches the published
  artifact) with vitest's module semantics (`module: esnext`,
  `moduleResolution: bundler`, ES2022 lib) and `rootDir` widened to the
  workspace root, since four test files deep-import sibling packages' route
  ledgers. Strictness flags are inherited, untouched.
- 13 errors surfaced; eight were the tests' own and are fixed here — two unused
  imports, an unused parameter, two possibly-undefined reads on an optional
  `routes` map, an `unknown` payload now asserted with `toMatchObject` instead
  of cast, a `reference_to` key the field schema never had (the lookup declared
  no target at all), and the phantom pin. Re-spelling that key uncovered one
  more of the remaining kind; the six that stay are one producer-side defect
  (#5543) held per file in `test-typecheck-debt.json`, EXACT and shrink-only.
- `scripts/check-test-typecheck.mts`: PROMOTED from `packages/spec/scripts/`,
  parameterized with `--package`, so client onboards by wiring its `typecheck`
  script rather than by copying 300 lines. spec's ledger and its 79 files / 691
  errors are unchanged, and the generated `_comment` is byte-identical.
- Both graduations the gates force: `@objectstack/client` leaves TEST_DEBT (its
  stale entry measured 15 files / 19 errors, five of them the inherited-rootDir
  TS6059 that were the check's own misconfiguration) and its
  `PHANTOM_PIN_DEBT` seed — the entry #5478 left addressed to this issue — is
  deleted.

Fixes #5449

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
@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 4:54pm

Request Review

@baozhoutao baozhoutao 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 documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests size/m labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/client, @objectstack/spec.

113 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 packages/client, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/client, @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/client, @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 packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/client)
  • 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/client, @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/kernel/realtime-protocol.mdx (via @objectstack/client)
  • 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/client, @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/client, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/client, @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/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.

@baozhoutao baozhoutao added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed skip-changeset PR has no user-facing published change; bypasses the changeset gate labels Aug 5, 2026 — with Claude
@baozhoutao
baozhoutao marked this pull request as ready for review August 5, 2026 16:57
@baozhoutao
baozhoutao enabled auto-merge August 5, 2026 16:57
@baozhoutao baozhoutao added the tooling label Aug 5, 2026 — with Claude
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit d7e07c8 Aug 5, 2026
36 of 38 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5449-client-ghost-pin branch August 5, 2026 17:17
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 documentation Improvements or additions to documentation size/m 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.

幽灵 @ts-expect-error 不止 spec:@objectstack/client 也有 1 处落在 tsconfig 排除区内(全仓横扫结果)

2 participants