Repository navigation
Closed
Description
#4411 的第二条待裁决。那条 issue 由一次同名双源引发(MetadataWatchEvent 在 ./kernel 与 ./system 各一份、形状不同、kernel 版零消费方),已在 #4411 的 PR 里连同同文件另外 10 个同类一并退役。但产生这类问题的机制没有被堵上 —— api-surface.json 里同名跨入口是可见但不报警的,下一个同名双源仍会静默落地。
为什么这类缺陷特别值得机器来抓
它是只在 import 路径上分叉的缺陷:两份声明共用一个名字,靠 from '@objectstack/spec/<entry>' 区分。
- 自动 import 和模型补全按名字解析,拿到哪一份取决于工具先看到哪个入口 —— 这不是判断力问题,是没有可判断的信息。
- 两份形状通常大量重叠,所以选错照样编译,只在边缘值或必填性上翻车(spec 同名双源:两个
MetadataWatchEvent形状不同、分挂两个子路径入口,其中 kernel 版零消费方(ADR-0049 enforce-or-remove) #4411 的例子:type: 'add'vs'added')。 - 更糟的是命名直觉可以是反的:spec 同名双源:两个
MetadataWatchEvent形状不同、分挂两个子路径入口,其中 kernel 版零消费方(ADR-0049 enforce-or-remove) #4411 里看起来更规范的那份(规范化枚举、字段必填、每个属性都有.describe())是死的,真正在用的是自称 legacy 的宽松超集。按"哪个看起来更正统"来选,选反。
一次 code review 很难看见它 —— 两个文件各自都是合理的。
当前实测基线(#4411 PR 合入后)
从 packages/spec/api-surface.json 直接算:
| 指标 | #4411 前 | #4411 后 |
|---|---|---|
跨子路径同名导出(name (kind) 出现在 >1 个非 . 入口) |
103 | 83 |
| 其中在 >1 个源文件里各自声明的(真·多源,非 re-export) | 70 | 待重算 |
复算脚本:
const s = require('./packages/spec/api-surface.json');
const map = new Map();
for (const [ep, list] of Object.entries(s).filter(([, v]) => Array.isArray(v)))
for (const n of list) (map.get(n) ?? map.set(n, []).get(n)).push(ep);
console.log([...map].filter(([, eps]) => eps.filter(e => e !== '.').length > 1).length);.(根入口)+ 子路径的组合要排除 —— 那是有意的 re-export,同一个符号。
设计要点(裁决时值得先看的几条)
- 按符号 identity 判定,不能只按名字。 83 条里绝大多数是同一个声明从两个入口可达(合法),真正的缺陷是两个不同声明共用一个名字。
scripts/build-api-surface.ts已经用 TS checker 解析 module symbol,拿到 declaration 位置来做同一性判断是可行的 —— 这也是这条检查唯一有价值的形态,只比名字会淹没在误报里。 - 需要 allowlist 基线。 剩余的真·多源不可能在一个 PR 里清完,得按 ratchet 模式落地(新增即失败,存量记账),形态参照
authorable-surface.json/check-type-check-coverage.mjs。 - 挂载点。 跟着
check:api-surface走(同一个TypeScript Type Checkjob,读 builtdist/*.d.ts);注意 AGENTS.md 记的 stale-dist 陷阱同样适用。
已知的存量条目(建档,便于起基线)
MetadataFormat——system/metadata-persistence.zod.ts(7 个成员,含yml/ts/js别名)与shared/metadata-types.zod.ts(4 个成员)枚举分歧,两者都在导出面上。spec 同名双源:两个MetadataWatchEvent形状不同、分挂两个子路径入口,其中 kernel 版零消费方(ADR-0049 enforce-or-remove) #4411 只把 kernel 的第三份删掉了,这一对仍在。contracts/metadata-service.ts自有的MetadataExportOptions/MetadataImportOptionsinterface —— 与system/metadata-persistence.zod.ts的同名 schema 第三形状,且有消费方,不是删得掉的死副本,需要单独判定哪边是真源。system↔integration(ConsumerConfig、DatabaseProvider、MessageQueueProvider、MultipartUploadConfig)、system↔cloud(EnvironmentArtifact、TenantPlan)、kernel↔api(*PackageRequest/Response一族)、ui↔shared(HttpRequest、HttpMethodSchema)等成组条目 —— 需逐组判定 re-export vs 真分歧。
待裁决
这条检查是否值得做、按什么优先级做,由维护者定(#4411 里已明确记为「值不值当由维护者定」)。开这条 issue 是为了不让那次裁决连同实测基线一起丢失。
Metadata
Metadata
Assignees
Labels
No labels
Activity
feat(spec): ratchet cross-entry dual-source exports — same name, diff…
fix(objectstack-ai#4570): spell reference-doc import examples from th…
docs(releases): v17 page covers the 17.0.0-rc.2 window (objectstack-a…
chore(spec): classify the test-layer typecheck gate in the check:gene…
fix(spec): 让 spec 测试层真的进 tsc,`@ts-expect-error` 退役 pin 不再是幽灵检查 (objec…