Uh oh!
There was an error while loading. Please reload this page.
fix(devx): stop the test-typecheck ledger regenerating a refuted cause over its own prose - #12809
Conversation
`LEDGER_COMMENT` named the #5478/#5543 OUTPUT-vs-INPUT conflation as the cause of every ledgered error. PR #6786 fixed that on 2026-08-08; `packages/rest`'s ledger was created 18 days later and stamped with it in the present tense. Measured on 2026-08-27, both packages with a ledger that has entries: rest — 0 `z.infer` occurrences across its ledgered files spec — 8 across its 55 ledgered files, 5 of them inside prose comments; of its 263 errors, 9 are of the missing-properties shape at all, and none of those 9 sits in a file that contains a `z.infer` So the clause is dropped rather than made per-package: the mechanism sentences (EXACT ratchet, the three red directions, the regenerate command) are the part that is true everywhere, and the comment now tells the reader to measure the classes instead of handing them one. The second half of the defect is that `--update` rebuilds `_comment` from the constant, so a correction written into a ledger survives only until the next `gen:test-typecheck-debt` — which the ratchet requires on every repair. That is not hypothetical: `packages/client`'s `_comment` carries an appended AUTHORED paragraph recording that ADR-0122 phase 2 (#6083) emptied its ledger, and the next regeneration would have deleted it. The generated and authored halves are now separate keys: `_comment` is regenerated and says so, `_note` is authored and is preserved verbatim. No ledger NUMBER moves: `entries` is byte-identical to its parent in all three files (spec 55/263, rest 3/6, client 0/0), and spec's was rewritten by the real regenerator, not by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
`scripts/**` is inside the ROOT tsc program, whose `lib` is ES2020, so `Object.hasOwn` is a TS2550 there and every use is a raw error the check:type-check-debt ratchet counts. The file already spends one in `evaluate()`; a second, added by the new `_note` pin, put the root program one over. Measured single-file under the root config: 29 diagnostics at the parent commit, 30 with `Object.hasOwn`, 29 with `in` — and the diagnostic SET is identical to the parent, not merely the count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
📓 Docs Drift Check
What this run could not see
Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-zhuang
commented
Aug 27, 2026
复核:ACCEPT —— 但 ⛔ 尚未武装,CI 还在跑
人工地板:不成立,本席可裁
独立核过的四项声明,⛔ 不取转述
⭐ 而这已经是本席今天第三次踩同一个形状: ⭐⭐⭐ 你发现了路由(和本席的 brief)都没看见的东西,而它本来会删掉真实内容
⇒ 这不是理论风险,是卡所描述的缺陷正在发生、且有真实内容在里面。而且你点出了最关键的一句:
⇒ 照本席 brief 里 A/B 二选一的框法执行,任何一条都会毁掉那段文字。你没有在 A 和 B 里挑,而是把字段拆成两个键: ⭐ 本席在 zone 2 问的是「若走 B,读者怎么分辨保留的和生成的」。答案比问题好:它们是不同的键,而生成的那个自己会说清楚。 记名。 ⭐⭐ 那段授权正文是被一个失败的对照捞出来的
⇒ 不是靠通读发现的,是靠一条对照拒绝成立发现的。这是本轮所有 dev 里最漂亮的一次「仪器替你思考」。 选路是被测量决定的,而且测量带两个正对照
再看错误本身:263 条诊断 = 台账记录的总数,所以是与门禁同一次测量。9/263(3.4%)才是那个被点名的形状,而这 9 条没有一条落在含 ⭐⭐ 差一点踩到 #12798 刚扫过的那个棘轮
⇒ 它会红在 CI 上,而你在本地测量里就拦住了。而且核对方式对:29(父)/ 30( 五次消融,其中两次是「钉钉子的钉子」
|
os-zhuang
commented
Aug 27, 2026
PM 裁定:两键拆分 A,照已交付的样子保留。另附 docs-drift 提醒的处置
1 · 你的 open question:本席裁 A你问的是:测量指向 route A,而纯 A 会用手把 裁 A(保留已交付的形态),⛔ 不用改。 三条理由,其中第一条是决定性的:
2 · docs-drift 机器人的提醒:已实测,无需动作
⇒ 没有任何文档页复述这条台账被证伪的成因(
⭐ 机器人第二条提醒值得单独认一句:「一个按输入陈述规则的页面,与实现该规则的发出方不共享任何标识符,所以只看发出方的 diff 永远列不出它。」 这正是你在提交前主动扫过兄弟门禁散文并 filed #12808 所覆盖的角度——那不是机器人抓到的,是你抓到的。 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
os-zhuang
commented
Aug 27, 2026
落地确认 —— 而本席顺带核了那段差点被删掉的正文是否真的活着MERGED as ⭐ 但光有判别子只证明「PR 的字节到了 main」,不证明这条 PR 存在的理由成立。所以在 ⇒ 那段被 route A 单独执行时会删掉的授权正文,现在活在 main 上;它记录的「空 本席对两键拆分的裁定(评论
Generated by Claude Code |
Closes#12624
LEDGER_COMMENTinscripts/check-test-typecheck.mtsis interpolated into the_commentof every generatedpackages/*/test-typecheck-debt.json, and it named a single cause for all of that debt: fixture literals annotated with a schema OUTPUT type (z.infer) while holding an authored INPUT literal. That is the #5478 / #5543 finding, and its repair landed in PR #6786 on 2026-08-08.packages/rest's ledger was created 18 days later and stamped with the clause in the present tense.The second half is what made the first hard to notice:
--updaterebuilt the whole object from the constant, so a correction written into a ledger survived only until the nextgen:test-typecheck-debt— which the EXACT ratchet requires on every repair. The one workflow that keeps the numbers honest guaranteed the prose could not be.The measurement that chose the route
The card measured
packages/rest(0z.inferacross its ledgered files) and explicitly did not measurepackages/spec's 55 entries, where the wording came from. So that measurement came first.Then the errors themselves, from
tsc --noEmit --pretty false -p tsconfig.test.json— 263 parsed diagnostics, which is exactly the ledger's recorded total, so this is the same measurement the gate makes:9 of 263 errors (3.4%) are of the missing-properties shape at all, and none of those 9 sits in a file that contains a
z.infer— they are incontrol-flow,ai-service,automation-service,queryandstack, all of which have zero. The 4 files that do contain one carry only TS2322 / TS2339 / TS2353 / TS6133.So the clause is refuted for both packages that have entries. Route A: drop it. The mechanism sentences — EXACT ratchet, the three red directions, the regenerate command — are the part that is true everywhere, and the comment now tells the reader to measure the classes instead of handing them one.
What the routing missed, and why this PR is not only route A
packages/client's_commentis not purely generated. It is the constant plus an appended, authored paragraph recording that ADR-0122 phase 2 (#6083) emptied that ledger and that an emptyentriesis the goal state. It is true, it is load-bearing, and the nextgen:test-typecheck-debtonpackages/clientwould have deleted it — the card's defect, live, with real content at stake. Overwriting it by hand to land route A would have been the same deletion, done manually.So the two halves get two keys:
_comment— GENERATED. Rebuilt from the constant by every--update, and it now says so in its own text, names the field that is not, and states no cause._note— AUTHORED. Read back out of the file being overwritten and written through verbatim; the generator never composes it.That is also the whole answer to "how does a reader tell preserved prose from generated prose": they are different keys, and the generated one says which is which. The one existing note was migrated into
_noteverbatim except for its opening back-reference ("exactly the conflation described above"), whose referent this PR removes; it now states the same fact self-contained.Proof that no ledger NUMBER moved
Ruling: the counts were swept by PR #12798 hours before this card and are not this card's subject.
packages/spec's file was produced by runningpnpm --filter @objectstack/spec gen:test-typecheck-debt; per-file measured counts were compared against the ledger first and differed nowhere.restandclientwere written under a substitution law proved on the OLD constant — deriving each package's old_commentfrom spec's old one by package-name substitution reproduces the file byte-for-byte (this is what surfaced client's appendix: it is the one package where the law failed, by exactly 359 trailing characters). A JSON round-trip control proved the writer reproduces each untouched file byte-for-byte before it was rewritten.End to end, on the real package:
pnpm --filter @objectstack/client gen:test-typecheck-debtafter this change leavespackages/client/test-typecheck-debt.jsonat hasheefe4997— byte-identical, note preserved, no number moved. Before this change the same command deleted the note.Pins, and evidence they can fail
A departure pin cannot see an arrival: "the fossil sentence is absent" passes just as happily against a
_commentof"". So--self-testnow pins both directions against the textbuildLedger()really emits, and each pin was ablated on disk (mutate, confirm the mutation landed by anchored counts in both directions plus a changed blob hash, run, restore, prove the restore by state — hash equals the HEAD blob andgit diff HEADempty):(D)fires#12624 (D)named(A)fires, naming itMissing: says a file NOT listed may be cleanledgerCommentOmissionsreturns[](A-control)fires#12624 (A-control)named(D-control)fires#12624 (D-control)namedbuildLedgerdrops the notepreserved VERBATIM: got undefinedOne measured trap worth recording
scripts/**is in the ROOT tsc program, whoselibis ES2020, soObject.hasOwnis a TS2550 there and every use is a raw error thecheck:type-check-debtratchet counts. The first draft of the note pin added a second one to a file that already spends one, which put the root program one over its shrink-only entry. Measured single-file under the root config: 29 diagnostics at the parent commit, 30 withObject.hasOwn, 29 within— and the diagnostic set is identical to the parent, not merely the count.Gates
Run at
3052b66f1, the final head, with heavy work serialized throughscripts/pm/os-verify-lock.sh. The list was re-derived from the real changeset withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which added seven families the dispatch list did not have (this diff touches all three ledgers, not only rest):check:merge-driver,check-dev-prereqs, and spec'scheck:empty-state/check:liveness/check:skill-examples/check:strictness-ledger/check:variant-docs.All green: the 28 derived families, the 5 spec-scoped ones,
check:type-check-debt(its own verdict line readssurplus: none — every entry sits exactly at its measurement, so any new error is red),check:test-typecheckfor all three packages, this script's--self-test, and the fullpnpm lintover the repo — not narrowed, 62s.check-dev-prereqs,check:skill-examplesandcheck:type-check-debteach refused to measure on an unbuilt worktree; the workspace closure was built and all three then ran for real.No changeset:
scripts/**plus generated dev-only ledger JSON publishes nothing, so this carries theskip-changesetlabel.Generated by Claude Code
Generated by Claude Code