Skip to content

fix(cli): os doctor tells a broken cloud-connection install from an absent one (#5644) - #5658

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-5644-doctor-import-boundary
Aug 5, 2026
Merged

fix(cli): os doctor tells a broken cloud-connection install from an absent one (#5644)#5658
baozhoutao merged 2 commits into
mainfrom
claude/issue-5644-doctor-import-boundary

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5644

按分诊 21:56Z 裁定的方案 A(先解析再加载)实现。B(用 ledger 目录在场做代理)、C(维持现状写成已知取舍)均未采用。

前提复核:成立(先证后改)

在已构建的工作树里,把 @objectstack/cloud-connection 置为「在场但求值抛错」,并放一份声明了 installation-wide unique 的 ledger 条目,OS_TENANCY_POSTURE=isolatedos doctor --verbose:

  → Checking unique scopes against the 'isolated' tenancy posture...
  ✓ Unique scope          No unconfirmed installation-wide uniques for this 'isolated' environment

invoice.code 这条本该出现的 finding 一行都没有,连 --verbose 也没有。issue 正文描述的假 PASS 在 origin/main 上逐字复现。

判别机制:为什么是 import.meta.resolve,不是 createRequire().resolve()

Node 22.22 上对真包实测的四态(见 packages/cli/src/utils/optional-package.ts 头注释):

状态 import.meta.resolve() createRequire().resolve() import()
包不在
包在、dist/ 缺失(未构建/被裁剪) 返回 URL
包在、入口求值抛错 返回 URL 返回路径
健康 返回 URL 返回路径 加载

第二行是关键,也是不能用 CJS 解析的两个理由:它会 stat 入口文件,于是这一行塌回「没装」——而这恰是 issue 正文那条复现路径(mv packages/cloud-connection/dist /tmp/x)所在的态;它还按 require 条件解析,回答的是另一个产物(index.cjs),而 import() 加载的是 index.jsimport.meta.resolve 回答同一个产物,且不 stat,所以「包在不在」与「入口能不能加载」保持两个问题。

判别分两步,两步都复用仓内既有的所有者:

  1. 抛出的不是 module-not-found 错误 → 一定是包自己抛的,不必再问解析器。isModuleNotFoundError()(@objectstack/types,framework#3265 指定的单一所有者,serve.ts 同样读它)。
  2. 是 module-not-found 错误 → 仍然覆盖两个世界,只有解析器能分开(上表第一、二行)。

解析只在 import() 已经失败之后才调用:happy path 零成本,且判定「加载是否成功」的始终是加载本身,不是解析器。无 import.meta.resolve 的运行时(本包 engines 是 Node 22+,不会发生)只保留第 1 步的确定性,含混态一律归 absent——宁可退回改动前的沉默,也绝不在从未装过该包的 checkout 里误报。这条硬约束原样保留。

报法

新增第三个同族行,与 #5412 目录级行、#5413 条目级行同档:Unique scope 名列、warning、退出码不变、错误原文入 --verbose 详情,走 renderHealthCheckResult(#5410)。

  ⚠ Unique scope          Could not load the installed-package ledger reader (installed packages
                          NOT checked for installation-wide uniques) — Cannot find module …

措辞与 #5412 那行刻意区分(loadread,且不是其超串),因为事实与药方都不同:修的是 @objectstack/cloud-connection 的安装,不是 ledger。这一行也刻意.objectstack/installed-packages/ 是否存在为条件——ledger 目录名是那个加载不了的包自己的导出常量,doctor 无从断言「没有 ledger」;拿目录在场当条件正是被否决的方案 B。

代价说清楚:未构建 packages/cloud-connection 的仓内工作树,isolated 下跑 doctor 会多这一条 warning。而这个态的沉默,正是 #5612 追查一个从未回退的报告面的起点。

测试(三态各就各位)

反向验证(方向先判后跑,两处都是 before-green/after-red)

  1. optional-package.ts 的解析限支删掉(所有 module-not-found 一律判 absent)。预测:两条判别用例红,其余绿。实测:
× reports a RESOLVABLE module whose file is not there as broken, not absent
× reports a package whose own dependency is missing as broken, not absent
Tests  2 failed | 4 passed (6)
  1. 把 doctor 的 broken 分支还原成改动前的合并(broken 也当 absent)。预测:两条 e2e 红,absent 那条仍绿(它走接缝)。实测:
× withholds the clean bill and names the reader, with a ledger it never read
× quotes the load failure verbatim and keeps the row to one line
Tests  2 failed | 22 passed (24)

验收

  • pnpm --filter @objectstack/cli test:83 passed (83) / 825 passed (825)(改动前为 82 文件 / 812 例)
  • 两个改动文件单跑:30 passed (30)
  • pnpm --filter @objectstack/cli typecheck 绿;改动文件 eslint 零告警
  • node scripts/check-nul-bytes.mjs OK;改动文件另做控制字节自扫,无命中
  • changeset:@objectstack/cli patch(行为变化:doctor 对坏包新增 warning 行)

边界

未动 packages/cloud-connection 本体、未动 serve.ts(它对同一个包的点名告警只是仓内对照读法)、未动 packages/client/**(#5638 在飞)。#5429(posture 门禁触发条件)现象不同,未收敛、未触碰。

一处界外观察,未新立单:在干净工作树里跑一次构建会重写 packages/spec/authorable-surface.base.json(baseRev + 三个 EmailServiceConfig 键)。已按关键词查重,命中既有 open 单 #5358 / #5370,同一现象,不另开;本 PR 已把该文件还原,不含在改动里。


🤖 Generated with Claude Code

https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh


Generated by Claude Code

… absent one (#5644)

`readInstalledPackageEntries()` reached the installed-package ledger through a
dynamic `import('@objectstack/cloud-connection')` whose `catch` meant "the
optional package is not installed". That covered two states with opposite
remedies: a specifier that does not resolve (genuinely absent — silence is
correct and stays), and a package that IS installed and will not load (unbuilt
or pruned `dist/`, interrupted install, an artefact that throws while it
evaluates). The second was answered with the first one's silence, so the
ADR-0120 D5e advisory saw "no installed packages" and printed
`✓ Unique scope` over a ledger nobody read — the false PASS #5412 removed at
the `readdir` boundary and #5413 at the entry boundary, one boundary up.

The two are separated by resolution, not by the `import()` having thrown:
`isModuleNotFoundError()` first (an error that is not module-not-found came
from the package itself), then `import.meta.resolve()`, which answers "is the
package there" without stating its entry file. New `utils/optional-package.ts`
owns the classification and the measurements behind it; doctor renders the
present-but-broken state as a `Unique scope` warning row through the same
renderer its two siblings use, and withholds the success line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 10:37pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli.

21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/automation/hook-bodies.mdx (via packages/cli)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/validating-metadata.mdx (via packages/cli)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/plugins/index.mdx (via @objectstack/cli)
  • content/docs/plugins/packages.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli)
  • content/docs/releases/v16.mdx (via @objectstack/cli)
  • content/docs/releases/v17.mdx (via @objectstack/cli)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Copy link
Copy Markdown
Contributor Author

CI 状态说明(交报告时点):快速门全绿——Check Changeset / Check PR Size / Auto Label / Flag docs affected by code changes / Console Pin Freshness / No other open PR may claim the same issue 均 success。重型作业(Build CoreTest Core (1-3/3)Dogfood Regression GateDogfood Verify CLIESLintTypeScript Type Check)自 22:38 起全部停在 in_progress 逾一小时、无一推进——是 runner 排队饱和,不是本 PR 的信号(若是本 PR 的问题,应有作业先跑完再报红,而不是连 ESLint 都不动)。

本地等价证据:pnpm --filter @objectstack/cli test83 passed (83) / 825 passed (825);typecheck 绿;改动文件 eslint 零告警。CI 收敛后如有红,请按 rework 回派。


Generated by Claude Code


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review August 5, 2026 22:41
@baozhoutao
baozhoutao enabled auto-merge August 5, 2026 22:41
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 214f67c Aug 5, 2026
24 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5644-doctor-import-boundary branch August 5, 2026 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants