做 cloud#1118 批次 1(objectstack-ai/cloud#1184,把 cloud 仓测试文件接进 tsc)时量到。不在那个 PR 范围内(跨仓契约面),按 Prime Directive #10 单独记在这里,unassigned。
与 #5543 / #5227 同根因,与 #5507 / #5551 同形状(按 zod 文件分区登记 #4963 确立的 X / XParsed house convention)。本条是 spec/src/cloud/ 这个分区,此前没有单子覆盖。
事实
packages/spec/src/cloud/environment.zod.ts:310、cloud/tenant.zod.ts 等只导出 OUTPUT 一侧:
export type ProvisionEnvironmentRequest = z.infer< typeof ProvisionEnvironmentRequestSchema >;
export type TenantRoutingConfig = z.infer< typeof TenantRoutingConfigSchema >;
export type ProvisionTenantRequest = z.infer< typeof ProvisionTenantRequestSchema >;
而这些 schema 里有 .default() 字段,例如 environment.zod.ts:304:
visibility: EnvironmentVisibilitySchema.optional().default('private')
于是 visibility 在 OUTPUT 类型上是必填。名字叫 ...Request 的类型,描述的却是解析之后的形状。
后果(在 cloud 仓实测)
packages/service-tenant/src/environment-provisioning.ts:340 把它用作自己那次 parse 的入参类型:
async provisionEnvironment(
request: ProvisionEnvironmentRequest, // OUTPUT 类型
): Promise< ProvisionEnvironmentResponse > {
const parsed = ProvisionEnvironmentRequestSchema.parse(request);
...
调用方写一个完全合法的 provisioning 请求:
svc.provisionEnvironment({
organizationId: 'org-123',
displayName: 'Alice dev',
createdBy: 'user-1',
});
得到 TS2345: Property 'visibility' is missing。即:类型要求调用方手写 zod 负责填的默认值。cloud#1118 批次 1 里,service-tenant 13 条报错中的 12 条是这一形状(visibility ×8、jwtOrganizationClaim/tenantHeaderName ×2、plan ×2),tenant-router 的 3 条同族。批次 1 只能在测试侧按默认值原样补齐绕过去,生产者侧没动。
一个佐证:源码自己不相信这个类型
同文件 environment-provisioning.ts:621:
visibility: parsed.visibility ?? 'private',
parsed 是 .parse() 的产物,visibility 有 .default('private'),所以它永远不可能是 undefined —— 这行 ?? 'private' 是死代码。作者在防一个类型说不可能、而直觉说可能的情况;直觉是对的(对入参而言可能),类型是错的(它描述的是出参)。
为什么记成 finding 而不是 bug
今天没有用户会撞到运行时错误:默认值该填的都填了,行为正确。代价是作者态的 —— 合法的调用写不出来、?? 死代码被当成防御留着、以及 cloud 仓测试为了过类型而多写 12 处默认值。严重度请按 triage 定。
建议(不代裁决)
按 #4963 的 house convention,给 spec/src/cloud/ 这批 request 类型补 Input 孪生(z.input),并把 provisionEnvironment 这类「自己 parse 自己入参」的方法形参改指 Input 侧。这是参数类型放宽,对现有调用方不破坏。是否连带清掉 ?? 'private' 这类死防御,可另议。
关联:#4963(convention)、#5543(objectql registerObject 同根因)、#5227(ApiEndpoint.authRequired 同根因)、#5507 / #5551(其他分区)、#5478(spec 自己那 691 条台账里的同族)、objectstack-ai/cloud#1118 / objectstack-ai/cloud#1184(本条的测量来源)。
Blocked-by: #5551
Generated by Claude Code
做 cloud#1118 批次 1(objectstack-ai/cloud#1184,把 cloud 仓测试文件接进 tsc)时量到。不在那个 PR 范围内(跨仓契约面),按 Prime Directive #10 单独记在这里,unassigned。
与 #5543 / #5227 同根因,与 #5507 / #5551 同形状(按 zod 文件分区登记 #4963 确立的
X/XParsedhouse convention)。本条是spec/src/cloud/这个分区,此前没有单子覆盖。事实
packages/spec/src/cloud/environment.zod.ts:310、cloud/tenant.zod.ts等只导出 OUTPUT 一侧:而这些 schema 里有
.default()字段,例如environment.zod.ts:304:于是
visibility在 OUTPUT 类型上是必填。名字叫...Request的类型,描述的却是解析之后的形状。后果(在 cloud 仓实测)
packages/service-tenant/src/environment-provisioning.ts:340把它用作自己那次 parse 的入参类型:调用方写一个完全合法的 provisioning 请求:
得到
TS2345: Property 'visibility' is missing。即:类型要求调用方手写 zod 负责填的默认值。cloud#1118 批次 1 里,service-tenant13 条报错中的 12 条是这一形状(visibility×8、jwtOrganizationClaim/tenantHeaderName×2、plan×2),tenant-router的 3 条同族。批次 1 只能在测试侧按默认值原样补齐绕过去,生产者侧没动。一个佐证:源码自己不相信这个类型
同文件
environment-provisioning.ts:621:parsed是.parse()的产物,visibility有.default('private'),所以它永远不可能是 undefined —— 这行?? 'private'是死代码。作者在防一个类型说不可能、而直觉说可能的情况;直觉是对的(对入参而言可能),类型是错的(它描述的是出参)。为什么记成 finding 而不是 bug
今天没有用户会撞到运行时错误:默认值该填的都填了,行为正确。代价是作者态的 —— 合法的调用写不出来、
??死代码被当成防御留着、以及 cloud 仓测试为了过类型而多写 12 处默认值。严重度请按 triage 定。建议(不代裁决)
按 #4963 的 house convention,给
spec/src/cloud/这批 request 类型补Input孪生(z.input),并把provisionEnvironment这类「自己 parse 自己入参」的方法形参改指 Input 侧。这是参数类型放宽,对现有调用方不破坏。是否连带清掉?? 'private'这类死防御,可另议。关联:#4963(convention)、#5543(objectql
registerObject同根因)、#5227(ApiEndpoint.authRequired同根因)、#5507 / #5551(其他分区)、#5478(spec 自己那 691 条台账里的同族)、objectstack-ai/cloud#1118 / objectstack-ai/cloud#1184(本条的测量来源)。Blocked-by: #5551
Generated by Claude Code