Repository navigation
Closed
Labels
bugSomething isn't workingSomething isn't workingdomain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
Description
发现于 #5269(content/docs/deployment/tenancy-modes.mdx 文档核证)过程中,逐断言对码时撞到;本单只记录,不在该文档 PR 里改。
声称的契约
packages/types/src/env.ts:145-160 —— resolveTenancyPosture() 对无法识别的值抛错,错误文案自称:
Invalid OS_TENANCY_POSTURE="bogus". Expected one of: single, group, isolated (or the legacy alias 'multi' = 'isolated').
Refusing to boot rather than silently falling back to a posture with no organization wall.
content/docs/deployment/environment-variables.mdx:85 也照此写:「An unrecognized value refuses to boot」。
实际发生的顺序(静态追踪,origin/main @ c89d18c)
packages/cli/src/commands/serve.ts:1772const tenancyPosture = resolveTenancyPosture();位于 AuthPlugin 那个大try内(更早的kernel.use(new AuthPlugin(...))@serve.ts:1692也会经packages/plugins/plugin-auth/src/auth-plugin.ts:470触发同一次解析)。- 该
try的 catch 在serve.ts:1942-1948,只打印一句黄字⚠ AuthPlugin failed to load: <msg>就继续。于是非法 posture 的第一次抛错被吞,启动继续进行,只是没有 plugin-auth。- 这一点代码自己就写明了:
serve.ts:1816-1819的注释说「this catch sits inside the broad AuthPlugin try below, which swallows errors — a throw would be caught and boot would silently continue degraded」,D5 闸门因此特意用process.exit(1)而不是 throw。posture 解析的抛错没有这层保护。
- 这一点代码自己就写明了:
await runtime.start()@serve.ts:2560—— HTTP 服务已经起来并绑定端口。printServerReady({ ..., tenancyPosture: resolveTenancyPosture() })@serve.ts:2698-2716—— 同一个解析器第二次抛错,这次不在 AuthPlugin 的 try 里,落到外层 catchserve.ts:2764-2773,printError+this.exit(1)。
结论
退出码这一半是对的(最终 exit 1),但「refusing to boot」的另一半不成立:
- 进程在报错前已经 listen 过;
- 运维看到的第一条信号是「AuthPlugin failed to load」,把一个 env 拼写错误伪装成插件加载问题;
- 真正的
Invalid OS_TENANCY_POSTURE=...只在最后由通用printError抛出,不是 D5 那种带修法清单的显式拒绝。
排查成本之外还有一个更实际的担忧:这条路径依赖「banner 一定会执行」这一点来兜底。任何让 printServerReady 不被执行的分支(或把 banner 的 posture 读取改成缓存值/容错读)都会把它变成「非法 posture 静默按 single 跑」——正是 env.ts 那句话要防的形态。
建议修法(不在本单实现)
在 serve 早期、任何 try 之外解析一次 posture 并显式 process.exit(1),与 ADR-0093 D5 闸门同一风格(有修法清单的 FATAL 文案),后续调用复用该值。
现状证据
packages/cli/** 下没有任何测试钉住这个行为:git grep -n "Invalid OS_TENANCY_POSTURE" packages/ 只命中 packages/types/src/env.ts:154。
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingdomain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
Activity
baozhoutao commentedon Aug 5, 2026
🔒 认领:PM 循环第 1 轮(cli 车道)
会话:
session_01VkPSGsX9o17MsGv3Lbxu2w分支:
claude/issue-5359-serve-posture-early-exitWorktree:
objectstack-issue-5359域:
domain:cli文件面:
packages/cli/src/commands/serve.ts+ 钉行为的测试 + changeset。⛔ 不动packages/types/src/env.ts的解析器本体(其文案与语义是对的,错的是调用时机)。与同批 #5000(validate/build 路径)、#5155(runtime dispatcher)不相交。执行口径:按 issue 建议修 ——
serve早期、任何try之外解析一次 posture,非法值走 ADR-0093 D5 同风格的显式process.exit(1)(FATAL + 修法清单文案),后续调用复用该值;补一条钉「监听发生前拒绝」的测试(现状git grep "Invalid OS_TENANCY_POSTURE" packages/仅命中 env.ts 声明处,零测试)。同日 churn:
serve.ts所在的packages/cli昨日被 #5314 / #5302 触及,issue 观测基线为c89d18c16—— 以origin/main为准先核对行号与结构。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
✅ 验收通过 —— PR #5381,CI 全绿,转 ready 入合并队列
对 GitHub 实况复核(cli 车道 PM,session_01VkPSGsX9o17MsGv3Lbxu2w):
serve.ts(+142/−2)+ 新测试 258 行 + changeset(patch),零releases/触碰,packages/types/src/env.ts按裁定未动。dotenvFlow.config()之后、所有try之外解析一次;拒绝以 verdict 返回值而非 throw 传递(throw 正是当初被宽 catch 降级的形态)—— 这是本修复的核心属性,且被测试钉住;修法清单由TENANCY_POSTURES生成,不会随词表演进过期;后续读取(组织插件判断、横幅)复用同一值。公开订正本单前提(dev 实测推翻,PM 采纳):标题与正文所称「拒绝发生在端口已监听之后」不成立 —— 逃逸抛错来自 kernel bootstrap 阶段一的 ObjectQL
SchemaRegistry构造,监听套接字在阶段四才打开;TCP 探针实测修复前后端口都从未绑定(对照组证明探针有效)。真实缺陷是:①归因错误(env 拼写错报成 AuthPlugin 加载失败);②带伤启动的副作用(dev crypto key 落盘);③兜底者是 ObjectQL 构造这种顺带闸门。修复把拒绝交给刻意闸门,三点全部消除。dev 以更强的「零启动副作用」顺序断言替代派发词里要求的「listen 前拒绝」断言(后者修复前也为真,恒过断言无证明力)—— 偏离成立,PM 认可。os doctor对非法OS_TENANCY_POSTURE退出码 0 并报告「环境功能正常」—— 抛错被 config 分析的宽 catch 吞成一句「Could not load config」 #5382(os doctor对非法 posture exit 0 报「功能正常」),已由 PM 定级入队。PR 转 ready 并挂合并队列,跟到 MERGED 为止。
Generated by Claude Code
test(cli): give the in-process serve ordering case a 60s timeout (#5359)
fix(cli): 非法 OS_TENANCY_POSTURE 在 serve 最开头被显式拒绝,不再伪装成 AuthPlugin 加载失败 (