Skip to content

Commit 5db0efe

Browse files
baozhoutaoclaude
andauthored
ci(dx): DEBT/TEST_DEBT 台账数字改为每次重测的真棘轮 —— 实测 > 记录即红 (#5278) (#5827)
* ci(dx): re-measure the type-check DEBT/TEST_DEBT ledger on every run (#5278) The coverage gate asserted only that a ledgered package had *some* positive error count written down -- `errors: 28` and `errors: 1` were equally acceptable to it, because the ledger was never re-measured. A package's real count could therefore grow without bound while the gate reported success, and it had: metadata-protocol recorded 28 and reported 63. `--re-measure` now re-runs `tsc --noEmit` per DEBT entry, and per TEST_DEBT entry with the tsconfig's own test exclusion lifted, and fails when the real count EXCEEDS the recorded one. Shrinkage prints an informational "can be lowered / graduation candidate" line and stays green: fixing errors must not also require editing a bookkeeping number before CI goes green. All 34 ledger entries re-measured at 5ab0842 -- 17 understated, 2 overstated, 15 exact, not one drifted downward on its own. Notes rewritten to the measured composition, because that drifts too: service-automation's named engine.test.ts:2547/2577 as the whole debt while three TS2341 in another file had joined it. Wired into lint.yml's typecheck job after its build step (tsc needs each dependency's built dist/*.d.ts); the cheap structural half stays where it is. Measured cost of the re-measure pass: ~4 min. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW * ci(dx): 补两处台账 note 与死代码清理 —— 根条目会随 showcase 动,measureDebt 的 || 分支不可达 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW * ci(dx): rest 的 TEST_DEBT 重测到 143 —— 记录 merge-commit 竞态,那正是闸门起作用的证据 CI 在 b8433ca 上判红:@objectstack/rest 记 136,实测 140。原因不是量错了 —— `pull_request` 运行编译的是「分支 merge 进当前 main」的树,而 sweep 之后 main 又落地了三个动 packages/rest 的 PR(#5808 / #5821 / #5806)。合并 main 后重量 得 143,tests 56 -> 58,其余 33 条纹丝不动。 这个竞态是引导期的一次性成本,不是常态:本不变式上了 main 之后,引入错误的那个 PR 自己会红 —— 这正是它的目的。写进 MEASURED 的文档块和 rest 的 note,下一个做 全量重测的人不必再自己发现一遍。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d93080d commit 5db0efe

5 files changed

Lines changed: 579 additions & 49 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
---
3+
4+
ci(dx): `scripts/check-type-check-coverage.mjs` 的 DEBT / TEST_DEBT 台账数字现在会被**重测**——实测 > 记录即红(#5278)。Dev scripts / CI only;releases nothing。
5+
6+
原来的闸门只断言「有一条台账、数字为正」:
7+
8+
```js
9+
if (!entry || typeof entry.errors !== 'number' || entry.errors <= 0) { ... }
10+
```
11+
12+
也就是说 `errors: 28` 和 `errors: 1` 对它完全等价,台账**从不复测**。包这一层对新增 debt 是关着的,错误**条数**这一层不是——一个新测试文件带进来的错误没有任何一道闸会看见。AGENTS.md 写着「DEBT is frozen debt, not a permission slip. Every entry below was measured」,而一个已经悄悄漂了 2.25 倍的数字不再描述它声称冻结的那笔债:记 28 的条目读起来像「快毕业了」,实际成本是它的两倍多。
13+
14+
本次在 `5ab08428` 上把 34 条台账**全部重测**,漂移比 issue 报的更普遍:
15+
16+
| | 记录 | 实测 |
17+
|:---|---:|---:|
18+
| `@objectstack/metadata-protocol` | 28 | 63 |
19+
| `@objectstack/spec-monorepo`(仓库根) | 50 | 80 |
20+
| `@objectstack/objectql`(TEST_DEBT) | 219 | 333 |
21+
| `@objectstack/plugin-approvals`(TEST_DEBT) | 467 | 547 |
22+
| `@objectstack/service-analytics` | 3 | 7 |
23+
| `@objectstack/service-automation` | 2 | 5 |
24+
| …… 共 17 条低估 | | |
25+
| `@objectstack/runtime`(TEST_DEBT) | 220 | 218 |
26+
| `@objectstack/driver-mongodb`(TEST_DEBT) | 44 | 43 |
27+
28+
17 条低估、2 条高估、15 条精确。**没有一条**是因为债在缩小而失真的。
29+
30+
## 新的 MEASURED 不变式
31+
32+
`--re-measure` 对每条 DEBT 跑该包自己的 `tsc --noEmit -p <pkg>/tsconfig.json`,对每条 TEST_DEBT 生成一份 `extends` 原配置、只去掉 test 排除项的临时兄弟配置再跑(临时文件在 `finally` 里删除)。判定是**不对称**的,这是本次的核心:
33+
34+
- 实测 **>** 记录 → **红**。这才是棘轮。
35+
- 实测 **<** 记录 → 打印一行 `ℹ … can be lowered`,**不红**。修错误不应该还要先改一个记账数字才能让 CI 变绿,否则台账就是在对它本该鼓励的工作收费。
36+
- 实测 **= 0** → 报告为 graduation candidate(毕业仍然是一次显式 PR:加 `typecheck` script + 删台账条目,由 COVERED / RECONCILED 两个方向共同强制)。
37+
38+
## note 的成分也要跟着重写
39+
40+
漂的不只是数字,还有 note 描述的**成分**——这是 `service-automation` 这个标本的价值所在:它记 2,note 逐字点名 `engine.test.ts:2547/2577` 的两条 TS2741 是「全部的债」,而实测 5 条里多出来的 3 条是 `nested-region-parity.test.ts` 里测试直接点号读私有字段 `engine.flows` 的 TS2341——不同文件、不同错误码、不同性质。一个「两个字面量缺字段」的 note 读起来是顺手就能毕业,实际却还夹着「测试到底该不该读私有状态」这一类判断。
41+
42+
所以每条被抬高的 note 都按实测成分重写了(错误码直方图 + 集中的文件),闸门的报错文案也直接要求这件事;确实归因不了的(本仓库 clone 是浅的,拿不到逐文件 blame)就明说「re-measured N at 5ab08428」,不编造成分。
43+
44+
另外记录两个重测才看得见的事实:`@objectstack/driver-mongodb` 净变化是 -1,但成分换掉了三分之二(老 note 归咎于缺 `types:["node"]` 的 15 条 TS2591 全没了,冒出 7 条 TS1309)——单看数字会以为什么都没发生;`@objectstack/http-conformance` 的 4 条里有 2 条报在 `node_modules` 的 `.d.ts` 上,所以这条会随 lockfile 动而不只随本包代码动,已在 note 里写明。
45+
46+
## 落点与成本
47+
48+
便宜的结构检查留在原地(只读 package.json / tsconfig.json,亚秒级,跑在 build 之前)。重测这一半需要各包依赖的 `dist/*.d.ts`,所以挂在 `lint.yml` 的 `typecheck` job 里、build 步骤**之后**——这个 job 本来就付了构建的钱。build filter 顺带扩到嵌套包组(`packages/services/*` 等):多数台账包没有 `typecheck` script,从来没进过 turbo 的任务图,它们的依赖也就不会被建。
49+
50+
实测重测本身 **~4 分钟**(34 个 project,顺序执行;并行 tsc 是拿 wall clock 换一个刚建完整个 workspace 的 job 上的 OOM 风险)。
51+
52+
## 反向验证
53+
54+
方向是先定后验的,两个方向都验了:把一条台账改到**低于**实测(`service-analytics` 7 → 4)、另一条改到**高于**实测(`service-automation` 5 → 9),同一次运行 exit=1,恰好 1 条红(前者)+ 恰好 1 条 `ℹ`(后者)——增长判红、缩小不判红、逐条独立,三件事一次落实。改动前的台账(即 origin/main 的数字)在新闸门下是 17 条红,重测后为绿。
55+
56+
self-test 新增 11 个用例:6 个钉住三个方向(涨/缩/归零)与逐条独立性,5 个钉住计数器本身——多行 elaboration 缩进行不能被重复计数(一条 TS2322 能打印 5 行),无文件前缀的全局诊断要计数,而正文里出现「error TS」字样但没有错误码的散文不能计数。

‎.github/workflows/lint.yml‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,10 @@ jobs:
482482
# failure: every package either declares `typecheck` (run by the turbo
483483
# step below) or carries a measured DEBT/EXEMPT entry in the script's
484484
# ledger, reconciled in both directions so the ledger can only shrink.
485-
# Reads package.json files only; no build, sub-second.
485+
# Reads package.json files only; no build, sub-second. The other half of
486+
# this gate — re-running tsc against each ledger number — needs the built
487+
# dist/*.d.ts and therefore lives after the build step, near the bottom of
488+
# this job ("Re-measure the type-check DEBT / TEST_DEBT ledger").
486489
- name: Check every package is type-check covered or ledgered
487490
run: pnpm check:type-check-coverage
488491

@@ -647,6 +650,42 @@ jobs:
647650
- name: Type check workspace packages
648651
run: pnpm exec turbo run typecheck --filter='./packages/*' --filter='./packages/*/*' --filter='./apps/*'
649652

653+
# The MEASURED half of the coverage gate (#5278). The cheap structural
654+
# check near the top of this job asserts that a package without a
655+
# `typecheck` script carries a DEBT/TEST_DEBT entry with a positive number
656+
# written down — and, until now, nothing more: `errors: 28` and
657+
# `errors: 1` were equally acceptable to it, because the ledger was never
658+
# re-measured. So a ledgered package's real error count could grow without
659+
# bound while the gate reported success. It had: metadata-protocol
660+
# recorded 28 and reported 63, service-analytics 3 -> 7, service-automation
661+
# 2 -> 5, and the wholesale re-measure this step ships with found 17 of the
662+
# 34 entries understated and not one overstated. A number that has drifted
663+
# 2.25x no longer describes the debt it claims to freeze.
664+
#
665+
# Asymmetric, on purpose: a count ABOVE its recorded number fails, a count
666+
# below prints an informational "can be lowered / graduation candidate"
667+
# line and stays green. Fixing errors must not also require editing a
668+
# bookkeeping number before CI will go green, or the ledger charges a toll
669+
# on exactly the work it exists to encourage.
670+
#
671+
# Here rather than beside its structural half because it runs the real
672+
# compiler over ~34 projects, and tsc resolves workspace imports through
673+
# each dependency's built `dist/*.d.ts` — so it needs the build steps
674+
# above, which this job already pays for. The build filter is widened to
675+
# the nested package groups (packages/services/*, packages/drivers/*,
676+
# packages/plugins/*, …) because most ledgered packages have no
677+
# `typecheck` script and therefore never entered the turbo task graph that
678+
# would otherwise have built their dependencies; it is a superset of what
679+
# the steps above already built, so it is cache hits plus the remainder.
680+
# Measured cost of the re-measure itself: ~4 min, sequential by design
681+
# (parallel tsc processes trade wall clock for an OOM risk on a job that
682+
# has just built the whole workspace).
683+
- name: Build the ledgered packages' dependencies
684+
run: pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*'
685+
686+
- name: Re-measure the type-check DEBT / TEST_DEBT ledger
687+
run: pnpm check:type-check-debt
688+
650689
- name: Type check example apps
651690
run: pnpm --filter './examples/*' run typecheck
652691

‎AGENTS.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ workspace package declares a `typecheck` script or carries a measured DEBT/EXEMP
2929
in `scripts/check-type-check-coverage.mjs`. New packages must arrive covered; a package
3030
that graduates deletes its ledger entry in the same PR.
3131

32+
The ledger numbers are ratcheted too (`pnpm check:type-check-debt`, run in the same CI
33+
job after its build step): every DEBT/TEST_DEBT count is re-run through `tsc --noEmit`,
34+
and a count ABOVE its recorded number fails. Below is only an informational
35+
"can be lowered" line — improvements never owe CI a bookkeeping edit. Before #5278 the
36+
gate asserted only that *some* positive number was written down, so the real counts had
37+
drifted up to 2.25x while it reported success. When a re-measure makes you raise an
38+
entry, rewrite its `note` as well: the composition drifts too, and a note that still
39+
names only the old errors reads as "nearly graduated" to the next author.
40+
3241
**Do not `exclude` `*.test.ts` / `*.spec.ts` from a package's `tsconfig.json`.** `tsc
3342
--noEmit` reads that config, so an exclusion there hides the tests from the check the
3443
`typecheck` script advertises — a green gate over source nothing read, which is the

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"check:workflow-status-functions": "node scripts/check-workflow-status-functions.mjs --self-test && node scripts/check-workflow-status-functions.mjs",
5959
"check:published-files": "node scripts/check-published-files.mjs --self-test && node scripts/check-published-files.mjs",
6060
"check:type-check-coverage": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs",
61+
"check:type-check-debt": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs --re-measure",
6162
"check:driver-conformance": "node scripts/check-driver-conformance.mjs --self-test && node scripts/check-driver-conformance.mjs",
6263
"check:engine-double-contract": "node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs",
6364
"check:resume-authority-declared": "node scripts/check-resume-authority-declared.mjs --self-test && node scripts/check-resume-authority-declared.mjs",

0 commit comments

Comments
 (0)