fix(cordis): preserve concurrent loader composition

This commit is contained in:
Tianyi Cui
2026-07-30 06:23:16 +08:00
parent 2fe205f01a
commit 0a297c39d3
6 changed files with 18 additions and 11 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-20-config-hot-reload-resilience.md
2026-07-20-config-hot-reload-resilience.md: f3c36f8055179870c19c9d1ce99c3533fe602aa6
2026-07-20-config-hot-reload-resilience.zh.md: 72ef2ebfa582dcc614198ed094c9b58ea1713460
2026-07-20-config-hot-reload-resilience.md: 0f15bb0aaacb6e06c416cbe35b44155279497eee
2026-07-20-config-hot-reload-resilience.zh.md: 8a185c1915b5247150d8bb1dd5c42d69bd4f2a35

View File

@@ -14,7 +14,7 @@ The vendored Cordis lifecycle and Loader plugins provide an awaited, compensatin
`Fiber.update()` returns its `internal/update` waterfall result. Config validation remains synchronous, while the default continuation returns the restart promise. Loader entry updates can therefore distinguish validation, import, application, and rollback failure from successful lifecycle settlement. `EntryTree.await()` rechecks service-gated fibers after Loader tasks drain and rejects settled failures; a fiber waiting on an absent service remains a valid pending entry rather than making settlement hang.
Loader imports a changed module name before disposing the active fiber. Candidate application is awaited; a failure disposes candidate effects and restores the prior plugin or config. Group reconciliation is sequential and restores earlier changed entries, additions, removals, and moves before rejecting. Persistence occurs only after successful programmatic mutation. This is a compensating transaction: lifecycle effects may be briefly visible, and a failed rollback is reported as an `AggregateError` rather than misrepresented as a retained tree.
Loader imports a changed module name before disposing the active fiber. Candidate application is awaited; a failure disposes candidate effects and restores the prior plugin or config. Group reconciliation starts candidates concurrently, awaits every outcome, and restores changed entries, additions, removals, and moves before rejecting. Persistence occurs only after successful programmatic mutation. This is a compensating transaction: lifecycle effects may be briefly visible, and a failed rollback is reported as an `AggregateError` rather than misrepresented as a retained tree.
Include reads and validates detached candidate content, applies patches to a clone, reconciles the Loader tree, and only then commits cached content and parsed data. `refresh()` rejects to its caller after a parse, validation, application, or rollback failure. Initial load remains fail-loud; only an absent file may use `initial`. A non-array YAML/JSON result is invalid, and both file refresh and Include-config update re-apply patches without mutating the cached parse.
@@ -26,7 +26,7 @@ HMR contains live refresh rejection. Its `registerConfig(filename, refresh)` met
**Restart the process for every config edit.** Rejected because Cordis effects already provide reversible plugin lifecycle, and a syntax error or failed optional plugin must not discard live sessions merely to recover the prior composition.
**Promise invisible atomic replacement.** Rejected because arbitrary plugin effects cannot be snapshotted. Sequential application plus explicit compensation provides a stable final result without claiming that observers cannot see intermediate lifecycle transitions.
**Promise invisible atomic replacement.** Rejected because arbitrary plugin effects cannot be snapshotted. Awaited application plus explicit compensation provides a stable final result without claiming that observers cannot see intermediate lifecycle transitions.
## Consequences
@@ -38,4 +38,4 @@ HMR contains live refresh rejection. Its `registerConfig(filename, refresh)` met
## Testing
`packages/ui/app-boot/tests/config-reload.spec.ts` boots real temporary Loader/Include trees and covers parse and shape rejection, import-before-dispose, plugin/config restoration, multi-entry rollback, ancestor disablement, overlay convergence, option identity, failed direct-update persistence, and failed programmatic moves. `packages/ui/app-boot/tests/hmr-config.spec.ts` covers existing and missing exact paths, add/change/removal, serialized coalescing, disposal drainage, non-`Error` normalization, failure broadcast, and rejecting-observer containment. `packages/host/webserver/tests/webserver.spec.ts` proves a service-gated startup failure rejects Loader composition with its bind diagnostic, and `packages/typert/loader/tests/loader.spec.ts` exercises awaited programmatic removal through a real Loader consumer.
`packages/ui/app-boot/tests/config-reload.spec.ts` boots real temporary Loader/Include trees and covers parse and shape rejection, import-before-dispose, plugin/config restoration, multi-entry rollback, ancestor disablement, overlay convergence, option identity, failed direct-update persistence, and failed programmatic moves. `packages/ui/app-boot/tests/hmr-config.spec.ts` covers existing and missing exact paths, add/change/removal, serialized coalescing, disposal drainage, non-`Error` normalization, failure broadcast, and rejecting-observer containment. `packages/host/webserver/tests/webserver.spec.ts` proves a service-gated startup failure rejects Loader composition with its bind diagnostic, `packages/typert/loader/tests/loader.spec.ts` exercises awaited programmatic removal through a real Loader consumer, and the ACP `pty-tools` snapshot guards concurrent composition from reordering equal-priority prompt sections.

View File

@@ -14,7 +14,7 @@ vendor 中的 Cordis 生命周期和 Loader 插件提供可等待、带补偿的
`Fiber.update()` 返回其 `internal/update` waterfall瀑布式事件的结果。配置校验保持同步而默认 continuation 返回重启 promise。因此Loader 配置项更新可以区分校验、导入、应用和回滚失败,以及生命周期成功完成。`EntryTree.await()` 会在 Loader 任务排空后重新检查受服务门控的 fiber并在 fiber 已结算为失败时 reject等待缺失服务的 fiber 仍是有效的 pending 配置项,不会让结算挂起。
Loader 会先导入变化后的模块名,再 dispose资源释放活动 fiber。它会 await 候选项的应用;若失败,则 dispose 候选项的 effect并恢复先前的插件或配置。组内对账按顺序进行,并会在拒绝前恢复此前已变更的配置项、添加项、移除项和移动项。只有程序化变更成功后才会持久化。这是一种补偿事务:生命周期 effect 可能短暂可见;回滚失败会报告为 `AggregateError`,而不会被误称为树已保留。
Loader 会先导入变化后的模块名,再 dispose资源释放活动 fiber。它会 await 候选项的应用;若失败,则 dispose 候选项的 effect并恢复先前的插件或配置。组内对账会并发启动各候选项,等待每项结果,并会在拒绝前恢复已变更的配置项、添加项、移除项和移动项。只有程序化变更成功后才会持久化。这是一种补偿事务:生命周期 effect 可能短暂可见;回滚失败会报告为 `AggregateError`,而不会被误称为树已保留。
Include 读取并校验尚未提交的候选内容,把补丁应用到其副本,对账 Loader 树,然后才提交缓存内容和解析数据。解析、校验、应用或回滚失败后,`refresh()` 会向调用方 reject。初始加载继续快速失败只有文件不存在时才可以使用 `initial`。YAML/JSON 结果若不是数组即为无效;文件刷新和 Include 配置更新都会重新应用补丁,且不修改缓存的解析结果。
@@ -26,7 +26,7 @@ HMR 收容实时刷新 rejection。其 `registerConfig(filename, refresh)` 方
**每次编辑配置都重启进程。** 已否决,因为 Cordis effect 已经提供可逆的插件生命周期,而语法错误或可选插件失败不应只为恢复先前的组合就丢弃正在进行的会话。
**承诺不可见的原子替换。** 已否决,因为任意插件 effect 无法制作快照。按顺序应用并显式补偿可以得到稳定的最终结果,同时不会声称观察者看不到中间生命周期转换。
**承诺不可见的原子替换。** 已否决,因为任意插件 effect 无法制作快照。等待应用完成并显式补偿可以得到稳定的最终结果,同时不会声称观察者看不到中间生命周期转换。
## Consequences
@@ -38,4 +38,4 @@ HMR 收容实时刷新 rejection。其 `registerConfig(filename, refresh)` 方
## Testing
`packages/ui/app-boot/tests/config-reload.spec.ts` 启动真实的临时 Loader/Include 树,并覆盖对解析和形状错误的拒绝、先导入再 dispose、插件/配置恢复、多配置项回滚、祖先禁用、overlay 收敛、option 对象身份、失败的直接更新不持久化以及失败的程序化移动。`packages/ui/app-boot/tests/hmr-config.spec.ts` 覆盖现有和缺失的确切路径、添加/变更/移除、串行化合并、dispose 排空、非 `Error` 值的规范化、失败广播以及对发生 rejection 的观察者的收容。`packages/host/webserver/tests/webserver.spec.ts` 证明受服务门控的启动失败会让 Loader 组合以其 bind 诊断 reject`packages/typert/loader/tests/loader.spec.ts` 则通过真实 Loader 消费方演练可等待的程序化移除。
`packages/ui/app-boot/tests/config-reload.spec.ts` 启动真实的临时 Loader/Include 树,并覆盖对解析和形状错误的拒绝、先导入再 dispose、插件/配置恢复、多配置项回滚、祖先禁用、overlay 收敛、option 对象身份、失败的直接更新不持久化以及失败的程序化移动。`packages/ui/app-boot/tests/hmr-config.spec.ts` 覆盖现有和缺失的确切路径、添加/变更/移除、串行化合并、dispose 排空、非 `Error` 值的规范化、失败广播以及对发生 rejection 的观察者的收容。`packages/host/webserver/tests/webserver.spec.ts` 证明受服务门控的启动失败会让 Loader 组合以其 bind 诊断 reject`packages/typert/loader/tests/loader.spec.ts` 则通过真实 Loader 消费方演练可等待的程序化移除ACPAgent Client Protocol`pty-tools` 快照会防止并发组合改变同优先级提示词段的顺序