diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.i18n.yaml b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.i18n.yaml index 763bc61176..81fc6c8fac 100644 --- a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.i18n.yaml +++ b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.i18n.yaml @@ -3,14 +3,14 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md /: - en: 4b668dd74d0315b9 - zh: a1885d2bbb4ef33e + en: 02dfa517303f3206 + zh: abf9a22603299cc7 /loader-entry-tree-reports-and-include-persistence: en: dc27815908c90cd9 zh: e9cc077d4d3fdbec /loader-entry-tree-reports-and-include-persistence/change: - en: 2436912b9554b6dd - zh: ff4582ebc4672662 + en: 6ada19e725d5c04e + zh: efbc6183cfd094e1 /loader-entry-tree-reports-and-include-persistence/migration: - en: cd7db13e17bfd339 - zh: be43c51c78e55439 + en: 574f30ad53da1e66 + zh: 1b9914c4ead412f7 diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md index 764cc06772..566d87df9b 100644 --- a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md +++ b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md @@ -1,6 +1,6 @@ --- kind: upgrade-guide -description: "Loader entry trees report mutations through commit(change), reject ids containing a colon, and Include persists them through a journal." +description: "Loader entry trees report mutations through commit(change), and Include persists them through a journal." --- # Loader entry tree reports and Include persistence @@ -12,14 +12,12 @@ English | [中文](guide.zh.md) `@deepseek-ai/cordis-plugin-loader` follows Loader 1.0.0-rc.8, and `@deepseek-ai/cordis-plugin-include` follows Include 1.1.1. This affects `cordis.yml` and patch files, code that subclasses `EntryTree`, and code that calls `applyEntryPatches` or relies on Include writes. - `EntryTree.write()` is removed. Subclasses implement `commit(change: EntryChange)`, which receives each create, update, move, removal and explicit Fiber save synchronously. In-memory trees implement a no-op. -- An entry `id` containing `:` is rejected, because `:` separates nested tree paths. A configured row such as `id: foo:bar` previously mounted; it is now logged as an error and skipped, and a programmatic `create()` throws. - `applyEntryPatches(data, patches, warn)` always returns cloned entries, including without patches, and clones inserted rows. Callers can no longer mutate the input through the result. - Include persists runtime changes through a journal reconciled with the latest file. File edits win conflicting fields and are logged as conflicts. Changes to patch-owned fields and inserted rows update the parent Include's patches. `flush()` waits for pending writes and rejects while changes remain unsaved; `refresh()` and `stop()` return the same result. ## Migration -1. Rename entry ids that contain `:` in `cordis.yml`, profile patches and `--patch` overlays, for example `foo:bar` to `foo-bar`, and update patches that address them. -2. In `EntryTree` subclasses, replace `write()` with `commit(change)`. Persist from `change.options` and `change.legacy`: a missing `legacy` is a creation and a missing `options` is a removal. -3. Treat the `applyEntryPatches` result as the only copy to modify. -4. Where code needs a runtime change on disk, await `include.flush()`; for patch-owned changes, flush the child Include and then its parent. -5. Start the application and confirm that no `entry id must not contain ":"` errors are logged and that runtime edits appear in the configuration file after `flush()`. +1. In `EntryTree` subclasses, replace `write()` with `commit(change)`. Persist from `change.options` and `change.legacy`: a missing `legacy` is a creation and a missing `options` is a removal. +2. Treat the `applyEntryPatches` result as the only copy to modify. +3. Where code needs a runtime change on disk, await `include.flush()`; for patch-owned changes, flush the child Include and then its parent. +4. Start the application and confirm that runtime edits appear in the configuration file after `flush()`. diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.zh.md b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.zh.md index 74cd3f6ec4..fc38566b63 100644 --- a/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.zh.md +++ b/docs/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.zh.md @@ -1,6 +1,6 @@ --- kind: upgrade-guide -description: "Loader 条目树通过 commit(change) 报告变更并拒绝含冒号的 id,Include 通过 journal 持久化这些变更。" +description: "Loader 条目树通过 commit(change) 报告变更,Include 通过 journal 持久化这些变更。" --- # Loader 条目树变更报告与 Include 持久化 @@ -12,14 +12,12 @@ description: "Loader 条目树通过 commit(change) 报告变更并拒绝含冒 `@deepseek-ai/cordis-plugin-loader` 跟随 Loader 1.0.0-rc.8,`@deepseek-ai/cordis-plugin-include` 跟随 Include 1.1.1。受影响的是 `cordis.yml` 与 patch 文件、继承 `EntryTree` 的代码,以及调用 `applyEntryPatches` 或依赖 Include 写盘的代码。 - 移除了 `EntryTree.write()`。子类改为实现 `commit(change: EntryChange)`,它会同步收到每一次创建、更新、移动、删除和显式 Fiber 保存。内存树实现为空操作。 -- 含 `:` 的条目 `id` 会被拒绝,因为 `:` 用于分隔嵌套树路径。此前 `id: foo:bar` 这样的配置行可以挂载;现在会记录一条错误并跳过该行,程序化调用 `create()` 会抛出异常。 - `applyEntryPatches(data, patches, warn)` 总是返回克隆后的条目,没有 patch 时也一样,插入行同样会被克隆。调用方不能再通过结果修改输入。 - Include 通过与最新文件协调的 journal 持久化运行时变更。文件编辑在字段冲突时优先,并记录为冲突。对 patch 所拥有字段和插入行的修改会写入父 Include 的 patch。`flush()` 等待待处理的写入,并在仍有未保存变更时拒绝;`refresh()` 和 `stop()` 返回相同的结果。 ## 迁移 -1. 在 `cordis.yml`、profile patch 和 `--patch` 覆盖层中重命名含 `:` 的条目 id,例如把 `foo:bar` 改为 `foo-bar`,并同步更新引用它们的 patch。 -2. 在 `EntryTree` 子类中把 `write()` 换成 `commit(change)`。根据 `change.options` 和 `change.legacy` 持久化:缺少 `legacy` 表示创建,缺少 `options` 表示删除。 -3. 只修改 `applyEntryPatches` 返回的结果。 -4. 需要运行时变更落盘时,await `include.flush()`;对于 patch 所拥有的变更,先 flush 子 Include,再 flush 其父 Include。 -5. 启动应用,确认没有记录 `entry id must not contain ":"` 错误,并且 `flush()` 之后运行时编辑已出现在配置文件中。 +1. 在 `EntryTree` 子类中把 `write()` 换成 `commit(change)`。根据 `change.options` 和 `change.legacy` 持久化:缺少 `legacy` 表示创建,缺少 `options` 表示删除。 +2. 只修改 `applyEntryPatches` 返回的结果。 +3. 需要运行时变更落盘时,await `include.flush()`;对于 patch 所拥有的变更,先 flush 子 Include,再 flush 其父 Include。 +4. 启动应用,确认 `flush()` 之后运行时编辑已出现在配置文件中。 diff --git a/scripts/cordis-entry-commit.spec.ts b/scripts/cordis-entry-commit.spec.ts index 912335fcec..e915af7b29 100644 --- a/scripts/cordis-entry-commit.spec.ts +++ b/scripts/cordis-entry-commit.spec.ts @@ -85,12 +85,14 @@ describe('Loader tree mutation reports', () => { expect(changes).toEqual([]) }) - it('rejects separator-containing ids before changing a tree', async () => { + it('mounts explicit ids containing the separator without nested-path resolution', async () => { const { loader, changes } = await fixture() const options: EntryOptions = { id: 'a:b', name: 'cordis:example' } - await expect(loader.create(options)).rejects.toThrow('entry id must not contain') - expect(loader.root.data).toEqual([]) - expect(changes).toEqual([]) + await expect(loader.create(options)).resolves.toBe('a:b') + await loader.await() + expect(loader.store['a:b']!.fiber!.state).toBe(FiberState.ACTIVE) + expect(changes).toEqual([{ id: 'a:b', group: loader.root, options }]) + expect(() => loader.resolve('a:b')).toThrow('cannot resolve entry a:b') }) }) diff --git a/vendor/README.md b/vendor/README.md index d39dfae8d8..2c6e377a2a 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -30,7 +30,7 @@ Intentionally **not** vendored (verified unused by this set): `reggol`, `@cordis ## Local modifications -Keep this log exhaustive — every divergence from upstream must be listed. Each entry has a stable ID: remove an entry when its divergence no longer exists, and never renumber or reuse IDs. The next new entry is V27. +Keep this log exhaustive — every divergence from upstream must be listed. Each entry has a stable ID: remove an entry when its divergence no longer exists, and never renumber or reuse IDs. The next new entry is V28. - **V1 · `hmr/src/index.ts`**: removed the `./locales/en-US.yml` / `./locales/zh-CN.yml` imports, the `.i18n({...})` call on the `Config` schema, and the `src/locales/` directory. Rationale: those imports require a runtime YAML loader hook (`@cordisjs/unyaml`) that we do not vendor; the i18n texts only localize config descriptions. - **V2 · All `package.json` files**: regenerated for Harness releases with scoped names, release versions, publication metadata, precise bundled-runtime and `lib/types/**/*.d.ts` / `.d.ts.map` file entries, source exports where applicable, and declaration metadata pointing at `lib/types`. References within the vendored set use `workspace:~`. HMR declares `esbuild` as a direct dev dependency for its imported `BuildFailure` type, and Loader requires `node-addon-require-builtin@^0.1.4` to match published app runtimes. - **V3 · All `tsconfig.json` files**: regenerated to extend the repo-root `tsconfig.base.json`, emit TypeScript intermediates to `lib/types`, and declare project references. @@ -50,6 +50,7 @@ Keep this log exhaustive — every divergence from upstream must be listed. Each - **V24 · `timer/locale/{en,zh}.json` display metadata**: local English and Chinese titles and descriptions for the Settings plugin inventory. `timer/package.json` exports and publishes these locale resources. - **V25 · Loader lifecycle adaptations**: before a combined context/config update, Loader refreshes dependencies locally, which prevents activation against stale isolation bindings; disabled-entry updates await disposal. Failed fibers require an explicit update after correcting their input or dependencies. Core retains the effect hardening in V6; rc.11 owns dependency teardown ordering and superseded-activation handling. - **V26 · `loader/src/config/tree.ts` cross-tree move reports**: `EntryTree.update()` reports a move into another tree as a removal to the source tree and a creation, without `legacy` or `from`, to the destination tree. Upstream reports the destination as an update with `legacy`, which the Include journal records as a partial row for an id that its file does not contain. Covered by `scripts/cordis-entry-commit.spec.ts`. +- **V27 · `loader/src/config/tree.ts` explicit ids with `:`**: `ensureId()` accepts explicit ids containing `EntryTree.sep`; upstream rejects them. Generated bundles from `@deepseek-ai/dsh-creation-recommendations` prefix their row ids with `:`, and installed profiles already contain such rows. `resolve()` still splits ids on `:`, so these entries cannot be addressed by nested path. Covered by `scripts/cordis-entry-commit.spec.ts` and `packages/experimental/creation-recommendations/tests/loader-composition.spec.ts`. ### Resolver adaptations diff --git a/vendor/loader/README.md b/vendor/loader/README.md index ae7e82a6f2..0b17513be8 100644 --- a/vendor/loader/README.md +++ b/vendor/loader/README.md @@ -68,4 +68,4 @@ Entry updates await `loader/patch-context`, the Fiber update waterfall and disab ## Tree mutation reports -`EntryTree.commit(change)` receives each mutation made through `EntryTree` methods synchronously after options and group membership change; file-driven `EntryGroup` reconciliation does not report. A missing `legacy` means creation; missing `options` means removal. Updates carry both, with `from` naming the previous group after a move within the tree; a move into another tree reports a removal to the source tree and a creation to the destination tree. Explicit Fiber saves report their simplified configuration. File-driven reconciliation, parent teardown and Loader removal do not produce spurious self-disable reports. In-memory trees implement a no-op; Include journals the reports and reconciles them with file edits. Entry ids cannot contain `:`, which separates nested tree paths. +`EntryTree.commit(change)` receives each mutation made through `EntryTree` methods synchronously after options and group membership change; file-driven `EntryGroup` reconciliation does not report. A missing `legacy` means creation; missing `options` means removal. Updates carry both, with `from` naming the previous group after a move within the tree; a move into another tree reports a removal to the source tree and a creation to the destination tree. Explicit Fiber saves report their simplified configuration. File-driven reconciliation, parent teardown and Loader removal do not produce spurious self-disable reports. In-memory trees implement a no-op; Include journals the reports and reconciles them with file edits. Explicit entry ids may contain `:`; `resolve()` cannot address such entries by nested path, because `:` separates nested tree paths. diff --git a/vendor/loader/src/config/tree.ts b/vendor/loader/src/config/tree.ts index 2d99fb68d8..f8c05dfefe 100644 --- a/vendor/loader/src/config/tree.ts +++ b/vendor/loader/src/config/tree.ts @@ -73,18 +73,17 @@ export abstract class EntryTree { } /** - * Assign a random tree-local id when `options.id` is absent. + * Assign a random tree-local id when `options.id` is absent. Explicit ids + * may contain `EntryTree.sep`; `resolve()` and `resolveGroup()` cannot + * address such entries by nested path. * @param options - Entry options; a generated id is written to `options.id`. * @returns The existing or generated id. - * @throws When an explicit id contains `EntryTree.sep`, which separates nested tree paths. */ ensureId(options: Partial) { if (!options.id) { do { options.id = Math.random().toString(16).slice(2, 10) } while (this.store[options.id]) - } else if (options.id.includes(EntryTree.sep)) { - throw new Error(`entry id must not contain "${EntryTree.sep}": ${options.id}`) } return options.id! }