实施 #5429(ledger 可读性提为 posture 无关的独立检查,PR #5988)时核验发现。观察类,今日无人踩到,记录以免下次有人照抄。
事实
packages/cli/src/commands/doctor.ts,readInstalledPackageEntries() 里:
const dir = path.join(cwd, mod.DEFAULT_INSTALLED_PACKAGES_DIR ?? '.objectstack/installed-packages');
DEFAULT_INSTALLED_PACKAGES_DIR 是 @objectstack/cloud-connection 的导出,也就是「ledger 目录叫什么」这件事的唯一权威。这里对它做了一次消费端 ?? 兜底。
同一个函数往下两行,读 list() 的返回时写着(#5413 留下的注释,原文):
// #5413 — read BOTH halves of the listing. Destructured with no `??`
// fallback on purpose: `list()` declares this shape, and a tolerant read
// here would be the exact consumer-side accommodation that let the silence
// live in the first place.
const { entries, skipped } = new mod.LocalManifestSource(dir).list();
同一个文件、同一个函数、同一个被读的模块,一处禁止 ?? 兜底并写明了理由,另一处正在用。
为什么这可能是个缺陷(以及为什么判为观察类)
Prime Directive #12:producer 是契约,consumer 不得用 ?? 容忍。这个 ?? 的语义是「模块加载成功了但没有这个导出」—— 真发生时,doctor 会安静地去读一个硬编码路径,而不是报告「我读 ledger 的那个包不认得 ledger 目录了」。运行时(rehydrate() 侧)读的仍是权威常量,于是两侧看的是两个目录、都不作声。#5644 刚刚才把「包在但加载不了」从静默里拆出来,这是同一条边界上剩下的最后一格。
判轻的原因,也是不建议现在动它的原因:
- 该导出当前存在,没有任何版本里不存在过 —— 这是一段死代码,不是活缺陷;
@objectstack/cloud-connection 是可选依赖、动态 import() 载入,没有静态类型能在编译期钉住这个导出,所以「删掉 ?? 直接读」需要顺带决定「读不到时报哪一行」—— 大概率是 installedPackageLedgerReaderFailureCheck 的近亲(包在、但不是我认得的那个包),属报告面的小扩展,不是删两个字符;
- 真删掉时应连带考虑:
installedPackageLedgerSkippedEntriesCheck 的 fix 里也硬编码了 Under \.objectstack/installed-packages/`:这句字面路径,而那一刻 doctor 手里是有真实dir` 的。
建议处置
留 finding,不排期。若哪天 packages/cloud-connection 的导出面要动,或有人再来碰 readInstalledPackageEntries(),顺手一并做掉:去掉 ??,把「导出缺失」变成一条具名的报告行,并让 fix 引用真实解析出的目录而不是字面量。
Generated by Claude Code
实施 #5429(ledger 可读性提为 posture 无关的独立检查,PR #5988)时核验发现。观察类,今日无人踩到,记录以免下次有人照抄。
事实
packages/cli/src/commands/doctor.ts,readInstalledPackageEntries()里:DEFAULT_INSTALLED_PACKAGES_DIR是@objectstack/cloud-connection的导出,也就是「ledger 目录叫什么」这件事的唯一权威。这里对它做了一次消费端??兜底。同一个函数往下两行,读
list()的返回时写着(#5413 留下的注释,原文):同一个文件、同一个函数、同一个被读的模块,一处禁止
??兜底并写明了理由,另一处正在用。为什么这可能是个缺陷(以及为什么判为观察类)
Prime Directive #12:producer 是契约,consumer 不得用
??容忍。这个??的语义是「模块加载成功了但没有这个导出」—— 真发生时,doctor 会安静地去读一个硬编码路径,而不是报告「我读 ledger 的那个包不认得 ledger 目录了」。运行时(rehydrate()侧)读的仍是权威常量,于是两侧看的是两个目录、都不作声。#5644 刚刚才把「包在但加载不了」从静默里拆出来,这是同一条边界上剩下的最后一格。判轻的原因,也是不建议现在动它的原因:
@objectstack/cloud-connection是可选依赖、动态import()载入,没有静态类型能在编译期钉住这个导出,所以「删掉??直接读」需要顺带决定「读不到时报哪一行」—— 大概率是installedPackageLedgerReaderFailureCheck的近亲(包在、但不是我认得的那个包),属报告面的小扩展,不是删两个字符;installedPackageLedgerSkippedEntriesCheck的fix里也硬编码了Under \.objectstack/installed-packages/`:这句字面路径,而那一刻 doctor 手里是有真实dir` 的。建议处置
留
finding,不排期。若哪天packages/cloud-connection的导出面要动,或有人再来碰readInstalledPackageEntries(),顺手一并做掉:去掉??,把「导出缺失」变成一条具名的报告行,并让fix引用真实解析出的目录而不是字面量。Generated by Claude Code