Skip to content

fix(verify,qa): 从 host app 解析企业版 organizations 包 —— 共享解析器落到 @objectstack/types/node (#4700) - #4857

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-4700-host-resolver-shared
Aug 3, 2026
Merged

fix(verify,qa): 从 host app 解析企业版 organizations 包 —— 共享解析器落到 @objectstack/types/node (#4700)#4857
os-zhuang merged 3 commits into
mainfrom
claude/issue-4700-host-resolver-shared

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4700

承接 #4699 / cloud#1013 的同一缺陷类:framework 侧的包用import() 加载 host app 提供的可选包。Node ESM 对裸 specifier 按导入方自己的 realpath 解析,而 packages/verifypackages/qa/dogfood 的 realpath 都在 framework 工作区里;@objectstack/organizations 是 cloud 私有包,只存在于被验证/被测 app 的 node_modules。于是这两处永远解析不到

共享位置:@objectstack/types/node(新增公开导出面)

createHostRequire / createHostImporterpackages/cli/src/utils/import-from-host.ts 迁到 packages/types/src/node.ts,CLI 改为消费它,私有副本连同其测试一并删除 —— 一个行为一个源。

为什么是子路径而不是根导出。 @objectstack/types@objectstack/hono 的依赖,后者的自我定位是 "edge-compatible REST API server for Cloudflare Workers, Deno, Bun, and Node";LiteKernel 在 Workers 上启动的 plugin/service 层同样依赖它。根入口今天可达的 node: 内建数为 0,这是那些消费方赖以成立的性质:Workers bundle 里出现 node:module,哪怕一次都不调用也会构建失败。而这里的东西按定义必须用 node:module / node:url(它存在的意义就是驱动 Node 自己的解析器),所以它住在自己的入口后面。

隔离是结构性的,不是约定性的:

  • packages/types/tsup.config.tssrc/index.tssrc/node.ts 编成两个独立入口,splitting: false,根 bundle 里不含对 node 切片的任何引用(与 @objectstack/metadata 已经在用的 ./node 形状完全一致,配置也逐字相同,只多一个 entry)。
  • packages/types/src/node-isolation.test.ts 走真实的 import 图,从 src/index.ts 出发,遇到第一个可达的 node: specifier 就失败;并且额外钉住「src/node.ts 确实还在用 node 内建」,避免解析器哪天不用 node 内建了、前两条空转通过、子路径看起来仍然正当。

实测产物:dist/index.mjsnode: 出现 0 次,dist/node.mjs 则是完整的 node 切片。

bootStack / objectstack verify --multi-tenant

harness.ts 改用共享解析器,并新增 BootOptions.hostRoot(可选,默认 process.cwd())指明「提供可选包的那个 app」。报错文案也从 "Install/link it in this workspace"(指向 framework checkout —— 唯一装了也没用的地方)改成点名 app 目录的 "Install/link it in THIS APP (路径)"。

qa dogfood:两条从没跑过的门(验收第 4 条)

两个 dogfood 文件各抄了一份同样的裸 import() 探测,所以 organizationsAvailable恒假的 —— 不是「因为包不在所以假」,而是构造上就不可能为真,在任何环境下都如此,包括注释里声称「会跑这条」的 cloud CI。结果是 #1994 跨租户 RLS 证明、以及 attachments 跨租户隔离 (g) 两个 block 从未执行过,而套件一直是绿的。这就是 Prime Directive #10「declared ≠ enforced」的测试版本,一个恒假的能力探测比没有探测更糟 —— 它制造了覆盖率的假象。

packages/qa/dogfood/test/enterprise-organizations.ts 取代了这两份副本:

  1. 按运行时的方式解析 —— 走同一个 @objectstack/types/node,探测因此能够为真了。
  2. 让 skip 可证伪 —— OS_TEST_MULTI_ORG_ENABLED=1 声明「这一次运行本应带着这个包」。声明了却解析不到 = 硬失败,而不是静默跳过;没声明且解析不到才跳过,且警告里点名了那个开关。

诚实地说明现状:在 framework 仓里这个 cloud 私有包确实装不上,所以这两条门在本仓仍然 skip —— 这是正确且不可避免的。改变的是,skip 现在是「环境的事实」而不是「解析器的产物」,而带着包的 cloud/enterprise 运行会真的执行这两个 block(并且在它以为自己带了包却没带时大声失败)。

#4719 的关系(重要)

#4719 说的是 createHostRequireNODE_PATH、于是「host app 必须自己声明」这条契约在 pnpm 工作区里没被强制。本 PR 不碰那个行为:两个函数是逐字搬运的,可执行代码与 origin/main 上的版本完全一致(diff 只有注释措辞:CLI → importing package)。#4719 的缺陷既没被修,也没被放大 —— 只是换了住址。修它的时候请落在 packages/types/src/node.ts,那里现在是唯一的源。

验证

全部在共享 verify 锁下、--max-old-space-size=4096--maxWorkers=2、按包 --filter 跑。

命令 结果
pnpm --filter @objectstack/types build 两个入口都产出,DTS 通过
pnpm --filter @objectstack/types typecheck 通过
pnpm --filter @objectstack/types test 6 files / 81 tests passed
pnpm --filter @objectstack/verify typecheck 通过
pnpm --filter @objectstack/verify test 3 files / 10 tests passed
pnpm --filter @objectstack/verify build 通过
pnpm --filter @objectstack/cli typecheck 通过
pnpm --filter @objectstack/cli test 66 files / 581 tests passed
pnpm --filter @objectstack/dogfood test(全量 82 文件) 81 passed / 1 skipped,473 tests passed / 3 skipped,266s
eslint --no-inline-config(改动文件) 干净
pnpm install --frozen-lockfile 通过
check-changeset-fixed / check-override-consistency 通过

新测试不是空转的 —— 实测过。harness.ts 临时改回裸 import() 后重跑:

× harness.host-resolution.test.ts > mounts the enterprise plugin installed in the APP, not in the framework workspace
  → verify: multiTenant=true requires the enterprise @objectstack/organizations package …
    Install/link it in THIS APP (/tmp/os-verify-org-host-ok-MluWHa) to run multi-org fixtures.
    (Cannot find package '@objectstack/organizations' imported from …/packages/verify/src/harness.ts)

app 目录里明明装着这个包,报错却说找不到 —— 这正是 issue 描述的症状,逐字复现。改回修复后三条全绿。

验收第 2 条(vi.mock 仍然生效)实测确认:harness.posture.test.ts 3 条全绿 —— host 解析不到 → 回退裸 import → mock 命中,这条链路没被改动破坏。

验收第 4 条(那条分支到底跑没跑起来)实测确认:在 packages/qa/dogfood/node_modules/ 里放一个一次性替身包(不入库,实验后已删除),让探测得以为真:

✓ rls-multitenant.dogfood.test.ts > CRM multi-tenant (#1994 org-scoped) > org-scoping engaged: at least one object is rls-consistent
✓ rls-multitenant.dogfood.test.ts > CRM multi-tenant (#1994 org-scoped) > has ZERO by-id-write RLS holes across org boundaries
  ── 4 consistent, 0 HOLES, 0 member-visible, 2 skipped
✓ attachments-permission-matrix.dogfood.test.ts > attachments cross-tenant isolation (g) >
    a member outside the admin org cannot read or delete the admin's attachments through /data  622ms

也就是说:那两条分支确实跑起来了,并且是通过的(boot 日志里 posture: "isolated",证明围墙姿态真的生效了)。反向也钉住了 —— 移除替身包后带 OS_TEST_MULTI_ORG_ENABLED=1 重跑,如期硬失败:

Error: OS_TEST_MULTI_ORG_ENABLED=1 declares that @objectstack/organizations … is installed for this run,
but it could not be resolved from …/packages/qa/dogfood. Refusing to skip the multi-org dogfood gates silently…
Test Files 1 failed (1) / Tests no tests

不带该环境变量时按预期 skip,并打印点名开关的 stderr 警告(默认 reporter 会折叠全跳过文件的 stderr,--reporter=verbose 下可见 —— 这是 vitest 的 reporter 行为,与改动无关;真正的强制手段是上面那条硬失败,它不依赖任何控制台输出)。

全量 dogfood 里那 1 skipped 文件 / 3 skipped tests,正是上面说的两条 enterprise 门(RLS 2 条 + attachments (g) 1 条)—— 数字对得上,没有别的东西在偷偷跳过。

顺带发现(未在本 PR 修)

packages/spec/** 零改动。

🤖 Generated with Claude Code

https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny


Generated by Claude Code

zhuangjianguo and others added 3 commits August 3, 2026 09:02
…st app (#4700)

Node ESM resolves a bare `import()` against the importer's own realpath.
`packages/verify`'s realpath is inside the framework workspace, while
`@objectstack/organizations` is cloud-private and only ever lives in the
verified app's `node_modules` — so `bootStack({ multiTenant: true })` fell into
its catch on every real host app and told operators to install a package they
had already installed. Same defect class as cloud#1013 (#4699 fixed `serve`).

Move the host-app resolver out of the CLI, where verify and the dogfood suite
could not import it without inverting the dependency direction, into a node-only
subpath export of @objectstack/types:

- `@objectstack/types/node` — new subpath, NOT the root export: the root is a
  dependency of the edge-targeted `@objectstack/hono` and reaches zero `node:`
  builtins. tsup emits both entries self-contained (`splitting: false`), and
  node-isolation.test.ts walks the root import graph and fails on the first
  reachable `node:` specifier, so the split is enforced, not just intended.
- CLI consumes it; its private copy and that copy's tests are deleted.
- `BootOptions.hostRoot` (optional, defaults to cwd) names the app supplying the
  optional packages.
- Dogfood: both multi-org probes were CONSTANT-FALSE by construction, so the
  #1994 cross-tenant RLS gate and the attachments isolation block had never
  executed while the suite was green. They now resolve like the runtime does,
  and OS_TEST_MULTI_ORG_ENABLED=1 turns an unexpected skip into a hard failure.

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

vercel Bot commented Aug 3, 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 3, 2026 10:20am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling size/l and removed documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/dogfood, @objectstack/types, @objectstack/verify.

23 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/permissions/authorization.mdx (via packages/qa/dogfood)
  • content/docs/permissions/delegated-administration.mdx (via packages/qa/dogfood)
  • content/docs/plugins/index.mdx (via @objectstack/cli)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/types)
  • 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, @objectstack/verify)
  • content/docs/releases/v15.mdx (via @objectstack/verify)
  • content/docs/releases/v16.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

复核通过 —— ACCEPT,已标 ready 并送合并队列

本单是接手一个被宿主上下文压缩机械性中断的 agent 的成果(那次中断与任务本身无关)。接手方做了正确的事:通读上一版的实质提交、确认四条验收全覆盖、不推倒重来也不做无谓改动,然后补上前一版一次都没跑过的验证。

验收第 4 条 —— 我派发时说"这是最像测试在说谎的一处",它被真正回答了

我要求的是:修好之后那条分支是否真的跑起来了,跑起来之后是过还是不过。不是"我改了解析器所以应该好了"。

dev 在 node_modules 里放了一次性替身包把探测翻真(实验后已移除并核对目录完好),那两道 enterprise 门确实执行并通过:

✓ CRM multi-tenant (#1994 org-scoped) > org-scoping engaged…
✓ … has ZERO by-id-write RLS holes across org boundaries
── 4 consistent, 0 HOLES, 0 member-visible, 2 skipped
✓ attachments cross-tenant isolation (g) > a member outside the admin org
    cannot read or delete the admin's attachments through /data   622ms

boot 日志里 "posture":"isolated" 证明围墙姿态真的生效,而不是探测过了、门却空转。

并且它把这条路堵死了:叠了一个声明式开关 OS_TEST_MULTI_ORG_ENABLED=1,反向实验里移除替身包后直接硬失败——

Error: OS_TEST_MULTI_ORG_ENABLED=1 declares that @objectstack/organizations …
       Refusing to skip the multi-org dogfood gates silently…

这正是 Prime Directive #10 要的形状:一个恒假的探测比没有探测更糟,而现在"声称要跑却跑不了"会当场红掉,不再是静默 skip。数字也对得上:全量 82 文件 81 passed | 1 skipped,那 1 个 skipped 文件 + 3 条 skipped 用例正好等于两道 enterprise 门。

反空洞验证也做了

harness.ts 临时改回裸 import() 重跑,issue 症状逐字复现:app 里明明装着,却报 Cannot find package '@objectstack/organizations'。改回后 3 条全绿。证明新测试钉住的是真缺陷,不是恰好能过。

隔离是结构性的,不是靠自觉

@objectstack/types 根入口零 node: 内建,由 node-isolation.test.ts 走真实 import 图强制(不是 grep 源码),加 tsup splitting: false。产物实测 grep -c 'node:' dist/index.mjs → 0。派发时我要求"明确说明为什么不是根导出",这个回答比说明更强——它让根导出不可能被污染。

changeset 等级正确

@objectstack/types: minor(新增 ./node 子路径导出)、@objectstack/verify: minor(新增 BootOptions.hostRoot)、@objectstack/cli: patch(仅消费,无新公共面)。与本仓「新增包根导出面 ⇒ minor,无新公共 API ⇒ patch」的做法一致。

边界守住了

packages/spec 零改动(git diff --stat origin/main...HEAD -- packages/spec/ 为空)。NODE_PATH 行为逐字搬运未改 —— 我派发时明确要求不碰(那是 #4719 的车道),核实为可执行代码与 origin/main 完全相同,只有注释措辞变了。#4855(dogfood 缺 typecheck script、已积 14 个类型错误)单独立单未夹带,且逐一确认全部先于本 PR 存在。

一处判断我认可它没有立单

tsup target: 'es2020' 会把 dist/node.mjs 里的 node: 前缀剥成裸内建名。dev 判为非缺陷:与既有先例 packages/metadata/tsup.config.ts 逐字一致、Node 对裸内建名的解析优先级高于 node_modules、只有把 /node 入口拿去 Deno / Workers 打包才有影响——而按定义没人会那么做。这个判断对,不占用 backlog 是正确的克制;记录在报告里已足够。


⚠️ 连带影响已处理:本 PR 删除 packages/cli/src/utils/import-from-host.ts,而那是 #4719 的目标文件。已在 #4719 留告示,要求在本 PR 合并前不要认领,并说明修复落点移到 packages/types/src/node.ts、影响面从 CLI 一家扩大到 CLI + verify + dogfood 三家。本 PR 合入后我会重新分诊 #4719 的车道标签。


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit b25a116 Aug 3, 2026
23 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4700-host-resolver-shared branch August 3, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@objectstack/verify 的 bootStack 与 qa dogfood 同样用裸 import 解析 cloud 私有包 —— 与 cloud#1013 同一缺陷类

3 participants