refactor(spec)!: remove the kernel metadata-loader envelope family — 11 names declared twice with different shapes (#4411) - #4458
Merged
Conversation
…11 names declared twice with different shapes (#4411) `@objectstack/spec` exported eleven names TWICE, with a different shape each time, on two subpath entries — so which type a consumer got depended on nothing but the import path: import type { MetadataWatchEvent } from '@objectstack/spec/kernel'; // one shape import type { MetadataWatchEvent } from '@objectstack/spec/system'; // another `MetadataFormat`, `MetadataStats`, `MetadataLoadOptions`, `MetadataSaveOptions`, `MetadataExportOptions`, `MetadataImportOptions`, `MetadataLoadResult`, `MetadataSaveResult`, `MetadataWatchEvent`, `MetadataCollectionInfo` and `MetadataLoaderContract` are removed from `kernel/metadata-loader.zod`. The `system/metadata-persistence.zod` copies stay as the single source. Why the kernel side goes, and why this was worth removing rather than living with: - Zero consumers. Import-statement scans across this repo, `cloud` and `objectui` find every consumer on `./system` (or `./contracts`' own interface); only `kernel/metadata-loader.test.ts` ever parsed the kernel copies. ADR-0049 enforce-or-remove. - The naming intuition pointed the wrong way, which is what made this sharper than an ordinary duplicate. The kernel copies were the ones that LOOKED canonical — normalized enums, required fields, a `.describe()` per property — and they were the dead ones; the live copy is the loose superset its own consumer calls "legacy". Picking by name, or by which reads as more rigorous, picked the dead one, and because the shapes overlap heavily that choice compiled and failed later, at an edge value (`add` vs `added`) or on a field one copy made required. No tombstone and no ADR-0087 conversion, deliberately: these are runtime envelope types, not authorable metadata, so no authored source can carry them and there is nothing for `os migrate meta` to rewrite (the plugin-runtime / dev-plugin precedents). `MetadataManagerConfig` and `MetadataFallbackStrategy` are untouched — they were never duplicated (kernel owns them, system re-exports them), and that is the split that survives: manager wiring is kernel's, the loader/watch envelope is system's, nothing is declared twice. `MetadataManagerConfig.formats` now reads the `shared` format enum (same four members, leaf module, no cycle) rather than a fourth local copy. Also: - `contracts/metadata-service.ts` drops the "spec carries TWO types named MetadataWatchEvent" warning added in #4404 — it no longer does. - `expression-conformance.ledger.ts` drops the now-absent `kernel/metadata-loader.zod.ts:filter` CEL surface (the surviving system options never declared a `filter`, so no loader predicate was ever evaluated through it). - Baselines dropped deliberately: `json-schema.manifest.json` −11 entries, `authorable-surface.json` −65 lines (nothing can author these, so no `[RETIRED]` markers). `api-surface.json` regenerated: 22 exports leave `./kernel`. `references/kernel/metadata-persistence.mdx` removed by `gen:docs`. v17 release notes + upgrade checklist extended. No runtime behaviour changes — nothing read the removed copies. The system shapes are NOT tightened here; narrowing them would be a separate change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
…-event-dual-source-v3jw3v
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 108 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…-event-dual-source-v3jw3v
…-event-dual-source-v3jw3v # Conflicts: # content/docs/references/kernel/metadata-persistence.mdx
The two baselines this branch edits by hand — `json-schema.manifest.json` and `authorable-surface.json` — came out with `—` escaped as `—`, because the edit went through Python's `json.dump`, whose `ensure_ascii` defaults to true. `build-schemas.ts` writes them with `JSON.stringify`, which emits the character literally. No gate catches this: the manifest is only rewritten when the SCHEMA KEY SET changes, so the escape would have sat in the file until the next PR that adds a schema, where the generator would silently rewrite it back and hand that author an unrelated one-line diff to explain. Re-serialised with `ensure_ascii=False`. Both files now differ from main by exactly the intended removals and nothing else: 11 manifest keys, 65 authorable-surface lines, zero incidental churn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
…-event-dual-source-v3jw3v # Conflicts: # content/docs/releases/v17.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4411.
@objectstack/specexported eleven names twice, with a different shape each time, on two subpath entries — so which type a consumer got depended on nothing but the import path:This removes the
kernel/metadata-loader.zodcopies ofMetadataFormat,MetadataStats,MetadataLoadOptions,MetadataSaveOptions,MetadataExportOptions,MetadataImportOptions,MetadataLoadResult,MetadataSaveResult,MetadataWatchEvent,MetadataCollectionInfoandMetadataLoaderContract(plus each one's…Schema).system/metadata-persistence.zodstays as the single source.对 #4411 范围的一处修正
issue 记录的是
MetadataWatchEvent一个同名双源。核实下来,同一个文件里还有 10 个同类,机制完全相同 —— 所以本 PR 一并退役了 11 个,而不是 1 个。只删被记录过的那一个,会留下九个一模一样的陷阱,而且恰好把唯一有文档的那个拆掉了。
为什么删 kernel 侧
cloud+objectui)import 语句级扫描:11 个名字的所有消费方都从./system导入(export/import options 走./contracts自有 interface)。只有kernel/metadata-loader.test.ts在 parse kernel 版。ADR-0049 enforce-or-remove。.describe()—— 而它是死的;活的是宽松超集,其消费方在自己的注释里管它叫 "legacy"。按名字选、或按"哪个看起来更严谨"选,都选中死的那份;而两份形状大量重叠,所以选错照样编译,只在边缘值(addvsadded)或必填性上翻车。退役路线
这些是运行时信封类型,不是可作者化 metadata —— 没有任何 load path 会解析它们,任何作者写的源码都不可能携带。所以刻意不加
retiredKey()tombstone、不加 ADR-0087 conversion:收不到的处方是噪音,os migrate meta也没有源码可改写(plugin-runtime.zod.ts/ dev-plugin 家族先例,#3950、#4149)。FROM → TO
改 import 路径,名字不变:
存活的
system版是更宽松的一份,所以读取方可能需要新增收窄,生产方不需要任何改动。差异明细见 changeset;要点:MetadataWatchEvent.type多add/change/unlink,metadataType/name/timestamp为可选;MetadataStats全部可选。没有运行时行为变化 —— 没有东西读过被删的副本。本 PR 不收紧
system侧任何形状(那是独立的行为变更)。未受影响
MetadataManagerConfig和MetadataFallbackStrategy不动 —— 它们从未重复(kernel 拥有、system re-export),而这正是保留下来的切分:manager wiring 归 kernel,loader/watch envelope 归 system,没有任何东西声明两遍。MetadataManagerConfig.formats原本引用被删的 kernelMetadataFormatSchema,改为引shared/metadata-types.zod的同名 schema(四个成员完全一致,叶子模块无循环)。没用system版 —— 那份含yml/ts/js别名,采用它会悄悄放宽这个 config 接受的值。顺带
contracts/metadata-service.ts删掉 feat(spec,objectql): IObjectQLEngine —— objectql 槽的契约落地,类 implements 之,七个本地 stand-in 退场 (#4251 B3) #4404 加的「spec carries TWO types named MetadataWatchEvent」警示注释 —— 现在不再是这样了。packages/qa/dogfood/test/expression-conformance.ledger.ts删掉已不存在的kernel/metadata-loader.zod.ts:filterCEL 面。全仓测试抓到的,不是预先想到的。附带确认:存活的system版那两个 options 从来没有filter字段,所以这个 CEL 面从未被求值过,没有东西需要重新指向。生成物
json-schema.manifest.json−11 条、authorable-surface.json−65 行,刻意手工删除(gate (a) 的 vanished-key 路径;没有东西能作者化它们,所以没有[RETIRED]标记)api-surface.json重新生成:./kernel少 22 个导出,0 added / 0 narrowedreferences/kernel/metadata-persistence.mdx由gen:docs移除验证
check:api-surface、check:authorable-surface、check:liveness)check:generated八件套全部 up to datepnpm typecheck122 tasks 全过;pnpm test132 tasks 全过main(含 docs(spec): SystemFieldName says which columns are actually injected #4430 / fix(spec,objectql,metadata-protocol): auserfield carries its target in the TYPE — bare{type:'user'}is not targetless #4438 / fix(automation): enforce isDefault and stop swallowing an unclaimable branch label (#4414) #4440)后重新 build 并完整复跑一遍 —— fix(spec,objectql,metadata-protocol): auserfield carries its target in the TYPE — bare{type:'user'}is not targetless #4438 也改了api-surface.json,git 的文本合并结果是对的,但这是在合并后的源码上重新构建验证出来的,不是假定的相关
follow-up #4446(跨 barrel 同名导出检查):本 PR 清掉了一个同名双源家族,产生机制没堵。基线已实测(本 PR 后仍有 83 条跨子路径同名导出)。
Generated by Claude Code