Skip to content

CLI 启动 banner 的 Tenancy: 读布尔 multiTenant,与 ADR-0105 D1 的权威 knob OS_TENANCY_POSTURE 对不上——isolated 会显示成 single-tenant #4801

Description

@xuyushun441-sys

跨分片移交:Part of objectstack-ai/cloud#1020(由 cloud 分片 PM 转入,本条不含那边正在等维护者拍板的许可闸门决策——banner 这半块无论决策怎么落都该修)。

现象

packages/cli/src/utils/format.ts:412-414:

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

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    认领(PM 分派)

    • session:session_018iARDqtrhQgz6fVHDeDkbQ
    • 分支:claude/issue-4801-banner-tenancy-posture
    • 车道:domain:cli(落点 packages/cli/src/utils/format.ts 及其调用点)

    与同批 #4835 / #4843(均在 scripts/)落点互斥。本单只做 banner 这半块,不触碰 cloud#1020 那边仍在等维护者拍板的许可闸门决策 —— 正如本单开头所写,banner 无论那个决策怎么落都该修。


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    审核通过 — PR #4994 已取消草稿并挂上 auto-merge(SQUASH)

    布尔字段的处置:删除,三条理由都成立,其中第二条最有分量 —— 删除把错误接线从运行期挪到编译期:tenancyPosture 是 TenancyPosture 类型,resolveMultiOrgEnabled() 返回 boolean,把 banner 接回旧 knob 现在无法编译,而不是产出一行看似合理的错话。本 bug 当初被写出来的方式正是"调用方看到 multiTenant 字段就合理地填了个布尔" —— 留一个永远只能被忽略的字段,等于给下一次同样的错误留门。影响面核实为零(printServerReady 全仓唯一调用点,ServerReadyOptions 包内部接口;verify.ts 的 multiTenant 属 bootStack() 选项,按边界正确未动)。

    测试断言的是性质而非样例:「打印出的 token 就是 resolveTenancyPosture() 的答案」,覆盖三种矛盾场景、legacy multi 拼法与 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions