跨分片移交:`Part of objectstack-ai/cloud#1020`(由 cloud 分片 PM 转入,本条**不含**那边正在等维护者拍板的许可闸门决策——banner 这半块无论决策怎么落都该修)。 ## 现象 `packages/cli/src/utils/format.ts:412-414`: ```ts if (opts.multiTenant !== undefined) { console.log(chalk.dim(` Tenancy: ${opts.multiTenant ? 'multi-tenant' : 'single-tenant'}`)); } ``` 打印的是布尔 `multiTenant`(即 `OS_MULTI_ORG_ENABLED` 那条线),而 ADR-0105 D1 之后权威 knob 是 `OS_TENANCY_POSTURE`,布尔只是它 unset 时的回退(`packages/types/src/env.ts` `resolveTenancyPosture()`)。serve 的实际接线读的是 `resolveTenancyPosture()`。 ## 实测证据(来自 cloud#1020 的 boot 日志,真实 CLI 非 mock) 设 `OS_TENANCY_POSTURE=isolated`、未设 `OS_LICENSE_KEY` 启动,一屏之内同时出现: ``` Tenancy: single-tenant ← banner 读布尔 Plugins: 40 loaded …, Organizations, … ← 企业多组织运行时真的挂上了 ``` banner 说单租户,实际跑的是 isolated + 真墙。 ## 为什么值得修 诊断信息一旦不可信,之后每一次排障都要多绕一圈——cloud#1020 就是靠人工比对插件表才发现 banner 在撒谎的。这不是显示美化,是**声明与执行不一致**(ADR-0049 那一类)在诊断面的实例。 ## 修法 `Tenancy:` 改为读 `resolveTenancyPosture()` 的结果并直接打印 posture 名(`single` / `isolated` / `group` …),而不是把它压扁成一个布尔。调用点需要把 posture 传进 `opts`(今天传的是 `multiTenant` 布尔),是否保留布尔字段作为兼容项由实施者判断;若保留,两者不一致时应以 posture 为准。 建议附一条断言 banner 输出与 `resolveTenancyPosture()` 一致的测试,否则下一个 knob 演进时同样会漂。 ## 关联 - 来源:objectstack-ai/cloud#1020(EE 许可闸门只钉布尔,`OS_TENANCY_POSTURE=isolated` 可绕过) - ADR-0105 D1(posture 取代布尔) 未指派——记录的 finding,谁开工谁认领。
Activity
xuyushun441-sys commentedon Aug 3, 2026
认领(PM 分派)
session_018iARDqtrhQgz6fVHDeDkbQclaude/issue-4801-banner-tenancy-posturedomain:cli(落点packages/cli/src/utils/format.ts及其调用点)与同批 #4835 / #4843(均在
scripts/)落点互斥。本单只做 banner 这半块,不触碰 cloud#1020 那边仍在等维护者拍板的许可闸门决策 —— 正如本单开头所写,banner 无论那个决策怎么落都该修。Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
审核通过 — PR #4994 已取消草稿并挂上 auto-merge(SQUASH)
布尔字段的处置:删除,三条理由都成立,其中第二条最有分量 —— 删除把错误接线从运行期挪到编译期:
tenancyPosture是TenancyPosture类型,resolveMultiOrgEnabled()返回 boolean,把 banner 接回旧 knob 现在无法编译,而不是产出一行看似合理的错话。本 bug 当初被写出来的方式正是"调用方看到multiTenant字段就合理地填了个布尔" —— 留一个永远只能被忽略的字段,等于给下一次同样的错误留门。影响面核实为零(printServerReady全仓唯一调用点,ServerReadyOptions包内部接口;verify.ts的multiTenant属bootStack()选项,按边界正确未动)。测试断言的是性质而非样例:「打印出的 token 就是
resolveTenancyPosture()的答案」,覆盖三种矛盾场景、legacymulti拼法与group(布尔根本没有group的拼法 —— 这也是三值谱系压不进布尔的直接证据)。@ts-expect-error也做了承重验证:删掉一条,tsc 立刻报 TS2322。dev 抓了自己一条空转测试并如实说明:第一轮反向验证里「never flattens into multi-/single wording」在改前也通过 —— 因为改前根本没有 Tenancy 行,
not.toMatch空转。改成先断言行存在再断言措辞,并在 PR 里写明那一轮输出是加强前的版本。一个在改前也绿的断言什么都不证明 —— 这条纪律本会话已重复到第九次,这次是 dev 自查自纠。worktree 里 typecheck 的 TS2307 噪音用"stash 全部改动、在等同 origin/main 的树上逐字复现"隔离干净,权威一轮交给 CI —— 目前无红,含新上线的 Console Pin Freshness(advisory)也绿。
落点 4 文件全在
packages/cli,零共享文件。#4855(qa/dogfood tsconfig)不再被本单阻塞,#4719 仍在飞,等它落地后一并派。Generated by Claude Code
fix(cli): boot banner reports the resolved tenancy posture, not the l…