Skip to content

feat(spec): preview/trial 的 discovery 折叠改为显式声明,折叠表对 EnvironmentType 穷尽 (#6287) - #6610

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-6287-preview-trial-mapping
Aug 8, 2026
Merged

feat(spec): preview/trial 的 discovery 折叠改为显式声明,折叠表对 EnvironmentType 穷尽 (#6287)#6610
qq9340100 merged 1 commit into
mainfrom
claude/issue-6287-preview-trial-mapping

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #6287

前提复核(基于 #6554 落地后的 origin/main)

复核通过,premise_still_valid: true。在 a36db28b7 实读
packages/spec/src/api/discovery.zod.ts:NODE_ENV_TO_DISCOVERY_ENVIRONMENT
(#6554 之后移到 :341)确实只有五条 —— production / prod / sandbox /
staging / development / dev / test,previewtrial 无条目;
resolveDiscoveryEnvironment 末行的 ?? 'development' 兜底仍在。

一条比 issue 描述更硬的旁证:改动前源文件注释第 318 行preview 写成
「未识别拼写」的举例
(An unrecognised spelling (qa, preview)),三个包的
测试也拿 preview 当未识别拼写的 fixture。这正是本单要修的那个混淆 ——
它是我们自己词表里的一等成员,不是没听说过的拼写。

#6554 建立的两分法(absent → production 是宿主拒答;未识别拼写 →
development 是猜测)原样保留,本 PR 不动它。

折叠目标判定:两者皆 sandbox —— 否决窗口见下

preview / trial 既不是 absent 也不是未识别拼写,属第三类:已声明的一等成员,
所以需要独立理据。三条:

  1. 它们在本仓语义里是什么。 本仓的 environment 是被开通的运行容器 ——
    独立数据库、规范主机名、套餐档位、按环境的 RBAC(cloud/environment.zod.ts
    开头的定义)。preview / trial 是这种东西,不是 development / dev /
    test 所描述的开发机与 CI 的一次性运行。sandbox 正是这个枚举里
    「已开通的准生产」那一档,而 staging 早就按同样理由折在此处。
  2. 姿态按收紧方向取。 trial 尤其装着评估中客户的真实业务数据。
    environment 是机器可读面,客户端读它决定要不要放宽破坏性操作的二次确认,
    development低报姿态 —— 与 NODE_ENV 未设置时 /discovery 广播 environment=development,而 os start 默认 NODE_ENV=production、CLI doctor 也按 production 解析 #5673 / NODE_ENV 未设置时,第二个 /discovery 生产者(metadata-protocol 的 getDiscovery,经 @objectstack/rest)仍广播 environment=development #5936 把 unset 一行翻成
    production 所要避免的是同一类错误,只是低一档。两者都不是 production:
    它们按定义就不是客户的生产部署,报 production 会让这个字段唯一要回答的问题
    朝另一个方向答错,也违反「机器可读面不得说谎」。
  3. 它保住了作者的区分。 把环境标成 preview 的人,手里本来就有
    developmenttest 而没有选。折到 development 会把这个选择携带的
    唯一信息抹平;折到 sandbox 在三成员枚举的分辨率内保住了它。

⛔ 否决窗口。 若维护者认为 preview / trial 应维持 development
(即 issue 建议的「行为零变化」版本),改判只需一行:把
discovery.zod.ts 里那两行的值改成 'development',穷尽检查、兜底声明化、
测试结构一概不受影响(表内数值测试与 changeset 的表格需同步)。
两者可分别否决。

这是行为变化,changeset 按 minor 定并写明 FROM → TO:

NODE_ENV(或任何 operator 提供的字符串) 之前(兜底) 现在(声明)
preview development sandbox
trial development sandbox

兜底声明化 + 编译期穷尽检查

七行分组在 satisfies Record< EnvironmentType, DiscoveryEnvironment > 之后;
operator 便利拼写 prod / dev 留在组外 —— 混进去的话每个简写都会被当成
词表成员,穷尽标注就写不出来了。合并后仍是原来那张查找表,
resolveDiscoveryEnvironment 的查找路径未变。EnvironmentType
import type,编译期擦除,不给 api/cloud/ 添运行时边。

?? 'development' 兜底保留(issue 的建议正确:它服务的是任意 operator
字符串这一真实输入类),但职责收窄并写进注释:如今只对既非词表成员、也非
operator 简写的拼写生效(qauat、拼错)。未来新增的桶再也够不到它 ——
够不到的方式是根本编译不过。

为什么是编译期而非运行期断言

运行期断言看不见这个缺陷:兜底与三条已声明的行(development / dev / test)
都产出 'development',所以调 resolveDiscoveryEnvironment('preview')
「有条目」与「?? 现编」两种情况下返回完全一致。一条运行期穷尽测试在
#6287 报告的那个坏状态下本来就是绿的。
tsc 比的是键集合,那才是真正的主张。

逆向验证(方向先判后跑,两条都是实读)

动作 事前预判 实际读数
删掉 preview 源程序编译红 error TS1360: … Property 'preview' is missing in type … but required in type 'Record< "test" ¦ "development" ¦ "production" ¦ "preview" ¦ "staging" ¦ "sandbox" ¦ "trial", … >'
加非成员键 qa 源程序编译红 error TS2353: Object literal may only specify known properties, and 'qa' does not exist in type 'Record< … >'
从枚举删掉 trial 成员 源程序红 测试里的负向 pin 变成未使用 源:TS2353;测试:TS2578: Unused '@ts-expect-error' directive

第三行是负向控制的负向控制 —— 它证明那条 @ts-expect-error 不是空过的。
packages/spec 的测试层确实被 tsc 编译(tsconfig.test.json,自 #5286
写进 typecheck 脚本),且 discovery.test.ts 不在
test-typecheck-debt.json 里(该账本只有 2 个文件),所以这条 pin 是活的,
不是 AGENTS.md 警告的那种幽灵检查。

一处如实修正:我最初在测试注释里写这条 pin「双向失败,包括源表标注被放宽时」。
不成立 —— 测试里的 pin 用的是它自己的 Record< EnvironmentType, … >,
源表标注被改宽它是不会红的(表未导出,为测试导出它等于把一个私有查找表
放上本包公开面)。注释已改成如实陈述覆盖与不覆盖的范围。

Fixture 三处逐条重判(按消费半径,不是批量改写)

preview 从「未识别拼写」的例子里毕业,三处 fixture 各自重判 —— 规则一概保留,
只换例子:

文件 处置
packages/spec/src/api/discovery.test.ts ['qa', 'preview', 'nonsense']['qa', 'uat', 'nonsense']
packages/metadata-protocol/src/discovery-schema-conformance.test.ts 同上
packages/runtime/src/discovery-schema-conformance.test.ts ['qa', 'preview', 'uat', 'nonsense'] → 去掉 preview

留着不改的话,它们会断言出与 mapper 决定相反的事实,而且会因为
「已声明的答案恰好等于兜底的答案」而继续绿 —— 正是这次要消灭的那种绿。

其中 metadata-protocol 那条是 spec 范围的 sweep 看不到、由消费者测试跑出来的
(先红后修,与 PR #5046 同一类:改在 A 包,坏的 fixture 在 B 包)。

验证

  • pnpm --filter @objectstack/spec test —— 全量 342 files / 8790 tests 全绿;
    定向复跑 discovery.test.ts + discovery-environment-subset.pin.test.ts:86 passed
  • pnpm --filter @objectstack/spec typecheck —— 绿(含 check:scripts-typecheck
    check:test-typecheck:测试层在 tsconfig.test.json 下编译通过)
  • pnpm --filter @objectstack/spec check:generated —— ✓ All 10 generated artifacts are up to date
  • pnpm check:spec-parsed-alias —— --self-test 18 assertions passed;
    1443 bare z.input aliases, 749 pinned isomorphic, 694 paired with an XParsed. OK
  • 消费者面:metadata-protocol discovery 一致性 19 passed(修 fixture 前 1 failed,
    实读 expected 'sandbox' to be 'development');runtime discovery + http-dispatcher
    258 passed
  • eslint(5 个改动文件,--no-inline-config)rc=0
  • node scripts/check-nul-bytes.mjs —— OK(6163 个文件);改动文件另做
    grep -naP 控制字符自扫,clean
  • pnpm check:empty-changeset —— self-test 48 assertions 通过

packages/metadata-protocoltypecheck 脚本(类型检查覆盖率账本内的既有状态),
故该包只跑测试。


Generated by Claude Code

#6287)

`EnvironmentTypeSchema` 有七个成员,`NODE_ENV_TO_DISCOVERY_ENVIRONMENT`
(`packages/spec/src/api/discovery.zod.ts`)只为其中五个写了条目。`preview`
与 `trial` 一直靠 `resolveDiscoveryEnvironment` 末行的 `?? 'development'`
兜底落到 `development` —— 不是一条被写下来的决定,而是掉出表尾的副作用。
这张表的注释本来就写明它是给后来者读的,读表的人会以为它是全的;#6554
刚把 unset 一行收进这个 mapper,留下的正是这最后一条隐式边。

折叠目标 = `sandbox`(两者皆是),三条理据 —— 它们既不是 absent(宿主拒答)
也不是未识别拼写(猜测),而是**已声明的一等成员**,属第三类:

1. 本仓语义:environment 是被开通的运行容器(独立数据库、规范主机名、套餐档位、
   按环境 RBAC,见 `cloud/environment.zod.ts`)。`preview` / `trial` 是这种东西,
   不是 `development` / `dev` / `test` 描述的开发机与 CI 的一次性运行;
   `sandbox` 正是枚举里「已开通的准生产」那一档,`staging` 已按同样理由折在此处。
2. 姿态按收紧方向:`trial` 装着评估中客户的真实业务数据,答 `development`
   是低报姿态 —— 与 #5673 / #5936 把 unset 翻成 `production` 所避免的是同类错误,
   只低一档。两者都不是 `production`,报 `production` 会朝另一个方向答错。
3. 保住作者的区分:把环境标成 `preview` 的人手里本就有 `development` 和 `test`
   而没有选;折到 `development` 会抹平这个选择携带的唯一信息。

漏补条目从此不编译:七行分组在
`satisfies Record<EnvironmentType, DiscoveryEnvironment>` 之后(operator 便利拼写
`prod` / `dev` 留在组外,否则穷尽标注写不出来)。逆向验证(方向先判后跑,两条都实读):
删 `preview` 行 → TS1360 `Property 'preview' is missing`;加非成员键 `qa` → TS2353
`'qa' does not exist in type`。选编译期而非运行期断言,是因为运行期断言看不见这个缺陷 ——
兜底与三条已声明的行都产出 `development`,一条运行期穷尽测试在坏状态下本来就是绿的。

`?? 'development'` 兜底保留,职责收窄并写进注释:只服务既非词表成员、也非 operator
简写的任意字符串(`qa`、`uat`、拼错)。#5936 的两条既有 pin(absent → `production`、
未识别 → `development`)规则未动且保持绿。

夹带的 fixture 三处按消费半径逐条重判(不是批量改写):`packages/spec`、
`packages/metadata-protocol`、`packages/runtime` 三个 test 都把 `preview`
当作「未识别拼写」的例子,而它现在是已声明成员 —— 规则保留,例子改成
真正未识别的 `uat`。其中 metadata-protocol 那条是 spec 范围的 sweep 看不到、
由消费者测试跑出来的(与 PR #5046 同一类)。

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

vercel Bot commented Aug 8, 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 8, 2026 7:04am

Request Review

@github-actions github-actions Bot added the size/m label Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 8, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 8, 2026 07:21
@qq9340100
qq9340100 added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit 84c86fb Aug 8, 2026
26 checks passed
@qq9340100
qq9340100 deleted the claude/issue-6287-preview-trial-mapping branch August 8, 2026 07:37
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/m tests tooling

Projects

None yet

2 participants