Skip to content

fix(qa,dx): 让 dogfood 的 strict tsconfig 真正被执行 —— 修 14 条积压并接进 turbo typecheck (#4855) - #5002

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4855-dogfood-typecheck-wired
Aug 3, 2026
Merged

fix(qa,dx): 让 dogfood 的 strict tsconfig 真正被执行 —— 修 14 条积压并接进 turbo typecheck (#4855)#5002
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4855-dogfood-typecheck-wired

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4855

问题

packages/qa/dogfood/tsconfig.json 是一份认真的配置 —— strict: truemodule/moduleResolution: NodeNextinclude 覆盖 test/**/*。但 packages/qa/dogfood/package.json 的 scripts 里只有 test。根 pnpm typecheckturbo run typecheck,只跑声明了该 script 的包;三个 qa 包里(downstream-contracthttp-conformancedogfood),这是唯一没声明的一个 —— 于是这份 strict 配置从未被执行过

这就是「declared ≠ enforced」在构建配置层的形状:读代码的人(和写代码的 agent)会合理假设它在 CI 里把关,照着它的严格度写;实际没有任何一步跑它。而 dogfood 是本仓最重的行为门,它的 fixture 类型漂移此前没有任何东西会挡。

覆盖率闸门为什么没抓到

抓到了,但记成了冻结债务而不是缺口。scripts/check-type-check-coverage.mjsDEBT 账本里有:

'@objectstack/dogfood': {
  errors: 12,
  note: 'code-tier 8 (TS2322/TS2554) + 3 config-tier + 1 noise.',
},

该闸门的 COVERED 不变式接受「声明了 typecheck script」「有一条带实测错误数的 DEBT 条目」。dogfood 走的是后一条,所以闸门一路绿灯 —— 它诚实地记录了这个包没被检查,但记录不等于执行。

DEBT 是暂缓,不是豁免,而且会漂移:账本冻结于 b07d829(2026-07-31)时是 12 条,我在最新 main(7d0e7b5)上实测是 14 条。多出来的 2 条正是「冻结不能长期替代执行」的直接证据 —— 门后的东西只会继续涨。所以本 PR 走的是账本注释里写明的毕业路径:修完错误 → 加 script → 同一个 PR 里删掉 DEBT 条目(否则 RECONCILED 不变式会失败)。

实测的 14 条与修法

类别 数量 修法
NodeNext 缺扩展名 TS2307(+1 条级联 TS7006) 3 两处 ./field-zoo.matrix.js
flow fixture 的 type 没收窄 TS2322 8 标注 : Flow
条件展开出的 headers TS2322 2 标注 Record< string, string >
连接器 handler 少传参 TS2554 1 按声明传两个参数

1. NodeNext 缺扩展名。 field-zoo-roundtrip.dogfood.test.tsfield-zoo-value-shape.test.tsfrom './field-zoo.matrix'。文件确实在,但 moduleResolution: NodeNext 要求 .js。这不是风格问题:包内另外 33 处相对 import 全部带 .js,这两处是仅有的例外。未解析的 import 让符号退化成 any,回调参数随即报 implicit-any —— 补上扩展名同时消掉那条级联的 TS7006。

2. flow fixture 的 type 四个 fixture 里的 flow 是裸对象字面量,type: 'autolaunched' 推成 string,喂给 defineStack 时对不上字面量联合。修法不是 as const,而是按仓内既有写法(examples/app-todo/src/flows/task.flow.ts)标注:

import type { Flow } from '@objectstack/spec/automation';
export const flowTouch: Flow = { ... };

差别是实质性的:as const 只让报错闭嘴,: Flow整份 fixture 被 spec 的真实契约检查。反向验证那一步(见下)正是靠这个标注才判红的。

3. headers。 token ? { Authorization } : {} 在匿名分支上推出 Authorization?: undefined,展开进 headersHeadersInit(Record< string, string >)拒收。给该常量显式标注即可。

4. 连接器 handler —— 这条值得单独说。 showcase-mcp-self-connection.dogfood.test.tshandler!({}),但契约声明的是两个参数:

handlers: Record<
    string,
    (input: Record< string, unknown >, ctx: unknown) => Promise< Record< string, unknown > >
>;

引擎侧也始终按两参数派发(service-automation/src/builtin/connector-nodes.ts:90:await handler((cfg.input ?? {}) as Record< string, unknown >, handlerCtx)),connector-mcp 自己的测试同样写 handlers.create_issue({...}, {})。这条调用一直「能跑」,只是因为 MCP 这个 handler 的实现恰好忽略 ctx —— 契约上它是错的,是一处被这道没人跑的门盖住的真实契约违反(运行时不炸,但它在冒充引擎的调用而少传了引擎必给的东西)。

按契约优先修的是调用方:改成 handler!({}, {})。反方向(把 ctx 改成可选)会为了迁就一个测试而放松 connector-mcp 的公开契约,是错误方向,没有采纳。

接进强制执行

"scripts": {
  "typecheck": "tsc --noEmit",
  "test": "vitest run"
}

外加删除 check-type-check-coverage.mjs 里的 DEBT 条目。闸门自测与实跑:

✓ check:type-check-coverage --self-test — 18 semantic case(s) hold.
check-type-check-coverage: OK — 61/77 workspace packages type-checked (plus the root),
  16 in the DEBT ledger (376 frozen raw errors), 1 exempt.

覆盖率 60/77 → 61/77,DEBT 17 → 16 个包。turbo.jsontypecheck 任务与 lint.yml 的调用都已存在(闸门的 RUNNABLE 不变式在管),无需改动 —— 缺的只是这个包的 script。

反向验证

flow-touch-fixture.ts 里把 type 改成 'not_a_real_flow_type',走真实 CI 入口:

$ npx turbo run typecheck --filter=@objectstack/dogfood
@objectstack/dogfood:typecheck: test/fixtures/flow-touch-fixture.ts(49,3): error TS2322:
  Type '"not_a_real_flow_type"' is not assignable to type
  '"api" | "schedule" | "autolaunched" | "screen" | "record_change"'.
 Tasks:    60 successful, 61 total
Failed:    @objectstack/dogfood#typecheck

改回后:

@objectstack/dogfood:typecheck: cache hit, replaying logs 238d29ba02f83710
 Tasks:    61 successful, 61 total

238d29ba02f83710 与注入前那次绿跑是同一个 turbo hash —— 证明状态被精确还原,绿不是残留缓存蒙对的。

测试

$ pnpm --filter @objectstack/dogfood test
 Test Files  83 passed | 1 skipped (84)
      Tests  481 passed | 3 skipped (484)

零行为改动:所有修改要么是类型标注,要么是补模块扩展名,要么是把调用补齐到既有契约(ctx 在这个 handler 的实现里本就未被读取)。ESLint 干净。

改动范围

限于 packages/qa/dogfood,加两个接线文件:

  • scripts/check-type-check-coverage.mjs —— 根目录共享文件,只删掉 dogfood 那一条 DEBT 条目(账本注释里写明的毕业动作;不删则 RECONCILED 不变式失败)。未改动闸门的任何语义。
  • .changeset/dogfood-typecheck-wired.md —— 内部 qa 包 + dev script,按 per-package-typecheck-coverage.md / ci-performance-optimization.md 的先例用空 frontmatter,不发版。

turbo.json.github/workflows/lint.yml 未改动


Generated by Claude Code

…pecheck (#4855)

`packages/qa/dogfood/tsconfig.json` 是一份认真的配置(strict、NodeNext、
include 覆盖 test/**),但 package.json 的 scripts 里只有 `test`。根
`pnpm typecheck` 是 `turbo run typecheck`,只跑声明了该 script 的包 ——
三个 qa 包里这是唯一没声明的一个,于是这份配置从未被执行过。

#4311 的覆盖率闸门看见了这个洞,但把它记成 DEBT(errors: 12)冻结下来。
DEBT 是「暂缓」不是「豁免」:在最新 main 上实测是 14 条,比冻结时多了 2 条
—— 漂移本身就是不能长期用冻结替代执行的证据。

14 条全部修掉:

- NodeNext 缺扩展名(2 条 TS2307 + 1 条级联 TS7006):两处 `./field-zoo.matrix`
  补 `.js`。包内另外 33 处相对 import 全都带 `.js`,这两处是仅有的例外。
- flow fixture 的 `type` 没收窄(8 条 TS2322):四个 fixture 的 flow 是裸对象
  字面量。按 examples/app-todo 的既有写法标注 `: Flow`,而不是 `as const` ——
  整份 fixture 因此被 spec 的真实契约检查,而不只是让报错闭嘴。
- 条件展开出的 headers(2 条 TS2322):给 auth 常量标注 Record<string, string>。
- 连接器 handler 少传参(1 条 TS2554):`handler!({})` 改为 `handler!({}, {})`。
  `McpConnectorBundle.handlers` 声明的是 (input, ctx),引擎侧 connector-nodes.ts
  也始终按两参数派发;这条一直「能跑」只因这个 handler 恰好忽略 ctx。按声明
  修正调用方,没有放松任何契约。

接进执行:package.json 加 `"typecheck": "tsc --noEmit"`,并按闸门的 RECONCILED
不变式在同一个 PR 里删掉 DEBT 条目。覆盖率 60/77 → 61/77,DEBT 17 → 16 个包。

反向验证:塞一个非法 flow `type`,`turbo run typecheck` 判红并精确指出该行;
移回后判绿(命中修复前那次绿跑的同一个 turbo hash)。
`pnpm --filter @objectstack/dogfood test` 481 passed / 3 skipped,行为未变。

Fixes #4855

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

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

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/permissions/authorization.mdx (via packages/qa/dogfood)
  • content/docs/permissions/delegated-administration.mdx (via packages/qa/dogfood)

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.

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/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/qa/dogfood 的 tsconfig 没人跑 —— 14 个类型错误积在一个「声明了但未执行」的门后面

2 participants