Skip to content

feat(spec): ADR-0122 第一期 —— 解析态统一命名 XParsed(纯增量)+ 防回流门 - #6072

Draft
qq9340100 wants to merge 3 commits into
mainfrom
claude/issue-5551-xparsed-convention-adr-phase1
Draft

feat(spec): ADR-0122 第一期 —— 解析态统一命名 XParsed(纯增量)+ 防回流门#6072
qq9340100 wants to merge 3 commits into
mainfrom
claude/issue-5551-xparsed-convention-adr-phase1

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Refs #5551

按 2026-08-06 维护者批复(裁 C,分期 C2)落地第一期。原单叙事(「翻 connector 单文件」)已被前任 dev 证伪,本 PR 不执行它。

关单口径:本 PR 只完成第一期(加性),裸名翻转留到 major 窗口,故用 Refs 而非 Fixes,由 PM 决定 #5551 是关单另开第二期单、还是保持 open。


一、先说结论:判据是「形状差」,不是「有没有 XInput」

裁决把第一期铺设判据交给 dev 定并论证。我重跑了全仓测量,结论与派发单沿用的 03:24Z 摘要不同,且差异是决定性的:

派发单里的「B 族 ~55 文件 / ~108 别名」是**「已经写了 XInput 的那批」**。它不是需要迁移目标的那批 —— 第二期翻裸名会改变含义的别名共 663 个,其中只有 91 个今天带 XInput,572 个裸名连作者态的名字都没有。按「有 XInput」铺,会漏掉 572 个 —— 恰恰是第一期要防的那种静默破坏。

所以 ADR-0122 D5 写下的判据是:

当且仅当 z.input< typeof XSchema >z.infer< typeof XSchema > 不是同一个类型时,该裸名别名获得 XParsed

三轴论证(写进 ADR):

  • 真实业务需要:第二期只在两形状有差时改变含义;同构者翻不翻都一样,给它 XParsed 是永久同义词。全仓同构的有 721 个 —— 按「一律铺」会给公开面凭空加 721 个「选哪个都行、于是必然有人选错」的名字。创业期收紧口径,名字要靠用途挣位置。
  • 长期正确性:XParsed 只在「解析态确实是另一个类型」时存在,名字才不撒谎。
  • 让 AI 不易写错:同构者不给第二个名字,作者就没有可选错的机会;有形状差的一律给,作者/LLM 迁移时永远找得到目标。

⚠️ 由此,本 PR 的量级与派发单预估(+~100 导出)不同:实际新增 660 个别名。这是测量取代估算,不是扩范围 —— 判据本身就是裁决交给我定的那一项。

二、改了什么(纯增量)

新增 XParsed 别名 660
涉及 *.zod.ts 文件 151
修改/重命名/删除的现有声明 0
钉住的同构 schema 717
// 之前
export type Connector = z.infer< typeof ConnectorSchema >;
export type ConnectorInput = z.input< typeof ConnectorSchema >;

// 之后 —— 只加了中间这行
export type Connector = z.infer< typeof ConnectorSchema >;
export type ConnectorParsed = z.infer< typeof ConnectorSchema >;
export type ConnectorInput = z.input< typeof ConnectorSchema >;

XInput 一个没动(qa downstream-contract 的 FROZEN fixtures 按文件头明文原样不迁移);A 族 8 文件未触碰。

三、同构豁免是钉住的,不是写在注释里的

D3 说同构者不给第二个名字。但「同构」不是谁声明的,是 schema 树的事实,而且会烂 —— 哪天某个嵌套字段加了 .default(),这个别名就悄悄进了形状差集合,第二期翻它时消费方没有迁移目标,正是第一期要防的那件事。

所以 717 个豁免全部落在 packages/spec/src/type-alias-convention.pin.test.ts,每条都是一句编译期断言。tsc 在给包做类型检查的同一趟里证明它们为真,某条不再成立的当天这个文件就红,并直接点名是哪个别名。修法只有一种:给裸名补 XParsed,然后删掉那行 pin。

四、防回流门:落在 scripts/,不在 packages/lint

派发单写的是「packages/lint/** 新增一条规则」。这条做不到,是架构事实不是偏好:@objectstack/lint 的模块头写着它校验的是内存里、已 schema-parse 的元数据图,"no I/O, no runtime, no filesystem",每条规则都是纯 (stack) => Finding[]。一条要读我们自己 .zod.ts 源码的规则放进去,会是该包里唯一打开文件的规则,直接破坏它的契约。

所以它落在本仓源码形态门那一族(check:error-code-casing / check:route-envelope / check:engine-double-contract 的邻居),挂进 lint.yml 的 lint job:

  • pnpm check:spec-parsed-alias —— 每个裸 z.infer 别名必须要么XParsed,要么在 pin 文件里被钉为同构;顺带报告「已经没人依赖」的过期 pin。
  • 豁免名单不写在门里,门去读 pin 文件 —— 一份产物两个用途:门拿到机器可读的豁免表,tsc 保证表上每条都为真。豁免名单要是搬进门里,它就变成 AGENTS.md 说的那种 phantom check 了。
  • --self-test(11 条正反断言),照本族惯例先自检再扫。

由此不涉及 packages/lint,与 devx 车道在飞的 #4251 零文件相交(注册表/barrel 都没碰)。

门与 pin 的实测信噪比

  • 铺完后全仓 0 误报:1384 bare z.infer aliases, 717 pinned isomorphic, 667 paired with an XParsed. OK(667 + 717 = 1384,门每次运行自己算这道账)。
  • 门在真语料上抓到了 5 个我的普查漏掉的真实边角,不是空转:ServiceObject(ObjectSchemaBase 未导出,探针够不到 —— 单独验证确有形状差,已补 ServiceObjectParsed)+ automation/execution.zod.ts 4 条本就配好 XParsed、因而多余的 pin。

五、反向验证(方向事先定,两处都预期变红)

1. 删掉一个新铺的 XParsed → 门必须红并点名该别名。ConnectorParsed:

ADR-0122: 1 type-alias convention violation(s) in packages/spec.
  integration/connector.zod.ts — Connector
    `Connector` names the PARSED state of `ConnectorSchema`, but ADR-0122 reserves
    the bare name for the AUTHOR state. Declare `export type ConnectorParsed = ...`
GATE_EXIT=1

还原后回到 ... 667 paired with an XParsed. OK

2. 给一个被钉为同构的 schema 加 .default() → pin 必须红。SyncStrategySchema(pin 文件第 781 行 = Iso376)加 .default('full'):

packages/spec/src/type-alias-convention.pin.test.ts(781,29): error TS2344:
  Type 'false' does not satisfy the constraint 'true'.

红的正是事先点名的那一行,不是别的行。还原后 check:test-typecheck 恢复绿。

六、SYNC_ARCHITECTURE.md 误导句

原文说 connector「还没搬到那条 house convention 上」,把 8 文件的少数派说成全仓惯例 —— 与实测相反。已改写为:两种拼法都无记录、ADR-0122 才是那条记录、第一期已给 Connector 备好 ConnectorParsed、第二期在 major 翻裸名。

七、生成物

XParsed 是纯类型别名,所以只有 api-surface.json 动:+637 导出名,0 删除。声明了 660 个,其中 23 个所在模块没有被任何公开入口再导出,故不上公开面 —— 这个差额是核对过的,不是漏生成。

json-schema/authorable-surface.json 零变化:gen:schema + gen:openapi 跑完 git status 只有我自己手改的两个文件。这是验证结果,不是预期。

check:generated 十个门:9 绿,唯一 stale 的 api-surface.json 已按它给的命令重生成(未整套重跑)。

八、必答:本改动对 #5837(生成物分片)的影响

变简单一点点,主要是无影响。 本 PR 不碰 packages/spec/scripts/**(#5837 的源文件面),两边源文件零相交;唯一相交是 api-surface.json 这个生成物本身,而这正是分片要解决的问题 —— 本 PR 给它加 637 个导出名,是分片收益的一个具体例证(单文件更大),但既不改变它的生成方式,也不增加分片的实现难度,更没让分片变得不必要。落地次序按 PM 定的 #5983#5837 → 本单,我在末次同步圈按分片后的生成流程整体重生成。

九、验证

命令 结果
pnpm --filter @objectstack/spec typecheck TYPECHECK_EXIT=0
pnpm --filter @objectstack/spec test Test Files 326 passed (326) / Tests 8311 passed (8311)
pnpm --filter @objectstack/lint test Test Files 61 passed (61) / Tests 1449 passed | 4 skipped
pnpm check:spec-parsed-alias(含 --self-test) 11 断言过 + 全仓 OK
pnpm check:adr-anchors OK(36 anchored files)
pnpm check:nul-bytes OK(5783 files)
npx eslint(改动文件) ESLINT_EXIT=0

@objectstack/lint 首跑 13 个测试文件红 / 3 条测试红。查明是 AGENTS.md §9 与工具链陷阱 #2依赖未构建假红(13 files 红但只有 3 tests 红 = collection error 特征),pnpm --filter '@objectstack/lint^...' build 后 61/61 全绿。与本改动无关。


Generated by Claude Code

claude added 2 commits August 6, 2026 15:59
`packages/spec` named a schema's two types (`z.input` = author state,
`z.infer` = parsed state) two different ways, with nothing recorded about
which was which. Measured on origin/main: 1384 bare aliases mean the parsed
state, 86 mean the author state, and three first-hand sources each called the
8-file minority "the house convention". No ADR recorded either.

ADR-0122 settles it — bare name = author state, `XParsed` = parsed state —
and lands additively. Phase 1 declares `XParsed` for the 660 aliases whose
schema genuinely has two shapes, so the major-window flip has a migration
target everywhere it changes meaning. Nothing renamed, nothing removed,
every `XInput` left in place.

- ADR-0122, with both measurement rounds as its appendix
- 660 `XParsed` aliases across 152 `*.zod.ts` files
- 717 isomorphic schemas pinned with compile-time assertions, so the D3
  exemption cannot rot silently when one later gains a `.default()`
- `check:spec-parsed-alias` backflow gate (lint.yml), reading the pin file
  as its exemption registry
- SYNC_ARCHITECTURE.md: correct the sentence that called the minority
  spelling the house convention

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

- api-surface.json: +637 export names, 0 removed. 660 aliases were declared;
  23 live in modules no public entry point re-exports.
- json-schema/ and authorable-surface.json verified unchanged — `XParsed` is a
  pure type alias, so `gen:schema` / `gen:openapi` produce no diff at all.
- the pin file's count case now reads the pin count out of the source instead
  of asserting a literal against itself. tsc proves each pin is TRUE; nothing
  proved they were still THERE, so a red pin could be made green by deleting
  it. Counting them makes that edit fail `pnpm test` too.
- ADR appendix: 151 files edited (not 152 — `automation/execution.zod.ts`'s
  three differing aliases already had their `XParsed`), plus the two findings
  the gate produced on its first real run.

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

vercel Bot commented Aug 6, 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 6, 2026 5:30pm

Request Review

@github-actions

github-actions Bot commented Aug 6, 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 size/xl documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file protocol:data protocol:system tests protocol:ui protocol:ai tooling labels Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

CI 收敛记录(⚠️ ESLint job 至今未拿到 runner —— 合并前请自行复核该 job 颜色)

在合并前请以 CI 上 ESLint job 的真实 conclusion 为准。本条记录我离开时的状态与替代证据,不代表该 job 已通过。

两个门 job 的实际状态(merge commit aed99a8,run 31123460219)

job conclusion 备注
TypeScript Type Check success 本改动风险最高的 job —— spec 全部八个生成物门都在这里跑
ESLint queued,未分配 runner 首轮 17:33 排队 21 分钟后被 cancelled(零 step 执行、runner: (none assigned));rerun_failed_jobs 重试后 18:12 起再排队 70+ 分钟,仍未分配

这是全仓 runner 饥饿,不是本 PR

最近 30 个 workflow run 里有 19–22 个处于 queued/in_progress,横跨多个分支,连 merge queue 自己的 run(gh-readonly-queue/main/pr-6031pr-6034)也在排队。同一时段 Console Pin Freshness / Duplicate Fix Guard / Validate Dependencies 三个 workflow 的 run 结论是 failure,但逐个查 job 全部是 cancelled(零 step),同一个饥饿现象。

替代证据:把 ESLint job 的命令逐条在合并后的树上跑了一遍

不是「本地测试绿」,是把该 job YAML 里的 27 条命令原样抽出来逐条执行,含全仓 pnpm lint 本身:

green: pnpm install --frozen-lockfile      green: pnpm check:route-envelope
green: pnpm lint                           green: pnpm check:error-code-casing
green: pnpm check:slot-lookup              green: pnpm check:wildcard-fallthrough
green: pnpm check:query-options-erasure    green: pnpm check:init-service-contract
green: pnpm check:nul-bytes                green: pnpm check:durability-log-level
green: pnpm check:doc-authoring            green: pnpm check:startup-registry-verdict
green: pnpm check:docs-audit-scope         green: pnpm check:objectui-changeset
green: pnpm check:role-word                green: pnpm check:release-notes
green: pnpm check:adr-anchors              green: pnpm check:release-body
green: pnpm check:org-identifier           green: pnpm check:node-version
green: pnpm check:authz-resolver           green: pnpm check:workflow-status-functions
green: pnpm check:service-providers        green: pnpm check:published-files
green: pnpm check:engine-double-contract   green: pnpm check:resume-authority-declared
green: pnpm check:spec-parsed-alias
LINTJOB_ALL_EXIT=0

其中 pnpm check:spec-parsed-alias 是本 PR 新增的门,也在这个 job 里 —— 所以它是否误报,这一轮已经用真语料验过(全仓 0 误报)。

合并前还需确认

  1. ESLint job 拿到 runner 并出 conclusion(这是唯一没拿到的那块)。
  2. Check Changeset 同轮被 cancel,需重跑;本 PR 带 changeset(.changeset/spec-type-alias-parsed-convention.md,@objectstack/spec: minor),因此不需要 skip-changeset 标签。
  3. 落地次序按 PM 定的 refactor(spec)!: 按 ADR-0049 摘除 IStorageService.list(prefix) —— 零消费方、双适配器语义分叉 (#5540) #5983spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837 → 本单;refactor(spec)!: 按 ADR-0049 摘除 IStorageService.list(prefix) —— 零消费方、双适配器语义分叉 (#5540) #5983 已在 main(本分支已 merge 并重新校验:check:generated 十个门全绿)。若 spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837 分片先落,本 PR 需按分片后的生成流程重跑一次 gen:api-surface

Generated by Claude Code


Generated by Claude Code

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants