Skip to content

os doctor 的 installed-package ledger 读取 catch 把「损坏」和「没装」当成同一件事 —— D5e 建议因此静默少报,并打出「clean」 #5412

Description

@baozhoutao

实施 #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 不带绑定,且覆盖了两件性质完全不同的事:

  1. import('@objectstack/cloud-connection') 解析不到 —— 可选包没装。注释写明了这一条,而且是对的:doctor 必须能在没有该包的 checkout 里跑完。
  2. 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,而不是任何提示。

可能的方向(供分诊,不预设)

备注

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions