Skip to content

Commit 5e1ea65

Browse files
baozhoutaoclaudeos-zhuang
authored
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 * ci(dx): objectql 的 TEST_DEBT 校准到 335,并把合并队列的竞态写进不变式文档 队列把本 PR 踢出:@objectstack/objectql 记 333,队列基实测 334。333 是在 07:41 的 main 上冻结的,而 #5802(registry.test.ts +116 行)与 #5850 在 07:52 之后才 落地。合并当前 main 后实测 335,两条增量都能逐一归因: - +1 TS2339 在 src/registry.test.ts —— #5802 新增的 registry 测试; - +1 TS2554 在 src/engine-update-prior-read-scope.test.ts —— #5850(#5284) 新建的文件。 tests 125 -> 126。其余 33 条纹丝不动(总计 2018 raw errors,无一超出记录值)。 先证伪了另一种解释:同一棵树连跑两次 --re-measure,输出逐字节相同,所以不是 tsc 计数不确定,校准就是正确处置(不需要谈容差)。 顺带把队列这一面写进 MEASURED 的文档块:队列是按「合并到队首」构建的,队首会随 前面的条目落地而移动,所以重跑失败的 job 无法自愈(重跑复用同一个 merge ref, 量的还是那个旧基),唯一修法是推新提交;以及排在后面的 PR 会被连坐,红了要先撤出 队列再修。双跑证伪法也一并写下,免得下一个人重新推导。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW * ci(dx): objectql 的 TEST_DEBT 校准到 339 —— +4 全在 #5861 新增的那一个文件里 第三次基漂移,签名同前:335 是在推上校准提交那一刻冻结的,而 #5861 (SaveMetaItemResponseSchema,#5745)几乎同分钟落地。合并 c15fcee 后实测 339, +4 全部集中在该 PR 新增的 src/save-meta-response-conformance.test.ts 一个文件: - :115 TS2554 Expected 2-5 arguments, but got 1 - :119 TS6133 'LOG' is declared but its value is never read - :119 TS2304 Cannot find name 'appendFileSync' - :119 TS2304 Cannot find name 'OUT' tests 126 -> 127。其余 33 条纹丝不动(合计 2022 raw errors,无一超出记录值,也没有 一行 can-be-lowered —— 34 条全部与实测严格相等)。 那两条 TS2304 已在 note 里点名:名字都解析不到,那一行根本跑不起来,不是类型讲究 问题 —— 但那是 #5861 自己要修的,不是本台账要修的,所以只记录、不代修。 按非确定性假说已证伪(前一轮双跑逐字节相同),本轮不再双跑。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW * ci(dx): 按裁决(option A)在合并树上全量重校准,三个热包记档 +10 余量 merge origin/main 后在合并树 e8db1a2 上重跑 --re-measure:5 条上漂 (service-analytics 7->10、runtime 218->227、rest 143->153、plugin-auth 129->131、lint 30->32)、1 条下降(service-storage 42->41),其余 28 条与 记录值严格相等。 按维护者 2026-08-07 的裁决执行选项 A:今日实证的三个热包 (objectql / lint / rest)按实测 +10 记档,note 逐条写明「bootstrap margin (+10 over N measured at e8db1a2) -- tighten via the ℹ hint immediately after landing」,并在 MEASURED 文档块加一段说明这是台账里唯一的余量、 每轮打印的 ℹ 行就是收紧工单。其余条目一律精确等于实测。 note 成分按实测重写,delta 能归因的逐条归因、不能的明说: - runtime +9 全部是 TS18048,全在本窗口新增的一个文件 src/domains/meta-item-envelope.test.ts(#5563 / PR #5895); - rest +10 中 8 条来自三个新增测试文件,余 2 条落在既有文件、不再编造归因; - plugin-auth +2 中 1 条在新增的 last-admin-guard.test.ts(#5941 / PR #5993); - service-analytics +3 仍全在 measure-source-field-gate.test.ts 同一文件、 同一错误码(#5716 / PR #5963),该包 3 -> 7 -> 10 全程无人看见; - lint +2 两条 TS7006 均在既有文件,pre-merge 逐文件计数未留存,如实记录不归因; - service-storage -1 跟着 #5540 / PR #5983 摘除 IStorageService.list(prefix), 下调而非留着——高于实测又没写明的余量就是暗账。 tests 文件数一并对齐实测(objectql 130、runtime 102、rest 62、plugin-auth 38、 plugin-security 35、service-sms 5)。 合并冲突只有 TEST_DEBT 一处:main 的 #5773 / PR #5982 改过 service-sms 一行,本分支重写了整块——取本分支并把该条的事实并入,两侧都不丢。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5 * ci(dx): 删掉空 frontmatter changeset,改走 skip-changeset —— #5471 / PR #6059 在飞行途中落地 新门禁 `scripts/check-empty-changeset.mjs` 明确判定:PR 新增的空 frontmatter changeset 是违规。本 PR 的 `.changeset/type-check-debt-ledger-ratchet.md` 正是 「A added, empty at head」这一行,门禁在合并树上逐字点名了它。 按门禁给的两条路选:本 PR 只动 dev scripts / CI(`scripts/`、`.github/workflows/`、 `package.json`、`AGENTS.md`),不发布任何包 —— 走 route 2:删掉 changeset,改用 `skip-changeset` 标签。空 changeset 名不到任何包,正文到不了任何 CHANGELOG,却是 changesets/action 的真实输入(全空集会让 Release 静默且绿地空跑,即 #4898);标签 不产生输入,因此严格更优。 原 PR body 的「## changeset」一节因此过期,更正写在正文「裁决落地」一节里,原节 按接手协议不改写。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5 * ci(dx): 第二个合并窗口的再校准 —— service-storage 转记档余量,objectql 余量按新实测抬回 +10 合并 main 到 dca5bd3 后再全量重测,余量在一小时内被兑付了两笔,记录如下: - `@objectstack/objectql` 实测 339 -> **345**(+6 全是 TS2554,全在 `src/summary-rollup.test.ts`,由飞行途中落地的 #5749 / PR #6013 扩写)。 记档 349 把它静默吸收了 —— 若按精确值 339 记账,这就是同一场赛跑的第 6 次红。 按裁决「实测 +10」把记录抬到 **355**,恢复满额余量。 - `@objectstack/service-storage` 42 -> 41 -> **42**:`IStorageService.list(prefix)` 的退休被拆成两个 PR,spec 半边(#5540 / PR #5983)减 1、适配器半边 (#5541 / PR #6061)删旧测试(-1 TS7006)又新增 `storage-adapter-list-retirement.test.ts`(+2 TS2835),净 +1。上一轮我按实测 下调到 41,一小时后就被咬红 —— 正是派发令说的「非余量条目被基漂移咬住」, 按同一记档规则给这条加 +10,记 **52**,不开精确校准 lap。 一个值得写进文档块的新形状:**拆成两个 PR 的退休会让计数先降后升**,在两半之间 记下的精确值,推上去之前就已经过期。 `rest` / `lint` 两条实测未动(153 / 32),余量原样,note 补记「一小时后在 77c7c88 复测仍是该值」。 重测输出:四条记档余量各打印一行 ℹ(各 -10),无一条上漂,exit 0。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5 * ci(dx): 第三个合并窗口的再校准 —— @objectstack/mcp 转记档余量(#5278 option A 第五条) 合并队列在 03:25:18Z 把本 PR 踢出,唯一红线是 `@objectstack/mcp`: TEST_DEBT 记 52,`tsc --noEmit` 实测 53(+1)。这 +1 不是队列里排在 前面的 #6077 引入的(该 PR 自己那一代队列构建是绿的),而是一条此前 无人可见的既有漂移 —— ratchet 第一次对着移动的 base 重测,就把它照了 出来。换句话说,这个不变量在自己的引入 PR 上先抓到了一条真实漂移, 这本身就是它有效的证据。 +1 已完全归因:src/skill-prompts.test.ts(185,23),一处把 `SkillPrompt | null` 断言成 `Record< string, unknown >` 的 TS2352 —— 正是 #3905 / PR #6077 投影 skill `instructions` 为 MCP prompt 原语时 新增的文件,测试文件数也因此 8 -> 9。 按裁决走 option A(记实测 +10)而不是再跑一轮精确校准:packages/mcp 今天刚落一个 feature,属于活跃变动包,精确数字大概率会再输一次已经 被裁决判死的那场竞速(option D 连输五次)。RECORDED 63 = 实测 53 @ 34558c2 + 10,note 按顶栏要求重写了成分 —— 旧 note 写的 "`error` is of type unknown, one catch-block idiom" 是错的,51 条 TS18046 全部是响应体 `json` 绑定,与 catch 块无关。 顶栏 BOOTSTRAP MARGINS 段同步从"四条"改为"五条"。 验证:`pnpm check:type-check-debt` 退出 0,34 条台账全部重测、无一条 高于记录值,五条余量各自打印 `ℹ can be lowered`;同一棵树上连测两次 数字逐字一致(mcp 53 / objectql 346 / rest 153 / lint 32 / service-storage 42),故这是校准问题而非 tsc 不确定性。 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-zhuang <jack@objectstack.ai>
1 parent def5919 commit 5e1ea65

4 files changed

Lines changed: 676 additions & 51 deletions

File tree

.github/workflows/lint.yml

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

@@ -720,6 +723,42 @@ jobs:
720723
- name: Type check workspace packages
721724
run: pnpm exec turbo run typecheck --filter='./packages/*' --filter='./packages/*/*' --filter='./apps/*'
722725

726+
# The MEASURED half of the coverage gate (#5278). The cheap structural
727+
# check near the top of this job asserts that a package without a
728+
# `typecheck` script carries a DEBT/TEST_DEBT entry with a positive number
729+
# written down — and, until now, nothing more: `errors: 28` and
730+
# `errors: 1` were equally acceptable to it, because the ledger was never
731+
# re-measured. So a ledgered package's real error count could grow without
732+
# bound while the gate reported success. It had: metadata-protocol
733+
# recorded 28 and reported 63, service-analytics 3 -> 7, service-automation
734+
# 2 -> 5, and the wholesale re-measure this step ships with found 17 of the
735+
# 34 entries understated and not one overstated. A number that has drifted
736+
# 2.25x no longer describes the debt it claims to freeze.
737+
#
738+
# Asymmetric, on purpose: a count ABOVE its recorded number fails, a count
739+
# below prints an informational "can be lowered / graduation candidate"
740+
# line and stays green. Fixing errors must not also require editing a
741+
# bookkeeping number before CI will go green, or the ledger charges a toll
742+
# on exactly the work it exists to encourage.
743+
#
744+
# Here rather than beside its structural half because it runs the real
745+
# compiler over ~34 projects, and tsc resolves workspace imports through
746+
# each dependency's built `dist/*.d.ts` — so it needs the build steps
747+
# above, which this job already pays for. The build filter is widened to
748+
# the nested package groups (packages/services/*, packages/drivers/*,
749+
# packages/plugins/*, …) because most ledgered packages have no
750+
# `typecheck` script and therefore never entered the turbo task graph that
751+
# would otherwise have built their dependencies; it is a superset of what
752+
# the steps above already built, so it is cache hits plus the remainder.
753+
# Measured cost of the re-measure itself: ~4 min, sequential by design
754+
# (parallel tsc processes trade wall clock for an OOM risk on a job that
755+
# has just built the whole workspace).
756+
- name: Build the ledgered packages' dependencies
757+
run: pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*'
758+
759+
- name: Re-measure the type-check DEBT / TEST_DEBT ledger
760+
run: pnpm check:type-check-debt
761+
723762
- name: Type check example apps
724763
run: pnpm --filter './examples/*' run typecheck
725764

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
@@ -63,6 +63,7 @@
6363
"check:workflow-status-functions": "node scripts/check-workflow-status-functions.mjs --self-test && node scripts/check-workflow-status-functions.mjs",
6464
"check:published-files": "node scripts/check-published-files.mjs --self-test && node scripts/check-published-files.mjs",
6565
"check:type-check-coverage": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs",
66+
"check:type-check-debt": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs --re-measure",
6667
"check:driver-conformance": "node scripts/check-driver-conformance.mjs --self-test && node scripts/check-driver-conformance.mjs",
6768
"check:engine-double-contract": "node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs",
6869
"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)