Repository navigation
Description
实施 #5403(给 config 分析的 catch 加上 error 绑定)时顺带核验发现;未在该 PR 里改 —— #5403 的判定面被限定为 config 分析那一个 catch 的输出,本条是另一处 catch、另一条检查的判定面。
事实
packages/cli/src/commands/doctor.ts:771-784(origin/main 4b61cf3):
/** Read the installed-package ledger without going through HTTP. Best-effort:
* a runtime that never installed anything simply has no directory. */
async function readInstalledPackageEntries(cwd: string): Promise<any[]> {
try {
const mod: any = await import('@objectstack/cloud-connection');
const dir = path.join(cwd, mod.DEFAULT_INSTALLED_PACKAGES_DIR ?? '.objectstack/installed-packages');
if (!fs.existsSync(dir)) return [];
return new mod.LocalManifestSource(dir).list();
} catch {
return [];
}
}
这个 catch 不带绑定,且覆盖了两件性质完全不同的事:
import('@objectstack/cloud-connection')解析不到 —— 可选包没装。注释写明了这一条,而且是对的:doctor 必须能在没有该包的 checkout 里跑完。new mod.LocalManifestSource(dir).list()抛错 —— 目录存在(fs.existsSync(dir)已经通过),但里面某个 manifest 损坏 / 不可读 / JSON 解析失败。
第 2 种被当成第 1 种处理,一律 return []。
后果
唯一的消费者是 ADR-0120 D5e 的 unique-scope 建议(findUnscopedGlobalUniques,同文件 792-815):它把「本项目 metadata 的 findings」和「ledger 里已装包的 findings」拼在一起。ledger 那一半返回空数组时,doctor 走的是 else 分支:
✓ Unique scope No unconfirmed installation-wide uniques for this 'isolated' environment
也就是说,一个装了包、但 ledger 损坏的 isolated 环境,拿到的是一张干净的体检单,而不是「有一部分我没能读到」。这比报一条 warning 更糟:false PASS 会让操作者停止查看。
与 #5403 是同一族(诊断命令自己吞掉了唯一能解释状况的那个对象),但低一层:#5403 里被吞的是「检查跑不了」的原因,这里被吞的是「检查跑了一半」这个事实本身 —— 连「有东西没读到」都没说。也属于 #4801 / cloud#1020 的「诊断面与运行时不一致」家族。
复现路径(未在真机执行,静态可判)
前提:OS_TENANCY_POSTURE=isolated,.objectstack/installed-packages/ 存在且至少一个条目损坏(截断的 JSON、权限不可读)。预期得到上面那行 ✓ Unique scope,而不是任何提示。
可能的方向(供分诊,不预设)
- 把两种情形分开:
import()单独 try(可选包缺失 = 静默,现状正确),list()的抛错另行处理; list()失败时产出一条常规HealthCheckResult(os doctor的 config 分析 catch 丢弃了 error 本身 —— 配置真坏掉时只说「载入不了」,不说为什么 #5403 刚把renderHealthCheckResult()提成具名函数,这条路径可以直接复用),措辞按 fix(cli):os doctor指名道姓报告非法OS_TENANCY_POSTURE并非零退出 (#5382) #5390 体例引上游原话;- 档位:大概率 warning(环境仍能跑),但「读不到 ledger 时 D5e 建议是否还能声称 clean」需要判定 —— 这一条是本单真正的决策点,不是实现细节。
备注
- 已按关键词 + 文件路径搜过 open issues(
doctor/installed-packages/ledger),未见重复;唯一命中是os doctor的 config 分析 catch 丢弃了 error 本身 —— 配置真坏掉时只说「载入不了」,不说为什么 #5403 自身。 - 与
os doctor的 config 分析 catch 丢弃了 error 本身 —— 配置真坏掉时只说「载入不了」,不说为什么 #5403 无依赖关系,独立成单(os doctor的 config 分析 catch 丢弃了 error 本身 —— 配置真坏掉时只说「载入不了」,不说为什么 #5403 的 PR 只动 config 分析那个 catch,不触及本函数)。
Activity
baozhoutao commentedon Aug 5, 2026
分诊(cli 车道 PM,
session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05):入队pm:queue,domain:cli。判级:具名位置的真缺陷 —— false PASS 比漏报更糟(操作者会停止查看),属「门禁必须真的检查它宣称检查的东西」恢复不变量类,PM 直接裁定不上交。方向裁定(留否决窗口):正文的决策点「ledger 读不到时 D5e 建议是否还能声称 clean」按仓规「Absence must be loud」裁定为不能 ——
✓ Unique scope在 ledger 不可读时不得打印,改为一条常规HealthCheckResult(warning 档位:环境仍能跑),指名「已装包清单读了一半/没读到」,cause 引上游原话(#5390 体例)。实现拆开两个 catch:import()缺失保持静默(现状正确),list()抛错走上述路径,复用 PR #5410 刚提出的renderHealthCheckResult()。⛔ 串行约束:与 PR #5410(#5403)同文件
doctor.ts,须待 #5410 合并后基于新 main 派发(复用其渲染器导出)。此为 step 3 已知陷阱记录,勿在 #5410 落地前认领。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
认领:PM 循环第 2 轮(cli 车道)
会话:
session_016FNvXhtSdnEGEfLEsMmvxh分支:
claude/issue-5412-doctor-ledger-read-failureWorktree:
objectstack-issue-5412域:
domain:cli文件面:
packages/cli/src/commands/doctor.ts+packages/cli/src/commands/*.test.ts串行约束已解除:PR #5410(#5403)已合并(
533d73f),本单基于合并后的 main 派发,复用其renderHealthCheckResult()。执行口径沿 09:1xZ 分诊裁定:拆开两个 catch(import()缺失静默不变,list()抛错产出常规HealthCheckResult,warning 档位,cause 引原话);ledger 不可读时✓ Unique scope不得打印。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
验收(cli 车道 PM,
session_016FNvXhtSdnEGEfLEsMmvxh):ACCEPT → PR #5414。公开认账一条前提更正:本单正文的复现路径(截断 JSON 条目)经 dev 以真实实现探针证伪 ——
LocalManifestSource.list()自带 per-file catch,条目级损坏在 producer 层就被吸收,list()成功返回短列表,够不着 doctor 的 catch。真正到达该 catch 的是目录级失败(ENOTDIR / 文件系统拒绝)。核心判定(catch 混淆两事、产出 false PASS)仍成立,PM 裁定口径对可达的那一半一字未改地适用;PM 在分诊时按正文复现路径写的测试要求 ① 由 dev 据实修正(ENOTDIR 触发)—— 这是 dev 纠正 issue 与 PM 的正确行为。import()静默保留;目录读取失败携 cause 返回,返回类型改{ entries, failure? }消灭空数组歧义。✓ Unique scope是对建议两半的断言,仅两半都跑过才打印;ledger 失败时以 warning 档HealthCheckResult顶替,占用同一名字列(另起名字 = 沉默换顶帽子,判断正确),复用 fix(cli):os doctor说出配置载入失败的原因,不再只说一句「载入不了」(#5403) #5410 渲染器,cause 原话照引。⚠ SCOPE BOUNDARY用例,注明LocalManifestSource.list()静默丢弃损坏的 ledger 条目 —— 已装应用在 boot 时消失、在控制台列表里缺席,且没有任何一条日志 #5413 落地时应转红),而不是改测试凑通过 —— 这正是要的诚实姿态。LocalManifestSource.list()静默丢弃损坏的 ledger 条目 —— 已装应用在 boot 时消失、在控制台列表里缺席,且没有任何一条日志 #5413(producer 静默丢条目,后果比 doctor 重:已装应用 boot 消失零日志)已确认存在,另行分诊。下一步:CI 全绿后转 ready 入合并队列,跟到 MERGED。
Generated by Claude Code
fix(cli): `os doctor` 不再在 ledger 读不到时打出 `✓ Unique scope` (objectstack…
fix(cloud-connection,cli): `LocalManifestSource.list()` reports the l…
test(cli): make the doctor ledger e2e block fail legibly when cloud-c…
fix(cli): `os doctor` tells a broken cloud-connection install from an…