同一个 id,为什么保留两棵树?
从替换 standard 声明后的时点开始,比较旧 Agent A、继承旧代的子 Scope C,以及新 Agent B。
01 / 沿退出过程检查
A 与 C 都保留 G1
A 仍使用 old-tool,子 Scope C 继承 G1;新 Agent B 使用 G2 的 new-tool。
旧 G1:users = 2 · retired = true | 新 G2:users = 1 · retired = false
C 继续保留 G1
A 的引用已经释放;C 仍持有 G1,旧树继续存在。
旧 G1:users = 1 · retired = true | 新 G2:users = 1 · retired = false
G1 可以回收
旧代已退役且没有使用者,Scope 退出;G2 和 B 继续运行。
旧 G1:users = 0 · retired = true | 新 G2:users = 1 · retired = false
旧 G1 → A、Cusers = 2,retired = true;A 与 C 都保留 G1。
新 G2 → B当前 standard 声明指向 G2,B 看到 new-tool。
旧 G1 → A、Cusers = 1,retired = true;C 继续保留 G1。
新 G2 → B当前 standard 声明指向 G2,B 看到 new-tool。
旧 G1 → A、Cusers = 0,retired = true;G1 可以回收。
新 G2 → B当前 standard 声明指向 G2,B 看到 new-tool。
02 / 声明、工具与回收分别检查
A: old-tool C: 继承 G1 的 old-tool B: new-tool 根作用域没有这两套 Preset 工具。
A: 已退出 C: 继承 G1 的 old-tool B: new-tool 根作用域没有这两套 Preset 工具。
A: 已退出 C: 已退出 B: new-tool 根作用域没有这两套 Preset 工具。
当前声明列表: standard → G2 实际保留修订: G1、G2 list() 读声明;inspectCompositions() 读保留代。
当前声明列表: standard → G2 实际保留修订: G1、G2 list() 读声明;inspectCompositions() 读保留代。
当前声明列表: standard → G2 实际保留修订: G2 list() 读声明;inspectCompositions() 读保留代。
G1.retired = true G1.users = 2 collect: 等待最后一个使用者退出 图中 users 只统计 A、C 和 B;只读租约也可以持有引用。
G1.retired = true G1.users = 1 collect: 等待最后一个使用者退出 图中 users 只统计 A、C 和 B;只读租约也可以持有引用。
G1.retired = true G1.users = 0 collect: 释放 G1 Scope 图中 users 只统计 A、C 和 B;只读租约也可以持有引用。
查看实验依据与边界
来自本文真实 AgentLoop 与 Preset Registry 的修订实验。C 是通过 composeFrom 加入旧代的子 Scope,未执行子 Agent 的模型任务。G1/G2 为解释用代号;图中没有画临时 retain 引用和读取租约。
composeFrom 继承父 Scope 实际加入的代,不按 id 重新选择最新声明。Scope 父链表示贡献可见性,不能据此推断 parentAgent 或调用方 Fiber 所有权。