Skip to content

fix(plugin-dev): 请求组织墙而企业包不可用时拒绝 init (ADR-0093 D5) (#5301) - #6053

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-5301-plugin-dev-d5-failfast
Aug 7, 2026
Merged

fix(plugin-dev): 请求组织墙而企业包不可用时拒绝 init (ADR-0093 D5) (#5301)#6053
qq9340100 merged 1 commit into
mainfrom
claude/issue-5301-plugin-dev-d5-failfast

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #5301

缺陷

DevPlugin 请求了有墙 tenancy posture(isolated / group)却加载不到企业 @objectstack/organizations 时,只打一条 logger.warn 就继续 boot。于是同一台机器上,同一个事实有两个相反的答案:

入口 请求 isolated、企业包缺失 结果
objectstack serve 拒绝启动(除非显式 OS_ALLOW_DEGRADED_TENANCY=1) 安全
DevPlugin(改前) warn 后继续 无墙服务流量,且没人显式同意过

ADR-0093 D5「请求了隔离就不得在没有隔离的情况下服务流量」是部署的性质,不是某一个入口的性质,所以 dev 装配路径欠同一个答案。#5262 让这条更容易被触发而不是更难:在它之前,只设 OS_TENANCY_POSTURE 的 dev 栈根本不进这个分支(那正是 #5262 本身的缺陷),修好读数之后它会进分支、会加载失败,然后正好走这条 fail-open 的路。

前提已按最新 origin/main(739f496)复核:dev-plugin.ts 的组织墙分支仍是单个 try + 只 warn 的 catch,无任何 hatch 闸。

改法

throw,不是 process.exit(1)

serve.ts 必须 process.exit,因为它那道闸嵌在会吞异常的 AuthPlugin try 里;DevPlugin库形态的装配插件,对宿主进程没有处置权,嵌入方(测试、脚本、父应用)有权 catch 它。而且它的 boot 链不吞异常——本 PR 已在当前 main 上复核该链仍成立:kernel.use() 只登记(kernel.ts:180),initPluginWithTimeout 只有 try/finally 没有 catch(kernel.ts:565),bootstrap() 的 catch 是 this.state = 'stopped'; throw error;(kernel.ts:405)。所以 throw 能真的中止 boot,与同文件 assertNotProduction() 的既有依据一致。

#4818 分两阶段,两种失败两种诊断

  • 阶段 1(import 失败 = 包缺席):OS_ALLOW_DEGRADED_TENANCY 生效。未设则拒绝 init,报文点名被请求的 posture 与全部出路;设了则照旧 warn 后降级继续,且该 warn 仍如实说明墙是 INACTIVE。判定用 resolveAllowDegradedTenancy()——serve.ts 同一个 resolver,两个入口对「显式同意」的定义因此不可能漂移(手写 === '1' 会让 true/on/yes 对 serve 有效、对 dev 无效,同一台机器同一份 .env)。
  • 阶段 2(construct / init 失败 = 包在、插件自己拒绝):hatch 不覆盖,一律中止。该 hatch 的含义始终是「这个能力缺席,我接受降级」,而不是「替我越过插件正在执行的闸」;放行会把插件的许可证/前置条件检查降格成一个环境变量。报文原样转述插件自己的说法(框架不解释、不猜原因),并明说这不是缺包问题,省掉一轮「去查安装」的排查。分类依据是哪一阶段抛的,刻意不是错误的形状——与 serve.ts 的理由相同。

阶段 2 在 DevPlugin 里比 serve.ts 多一处落点

serve 把插件交给 kernel.use(),其 Phase-1 循环会 rethrow init 失败,所以 serve 不需要为此写任何东西。而 DevPlugin 自己 init 子插件,那个循环刻意是 best-effort(记一条 error 继续,dev 栈才能在缺包时照常起)。对这一个子插件,best-effort 默认就是同一个 fail-open——墙没起来,进程照样服务流量。因此只让它单独例外,其余子插件的容错完全不变(有专门的回归用例钉住这条边界)。

若只镜像 construct 阶段而不管这处,本 PR 就会留下一个自己刚关掉的洞:包在、插件在 init() 里拒绝,依旧无墙继续跑。

测试

新增两个 suite,按「能不能诚实观察」切分:

  • dev-plugin-tenancy-failfast.test.ts —— 阶段 1。@objectstack/organizations 是云私有包、在本 workspace 里真实缺席,所以 import 真的失败、真实的 catch 分支真的跑,不 stub 任何被测对象。含:拒绝 init、报文点名 posture 与出路、hatch 放行后降级 warn 仍诚实、hatch 真值词表与 serve 一致、未调用 process.exit(这条断言能跑起来本身就是证明——process.exit(1) 会把 test runner 一起带走)、以及无墙 posture 完全不受影响。
  • dev-plugin-tenancy-mount-refusal.test.ts —— 阶段 2。「包在且拒绝」按定义无法在没有包的情况下观察,故 mock 仅提供 open-source workspace 不可能有的那一件东西(一个可解析的 @objectstack/organizations),被测的拒绝语义全部留在 dev-plugin.ts 里。含 construct 拒绝、init 拒绝、两者都不被 hatch 放行,外加一个正向对照(包在且健康则正常挂载)——没有它,整个文件可能只是靠「有墙 posture 一律抛」而通过,那就什么也没钉住。

已有的 #5262 suite 观察的是分支进入,它从 warn 那一行读这个信号;该路径现在 fail-fast,故其 beforeEach 显式设上 hatch 并写明理由——降级路径仍在同一行点名被请求的 posture,#5262 钉的信号一字未失。

pnpm --filter @objectstack/plugin-dev test    → Test Files 4 passed (4) / Tests 45 passed (45)
pnpm --filter @objectstack/plugin-dev typecheck → 干净

反向验证(方向为事前预测,非事后解释):预测「把 warn-only 块放回去,新增的拒绝类断言应转红」——即常规的红向,因为新用例断言的是旧代码从不产生的拒绝。实测:仅还原 dev-plugin.ts、保留全部新测试,Tests 11 failed | 34 passed,红的恰好是 11 条拒绝类断言;正向对照、best-effort 边界回归、无墙用例与整个 #5262 suite 全部保持绿。方向与预测一致。

消费半径已清扫:new DevPlugin( 全仓仅出现在本包内;examples/app-todoscripts/analytics-reconcile 等均显式 OS_MULTI_ORG_ENABLED='false'(single posture,不进分支),packages/qa/dogfood 为单组织 bootStack,packages/cli 的两个 organizations e2e 走的是 serve.ts

本地另跑通 CI ESLint job 内的家族闸:durability-log-levelstartup-registry-verdicterror-code-casingwildcard-fallthroughinit-service-contractroute-envelopeengine-double-contractservice-providerspublished-filesrole-worddoc-authoring 全绿,check:nul-bytes 与逐文件控制字符自扫描均干净,eslint packages/plugins/plugin-dev --no-inline-config 退出 0。

范围

⛔ 未改 packages/cli/src/commands/serve.ts(只读参照面)。已附 @objectstack/plugin-dev changeset,内含迁移说明:依赖静默降级的 dev 栈需显式设 OS_ALLOW_DEGRADED_TENANCY=1


Generated by Claude Code

DevPlugin 请求了有墙 posture 却加载不到企业 @objectstack/organizations 时,
只打一条 logger.warn 就继续 boot——同一台机器上,objectstack serve 对同一个
事实是拒绝启动。ADR-0093 D5 是部署的性质而非某个入口的性质,dev 装配路径欠
同一个答案。

改为 throw(不是 process.exit):DevPlugin 是库形态装配插件,对宿主进程没有
处置权;boot 链不吞异常(kernel.use 只登记、initPluginWithTimeout 不 catch、
bootstrap rethrow),与同文件 assertNotProduction() 的既有依据一致。

照 #4818 分两阶段:import 失败(缺包)受 OS_ALLOW_DEGRADED_TENANCY 管辖,
用的是 serve.ts 同一个 resolveAllowDegradedTenancy();construct/init 失败
(包在、插件自己拒绝)一律中止,hatch 不覆盖。阶段 2 在 DevPlugin 里多一处
落点——它自己 init 子插件的 best-effort 循环会吞掉 init 拒绝——故对这一个
子插件单独例外,其余子插件容错不变。

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

vercel Bot commented Aug 6, 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 6, 2026 3:45pm

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-dev.

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

  • content/docs/kernel/services.mdx (via @objectstack/plugin-dev)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-dev)
  • content/docs/releases/v17.mdx (via @objectstack/plugin-dev)

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.

@qq9340100 qq9340100 closed this Aug 7, 2026
@qq9340100 qq9340100 reopened this Aug 7, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 7, 2026 01:06
@qq9340100
qq9340100 added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 7e791e5 Aug 7, 2026
60 of 94 checks passed
@qq9340100
qq9340100 deleted the claude/issue-5301-plugin-dev-d5-failfast branch August 7, 2026 01:27
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

Development

Successfully merging this pull request may close these issues.

plugin-dev 没有 ADR-0093 D5 的 fail-fast:dev 栈请求了组织墙但企业包缺失时,只 warn 就继续跑无墙

2 participants