|
| 1 | +--- |
| 2 | +"@objectstack/spec": major |
| 3 | +--- |
| 4 | + |
| 5 | +refactor(spec)!: remove the `kernel` metadata-loader envelope family — eleven names that each existed twice, with different shapes, on two subpath entries (#4411) |
| 6 | + |
| 7 | +`MetadataFormat`, `MetadataStats`, `MetadataLoadOptions`, `MetadataSaveOptions`, |
| 8 | +`MetadataExportOptions`, `MetadataImportOptions`, `MetadataLoadResult`, |
| 9 | +`MetadataSaveResult`, `MetadataWatchEvent`, `MetadataCollectionInfo` and |
| 10 | +`MetadataLoaderContract` (plus each one's `…Schema`) are removed from |
| 11 | +`@objectstack/spec/kernel` (`kernel/metadata-loader.zod`). Every one of those |
| 12 | +names *also* existed, with a **different shape**, in |
| 13 | +`@objectstack/spec/system` (`system/metadata-persistence.zod`). |
| 14 | + |
| 15 | +Which type you got depended on nothing but your import path: |
| 16 | + |
| 17 | +```ts |
| 18 | +import type { MetadataWatchEvent } from '@objectstack/spec/kernel'; // one shape |
| 19 | +import type { MetadataWatchEvent } from '@objectstack/spec/system'; // another |
| 20 | +``` |
| 21 | + |
| 22 | +- **The `kernel` copies had zero consumers.** Import-statement scans across this |
| 23 | + repo, `cloud` and `objectui` found every consumer importing from |
| 24 | + `./system` (or, for the export/import options, `./contracts`' own interface). |
| 25 | + Nothing but `kernel/metadata-loader.test.ts` ever parsed the `kernel` copies. |
| 26 | +- **The naming intuition pointed the wrong way**, which is what made this worse |
| 27 | + than an ordinary duplicate. The `kernel` copies were the ones that *looked* |
| 28 | + canonical — normalized enums, required fields, a `.describe()` on every |
| 29 | + property — and they were the dead ones. The live copy is the loose superset, |
| 30 | + and `metadata-manager.ts` calls it "legacy" in its own comments. An |
| 31 | + auto-import or a model completion picking by name, or by which one reads as |
| 32 | + more rigorous, picked the dead one; because the shapes overlap heavily, that |
| 33 | + choice compiled and only failed later, at an edge value (`add` vs `added`) or |
| 34 | + on a field one copy made required. |
| 35 | +- **No load path parsed them.** These are runtime envelope types, not authorable |
| 36 | + metadata — no authored source can carry them. So there is deliberately **no** |
| 37 | + `retiredKey()` tombstone and **no** ADR-0087 conversion: a prescription nobody |
| 38 | + can receive is noise, and there is nothing for `os migrate meta` to rewrite |
| 39 | + (the `plugin-runtime.zod.ts` / dev-plugin precedents, #3950, #4149). |
| 40 | + |
| 41 | +**FROM → TO — change the import path, keep the name:** |
| 42 | + |
| 43 | +```diff |
| 44 | +-import type { MetadataWatchEvent, MetadataStats } from '@objectstack/spec/kernel'; |
| 45 | ++import type { MetadataWatchEvent, MetadataStats } from '@objectstack/spec/system'; |
| 46 | +``` |
| 47 | + |
| 48 | +The surviving `system` copy is the **looser** of the two, so a *reader* of these |
| 49 | +types may need narrowing it did not need before; a *producer* needs nothing. The |
| 50 | +differences that actually bite: |
| 51 | + |
| 52 | +| Type | `kernel` (removed) | `system` (keep) | |
| 53 | +| --- | --- | --- | |
| 54 | +| `MetadataWatchEvent.type` | `'added' \| 'changed' \| 'deleted'` | also `'add' \| 'change' \| 'unlink'` — the raw watcher values the runtime really emits | |
| 55 | +| `MetadataWatchEvent` | `metadataType` / `name` / `timestamp` required | all three optional; adds `stats` | |
| 56 | +| `MetadataStats` | `size` / `modifiedAt` / `etag` / `format` required | all optional; adds `mtime`, `hash` | |
| 57 | +| `MetadataFormat` | `json \| yaml \| typescript \| javascript` | also the `yml` / `ts` / `js` aliases | |
| 58 | +| `MetadataSaveResult.path` | required | optional; adds `stats` | |
| 59 | +| `MetadataImportOptions` | `conflictResolution` / `dryRun` / `continueOnError` / `transform` | `source` / `strategy` / `validate` | |
| 60 | +| `MetadataCollectionInfo` | `formats: MetadataFormat[]` | `namespaces: string[]` | |
| 61 | + |
| 62 | +No runtime behaviour changes: nothing read the removed copies. The `system` |
| 63 | +shapes are **not** tightened here — they describe what `MetadataManager` |
| 64 | +actually emits, and narrowing them would be a separate behaviour change. |
| 65 | + |
| 66 | +`MetadataManagerConfig` and `MetadataFallbackStrategy` are **unaffected**. They |
| 67 | +were never duplicated — `kernel` owns them and `system` re-exports them — and |
| 68 | +that is the split that survives: manager *wiring* is kernel's, the loader/watch |
| 69 | +*envelope* is system's, and nothing is declared twice. |
| 70 | + |
| 71 | +The retirement kit: baselines dropped deliberately |
| 72 | +(`json-schema.manifest.json` minus the 11 `kernel/Metadata*` entries; |
| 73 | +`authorable-surface.json` minus the 65 matching lines — nothing can author |
| 74 | +these, so no `[RETIRED]` markers); `api-surface.json` regenerated (22 exports |
| 75 | +leave `./kernel`); `references/kernel/metadata-persistence.mdx` removed by |
| 76 | +`gen:docs`; v17 release notes' dead-clusters table and upgrade checklist |
| 77 | +extended. No liveness-ledger entries existed (the ledger tracks authorable |
| 78 | +metadata types; these were never one). |
0 commit comments