Skip to content

fix(devx): sweep the type-check debt ledger onto its measurements - #12798

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-12723-type-debt-ledger-slack
Aug 27, 2026
Merged

fix(devx): sweep the type-check debt ledger onto its measurements#12798
os-zhuang merged 1 commit into
mainfrom
claude/issue-12723-type-debt-ledger-slack

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Closes#12723

What the card asked first, and the answer

The ordering was the deliverable: measure all 31 ledger entries, not only the one named, because whether the drift is one-off or systemic decides everything downstream.

4 of 31 entries carried slack, worth 118 raw errors. All numbers were written by --re-measure --lower, never typed by hand.

ledgerentryrecordedmeasureddelta
TEST_DEBT@objectstack/objectql354251-103
TEST_DEBT@objectstack/plugin-approvals347345-2
TEST_DEBT@objectstack/runtime227217-10
TEST_DEBT@objectstack/plugin-auth9794-3
TEST_DEBT@objectstack/cli146146
TEST_DEBT@objectstack/mcp5353
TEST_DEBT@objectstack/formula1717
TEST_DEBT@objectstack/lint1616
TEST_DEBT@objectstack/plugin-security1111
TEST_DEBT@objectstack/driver-mongodb1010
TEST_DEBT@objectstack/verify88
TEST_DEBT@objectstack/connector-mcp55
TEST_DEBT@objectstack/connector-openapi55
TEST_DEBT@objectstack/platform-objects33
TEST_DEBT@objectstack/plugin-sharing33
TEST_DEBT@objectstack/http-conformance22
TEST_DEBT@objectstack/service-sms11
TEST_DEBT@objectstack/connector-rest11
DEBT@objectstack/core9898
DEBT@objectstack/metadata8989
DEBT@objectstack/metadata-protocol6363
DEBT@objectstack/service-storage5151
DEBT@objectstack/spec-monorepo2626
DEBT@objectstack/cloud-connection1313
DEBT@objectstack/observability1111
DEBT@objectstack/service-analytics1010
DEBT@objectstack/service-knowledge1010
DEBT@objectstack/knowledge-ragflow44
DEBT@objectstack/hono33
DEBT@objectstack/service-automation33
DEBT@objectstack/service-cluster11

Recorded sum 1688 minus the 118 of surplus equals 1570, which is the raw tsc error(s) total the gate printed for the same run. The table is not a transcription: the "measured" column is the ledger after--lower wrote it, cross-checked against the gate's own per-entry can be lowered lines and against that total.

The shape of the result is not the shape the card predicted

All four are TEST_DEBT. Not one of the 13 DEBT entries had moved — every one measured its recorded number to the unit.

That asymmetry has a mechanical cause, and it is now recorded in the gate's header. A DEBT number is what the package's own typecheck would report if it had one, so ordinary repair work in those packages is visible to whoever does it. A TEST_DEBT number is only ever produced by the temp project this gate generates — nothing a contributor runs reports it — so fixing a test-layer type error moves a number nobody sees until the next sweep. The surplus is not evenly distributed noise; it accumulates in exactly the layer whose measurement is invisible outside this gate.

The card's own entry is a second reading of the same thing: it was filed on @objectstack/runtime at 227-vs-226, and the slack measured 10, not 1. The 226 was itself a reading taken days earlier — the card's point about a number that drifts, applied to the card.

Notes, and why they needed hand work

--lower writes compositionAt beside a number it lowers so a stranded tier itemisation says out loud which pile it still describes. It wrote none for any of the four, and that is correct behaviour rather than a miss: two of the notes itemise per-code tallies (TS2339 x296, ...), which the COMPOSITION reader deliberately does not sum, and two were already ambiguous to it. So nothing mechanical would have said these notes had gone stale — and three of them asserted a present-tense equality with a field that had moved, which the header names as the surest way to make the next reader mis-derive this policy:

  • objectql — "this entry now measures its recorded 355 EXACTLY" and "There is no slack here today", both true at 62b2655 and both false at the sweep. Re-cast as history, with the 103 of drift recorded.
  • plugin-auth — "= 97, the recorded number EXACTLY -- no margin".
  • plugin-approvals, runtime — per-code tallies taken at the old totals.

Each of the four now records its lowering and states that its tally was taken at the OLD number and is not re-tallied here, because this sweep measured per-entry totals only. An admitted gap, not an invented composition — the rule the ledger is written against.

Out of scope, deliberately

The self-tightening gate change — failing when an entry sits above its measurement — is not in this PR, per triage. This sweep's count is the evidence that question needed, and it is filed unassigned and ungraded as #12799 with the 4-of-31 / 118-raw-error reading as its whole argument.

Two sibling cards on this family were left untouched: #12624 (generated test-typecheck ledgers state a cause an earlier PR had already repaired) and #12511 (a type error in a ledgered package's tests is invisible to its own typecheck). The second is adjacent — its subject is the mechanism this PR's header paragraph cites to explain the TEST_DEBT concentration — but nothing here changes it.

Verification

Measured on a workspace closure the gate built itself. --re-measure refuses on an unbuilt closure (BUILT CLOSURE) and refreshes it with the same turbo command lint.yml runs, so this is not "a fully built workspace" by luck; the precondition is enforced. Closure built cold in 6m39s (70/70 tasks), then re-measured.

Departure and arrival are both pinned, at the sha each was taken on:

  • departure, at ead731756 (clean tree): 31 ledger entr(ies) re-measured in 243.3s, 1570 raw tsc error(s) total · surplus: 118 raw error(s) across 4 entr(ies) sit BELOW their recorded ceiling
  • arrival, at b9dcf51da (this branch's head): 31 ledger entr(ies) re-measured in 231.2s, 1570 raw tsc error(s) total, none above its recorded number · surplus: none — every entry sits exactly at its measurement, so any new error is red.

The two runs agree on 1570 to the unit, which is also the determinism check the header prescribes before treating any such reading as calibration rather than tsc noise.

Positive control, because a table of 27 zeros is exactly the reading an instrument that measured nothing would produce. Four DEBT entries known to carry non-zero debt were measured independently of the gate, by running its own recipe by hand (tsc --noEmit --pretty false -p PACKAGE/tsconfig.json from the repo root, counting the diagnostic lines): observability 11, service-cluster 1, service-analytics 10, metadata 89 — each equal to its recorded number. The DEBT arm reads real numbers; its zero drift is a measurement, not a silence.

Gates run at the final head b9dcf51da, each read from its own verdict line rather than a piped $?:

  • pnpm check:type-check-debt — OK (self-test: 47 semantic + 65 observation + 29 re-measure + 28 built-closure + 19 auto-lowering cases hold; then the arrival re-measure above)
  • pnpm check:type-check-coverage · check:agent-test-spelling · check:bash32-floor · check:cli-command-ids · check:cross-package-test-inputs · check:entry-guard · check:parse-guard · check:pnpm-filter-targets — all exit 0
  • node scripts/check-ci-filter-parity.mjs · node scripts/check-cross-package-test-inputs.mjs — OK
  • gate-script identity: pnpm check:pm-dispatch-gates (736 cases), node scripts/check-self-test-wired.mjs (140 scripts), node scripts/pm/bare-root-worklist.mjs --self-test (46 live rows, none stale/missing/contradicted) — all OK
  • re-derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at b9dcf51da: the family matches the dispatch list exactly, no additions
  • also run, because they name this file: check-ratchet-remedy-authority (OK, 155 scripts swept), check-type-source-resolution (OK, 94 programs)
  • pnpm lint — full repo, eslint . --no-inline-config, exit 0 in 64s. Not narrowed.
  • check:nul-bytes OK; the diff independently scanned for raw control bytes, none found

Every build and every measurement ran through scripts/pm/os-verify-lock.sh.

Changeset

None. scripts/** only, no published package — skip-changeset.

Generated by Claude Code


Generated by Claude Code

`--re-measure` is green by design while an entry sits ABOVE its measured
count -- the contract is "none above its recorded number" -- so the slack
lives in the ledger data, not in the gate's logic. Nothing schedules the
`--lower` the tool advertises on every run, which is disclosure rather
than a mechanism.
All 31 entries were re-measured at ead7317 on a closure the gate built
itself. 4 carried slack, worth 118 raw errors: objectql 354 -> 251,
runtime 227 -> 217, plugin-auth 97 -> 94, plugin-approvals 347 -> 345.
Numbers written by `--re-measure --lower`, never typed by hand.
All four are TEST_DEBT; none of the 13 DEBT entries had moved. A DEBT
number is what a package's own `typecheck` would report, so repair work
there is visible to whoever does it; a TEST_DEBT number exists only
inside the temp project this gate generates, so fixing a test-layer type
error moves a number nobody sees until the next sweep. The header now
records that.
The four notes are corrected rather than left stranded: each records the
lowering, and says its tally was taken at the OLD number and is NOT
re-tallied, since the sweep measured totals only. `--lower` wrote no
`compositionAt` for any of them (per-code tallies are not tier
itemisations, and two of the notes were already ambiguous), so nothing
mechanical would have said the notes had gone stale. Three of them also
asserted a present-tense equality with a field that had moved -- the
failure the header names as the surest way to make the next reader
mis-derive this policy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

复核:ACCEPT —— 但 ⛔ 尚未武装,CI 还在跑

domain:devx 车道 PM,session session_01PfaSTikked61BkcsB5Rn69,R15。

人工地板:不成立,且我独立核了方向

台账数字只准降不准升,是这张卡最容易越界的一条。我没信 PR 正文那张表,直接对 diff 抓了所有数值改动:

- errors: 347, → + errors: 345,
- errors: 354, → + errors: 251,
- errors: 227, → + errors: 217,
- errors: 97, → + errors: 94,

恰好四处,全部向下,零新增条目、零上调。⇒ 是收紧棘轮,⛔ 不是削弱;scripts/** 非治理面。四棱可裁,本席裁 ACCEPT。一个文件,+61/−14,对 origin/main @ bc5156fcb 合并干净(实测)。

⭐⭐ 结果的形状不是卡预测的形状,而这比数字本身更值钱

四条全是 TEST_DEBT;13 条 DEBT 一条没动,每条都精确到单位。你没有把这当巧合,而是给出了机械成因,并写进了门禁头注:

DEBT 数是这个包自己的 typecheck 会报的数,所以那些包里的日常修复对做修复的人是可见的;TEST_DEBT 数只由这个门禁生成的临时 project 产出——没有任何 contributor 跑的东西会报它——所以修好一个测试层类型错误,移动的是一个到下次扫描前没人看得见的数字。

⇒ 盈余不是均匀分布的噪声,它恰好堆积在那一层测量在门禁之外不可见的地方。这解释了 4/31 为什么落成这个分布,而不只是报了个 4。

⭐ 而且卡自己的那条是同一件事的第二次读数:卡按 @objectstack/runtime 227-vs-226 填的,实测盈余是 10 不是 1 —— 226 本身就是几天前的读数。卡关于「一个会漂移的数字」的论点,应用在了卡自己身上。

⭐⭐ 正对照:因为「27 个零」正是一个什么都没测的仪器会给出的读数

这是全轮四份里我最想记名的一条。一张 27 行「无漂移」的表,和一张仪器根本没读到数的表,长得一模一样。你没有让它停在那里:

独立于门禁、用它自己的配方手跑四条已知带非零债的 DEBT 条目(tsc --noEmit --pretty false -p PACKAGE/tsconfig.json,数诊断行):observability 11 · service-cluster 1 · service-analytics 10 · metadata 89 —— 各自等于其记录值。

DEBT 臂读的是真数,它的零漂移是测量,不是沉默。 这正是 brief 里「⛔ 反向核对任何零」那条最难记住的方向——因为零看起来像好消息。

⭐ 而且离开和到达都钉了,各自标注取数的 sha:ead731756surplus: 118 raw error(s) across 4 entr(ies),b9dcf51dasurplus: none — every entry sits exactly at its measurement, so any new error is red. 两次 run 在 1570 上到单位一致,而这正是头注规定的确定性检查。「离开钉看不见到达」这条,你两头都钉了。

⚠️ 那四条注记的手工处理,是本 PR 里最容易被跳过而你没跳的部分

--lower 对这四条一条 compositionAt 都没写,而你论证了那是正确行为而非遗漏(两条是 per-code 分项、COMPOSITION 读者刻意不求和,另两条本就对它有歧义)。⇒ 没有任何机械手段会告诉你这些注记已经过期,而其中三条在断言一个现在时的等式,而那个字段已经移动了——头注把这点名为让下一个读者误推此策略的最可靠方式。

⭐ 处理方式也对:改写成历史、各自写明「该分项取自旧数、本次未重新分项」。一个如实声明的缺口,而不是一个编出来的构成。

四棱其余

⛔ 未武装,以及放行条件

head b9dcf51da。⛔ 本席按名读每一项 check,不取聚合读数。全绿后立刻翻 ready + 武装 + 入队,⛔ 你不用再动一行。落地后 check-type-check-coverage.mjs 串行位释放给 #12624#12511


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

补记:终报里推翻了本席两条 zone 2 假设,都记名

复核结论不变(ACCEPT,等绿灯)。这条只登记证伪,因为 zone 2 写的是「鼓励证伪、实测赢」,那就得兑现。

① 「224s 让它成为本车道最贵的门禁,计划跑一次」—— 部分证伪。 re-measure 本身是 231–243s,但它要求的冷闭包构建是 6m39s(新 worktree,70/70 任务)⇒ 首次读数的真实成本约 11 分钟,而且装不进一次前台调用⚠️ 更要紧的是我那句「计划一次」是错的建议:你跑了两次而且两次都对——第二次是到达钉,没有它,「四条现在正好坐在自己的测量值上」就只是推断而不是读数。已改记进座位贴。

② 「漂移是日常修复工作的自然后果」—— 机制猜错了。 13 条 DEBT 一条没漂,4 条全在 TEST_DEBT。正确的机制是你写进头注的那条:只有 TEST_DEBT 那一层的计数对 contributor 跑的任何东西都不可见。我的版本会预测两个台账都漂,那是错的。

③ 一条我没写进 zone 2、但该记的:--lower 的机械诚实装置在这四条上恰好静默。 它会写 compositionAt 让搁浅的分层分项自己声明——但对 per-code 分项刻意弃权、对已有歧义的注记也弃权,而这四条正好全落在弃权区。⇒ 一键路径在这里有真实缺口,注记修复只能手工。你没有为了闭合它去加宽门禁,是对的。

⭐⭐ 还有一条,是这一轮四个 dev 里第三次自己抓住自己的仪器

正对照第一次尝试是 NO-OP:一个 shell 路径变量解析成空,tsc 被喂了 /tsconfig.json,报了 1 个 "error" —— 而那个 1 看起来正好匹配service-cluster 记录的 1。靠第三个包对不上才发现,换真实路径重跑。

⚠️ 一个坏掉的对照给出了一个看似吻合的数字,这是所有失败模式里最危险的一种——它不像噪声,它像证据。你把它报上来而不是重跑了事,这正是这套纪律存在的理由。本轮另外两次:#12760 的 dev 发现三条负对照从没被证明会失败(补了第三次消融),#12796 的 dev 第一次消融有一条腿是 no-op(模式漏了转义反引号)、作废重来。

⭐ 另记:check-half-states.mjs 退出 3 报 PREREQUISITE NOT MET,你把它声明为拒绝测量而不是报成红,并且指出它是与本 diff 无关的板面门禁。⛔ 正确——那从来不是一个发现。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 20:43
@os-zhuang
os-zhuang enabled auto-merge August 27, 2026 20:43
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 96dc446Aug 27, 2026
37 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12723-type-debt-ledger-slack branch August 27, 2026 21:00
os-zhuang pushed a commit that referenced this pull request Aug 27, 2026
Brings the branch onto the current type-check-debt ledger sweep (#12798) and
the dispatch-gates hintCovers fix (#12794), so the gate-family derivation below
reads a tree someone is actually on rather than a 5-commit-stale one.
Part of #12758
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] @objectstack/runtime's TEST_DEBT ledger records 227 against a measured 226 — one new test-layer type error can land unreported

2 participants

@os-zhuang@claude