背景
Holon 已经分别具备:
TaskRecord 的单调生命周期与 terminal result 持久化;
WaitFor(wake=task_result) / exact task rejoin;
InvokeAgent(new_subagent | existing_agent);
- agent message delivery ledger、activation / turn / WorkItem continuation;
- stale / duplicate rejoin 的局部拒绝逻辑。
但这些机制之间尚缺少一个统一、持久、可恢复的 task result settlement 状态机。当前尤其容易出问题的是 InvokeAgent(existing_agent):调用方创建 invocation task,目标侧接收一条带 delivery / task correlation 的消息,monitor 再从目标 agent 的 activation、turn 和后续 continuation 中推断“这次调用是否已经回复”。
这仍然是在用目标 agent 的执行终局推断调用结果,而不是用一次 invocation 独占的 result identity 明确结算。目标 agent 是长期存在、可并发接收消息、可休眠/离线/重启、也可进入 WorkItem continuation 的主体,因此“目标出现了一个 terminal/replied turn”不必然等于“它回复了这次调用”。
P0 问题
需要定义 task result settlement 的 canonical 状态机,至少覆盖以下路径:
- inline:结果在 requester 当前 execution/turn 仍可安全接纳时产生;
- late:requester 已结束当前 turn,或已注册/尚未注册 task wait 后结果到达;
- offline:requester 或 target 不在内存中,经历 daemon restart / recovery 后仍能完成;
- caller-admitted:结果已经通过 requester 侧 canonical scheduler admission,明确绑定到唯一 caller execution;
- duplicate-suppressed:同一结果因 producer retry、delivery retry、recovery replay 或重复 wake 再次出现时,只返回既有 settlement,不再创建 turn、重开 task 或重复交付。
没有这层状态机时,会出现以下语义风险:
- existing agent 同时处理其他消息时,错误的 terminal turn/brief 被识别为本次 invocation reply;
- 同一 target 上的多个 invocation 并发或乱序完成时,结果串线;
- result 先于
WaitFor 注册到达,inline 与 late 路径产生不同真相;
- target 已接受 delivery 但随后休眠、离线、重启或进入 continuation,monitor 永久轮询或错误结算;
- requester 已完成/切换 WorkItem 后,结果被静默丢弃、错误重入或重开 terminal task;
- producer、delivery、scheduler 各自做局部去重,但缺少端到端幂等身份。
需要先定义的身份
1. Requester key
必须稳定标识“谁在等待这个结果”,不能仅依赖当前内存态或模糊的 WorkItem 归属。建议至少包含:
- requester agent identity;
- requester-owned task id;
- 创建该 task 的 execution/turn identity(若有);
- requester WorkItem binding + generation(若有)。
WorkItem 可以参与路由,但不能单独充当 requester identity。
2. Spawn / invocation lineage
每次调用都需要不可变 lineage,贯穿:
- invocation task;
- target agent;
- initial delivery;
- target activation / turn;
- recovery attempt;
- WorkItem / task continuation;
- terminal result publication。
对 existing_agent,lineage 表示“这次调用关系”,不能等同于 agent 的创建 parentage,也不能被目标 agent 的其他消息或既有 WorkItem 继承/覆盖。
3. Result identity
每个逻辑结果必须有稳定、可重放的 identity。它应由 requester task + invocation lineage + producer terminal generation/outcome 派生,而不是由“最近一个 turn”或随机 message id 临时决定。
同一逻辑结果的 producer retry、delivery retry、daemon recovery 与 scheduler replay必须复用该 identity;不同 invocation 即使 target、文本和 terminal turn 相似,也不得共享 identity。
建议状态机边界
具体命名可在 RFC 中调整,但至少需要表达以下事实,而不是只用 TaskStatus 代替:
Pending
-> ResultProduced(result_identity, lineage)
-> ResultPersisted
-> InlineEligible | LateQueued | OfflineQueued
-> CallerAdmitted(requester_key, activation_id)
-> Settled(settlement_id)
任意已见 result_identity 的重复输入
-> DuplicateSuppressed(existing settlement)
附加要求:
TaskStatus::Completed/Failed/... 描述 producer/task 终态;settlement 描述结果是否以及如何被 requester 接纳,两者不能混为一个字段。
- terminal task + result publication 必须保持原子或具备明确可恢复的 intent。
- caller admission 必须有 durable fence;仅 enqueue 成功或 target turn terminal 不等于 requester 已接纳。
- requester 已不再允许重入时,结果必须进入可查询的 terminal disposition,而不是无限等待或静默消失。
- duplicate suppression 是正常幂等结果,应有结构化 outcome / audit event,不应只记录 generic invariant violation。
InvokeAgent(existing_agent) 的回复契约
必须明确:
- target 收到 invocation 时,delivery、invocation task 和 lineage 被写入 canonical execution source;
- lineage 必须跨 target 的 yield、wait、task rejoin、WorkItem continuation 与 restart 传播;
- 只有携带该 lineage 的 terminal result publication 才能结算对应 invocation task;
- target 的无关 operator input、peer message、timer、其他 invocation 或已有 WorkItem 的 terminal turn 不得被识别为本次回复;
- target 可以显式产生“成功但无附加文本”“失败”“取消”“拒绝”“中断”等结果,不能靠抓取最近 assistant message 猜测;
- target 不在线时,delivery 与 monitor 可恢复;恢复后不能丢失 requester/result identity;
- 多个 caller 并发调用同一个 existing agent 时,允许乱序完成,但必须一一对应。
验收标准
与现有 issue 的关系
以下 issue 修复或记录了相邻的单点故障,但不替代本 issue 的统一 settlement contract:
这些问题共同说明:局部修复 delivery、turn id、WorkItem binding 或 stale 判定仍不足以保证端到端结果结算。
当前实现定位(2026-09-14,基于 4de34d6f)
src/runtime/agent_services.rs:创建 requester-owned invocation task,并启动 monitor;
src/host.rs::invoke_existing_agent:创建 durable delivery,记录 current_task_id / correlation 与 pre-delivery turn baseline;
src/host.rs::invocation_delivery_terminal_evidence:沿 delivery activation、attempt、WorkItem/task continuation 推断 terminal evidence;
src/runtime/tasks.rs::monitor_spawned_child_agent_task:把推断结果提交为 terminal task result;
src/runtime/scheduler_executor.rs::provably_stale_task_rejoin:在 requester 侧独立判断 exact task rejoin 是否 stale。
需要把上述分散推断收敛为一个 canonical settlement reducer / ledger,而不是继续在 monitor 和 scheduler 两端增加启发式判断。
背景
Holon 已经分别具备:
TaskRecord的单调生命周期与 terminal result 持久化;WaitFor(wake=task_result)/ exact task rejoin;InvokeAgent(new_subagent | existing_agent);但这些机制之间尚缺少一个统一、持久、可恢复的 task result settlement 状态机。当前尤其容易出问题的是
InvokeAgent(existing_agent):调用方创建 invocation task,目标侧接收一条带 delivery / task correlation 的消息,monitor 再从目标 agent 的 activation、turn 和后续 continuation 中推断“这次调用是否已经回复”。这仍然是在用目标 agent 的执行终局推断调用结果,而不是用一次 invocation 独占的 result identity 明确结算。目标 agent 是长期存在、可并发接收消息、可休眠/离线/重启、也可进入 WorkItem continuation 的主体,因此“目标出现了一个 terminal/replied turn”不必然等于“它回复了这次调用”。
P0 问题
需要定义 task result settlement 的 canonical 状态机,至少覆盖以下路径:
没有这层状态机时,会出现以下语义风险:
WaitFor注册到达,inline 与 late 路径产生不同真相;需要先定义的身份
1. Requester key
必须稳定标识“谁在等待这个结果”,不能仅依赖当前内存态或模糊的 WorkItem 归属。建议至少包含:
WorkItem 可以参与路由,但不能单独充当 requester identity。
2. Spawn / invocation lineage
每次调用都需要不可变 lineage,贯穿:
对
existing_agent,lineage 表示“这次调用关系”,不能等同于 agent 的创建 parentage,也不能被目标 agent 的其他消息或既有 WorkItem 继承/覆盖。3. Result identity
每个逻辑结果必须有稳定、可重放的 identity。它应由 requester task + invocation lineage + producer terminal generation/outcome 派生,而不是由“最近一个 turn”或随机 message id 临时决定。
同一逻辑结果的 producer retry、delivery retry、daemon recovery 与 scheduler replay必须复用该 identity;不同 invocation 即使 target、文本和 terminal turn 相似,也不得共享 identity。
建议状态机边界
具体命名可在 RFC 中调整,但至少需要表达以下事实,而不是只用
TaskStatus代替:附加要求:
TaskStatus::Completed/Failed/...描述 producer/task 终态;settlement 描述结果是否以及如何被 requester 接纳,两者不能混为一个字段。InvokeAgent(existing_agent)的回复契约必须明确:
验收标准
与现有 issue 的关系
以下 issue 修复或记录了相邻的单点故障,但不替代本 issue 的统一 settlement contract:
canonical_task_rejoin_stale(51 in 7 days, exit=0 output never delivered) #2967:同一 WorkItem 下未等待的并发 task result 被当作 stale rejoin 丢弃(进行中)。这些问题共同说明:局部修复 delivery、turn id、WorkItem binding 或 stale 判定仍不足以保证端到端结果结算。
当前实现定位(2026-09-14,基于
4de34d6f)src/runtime/agent_services.rs:创建 requester-owned invocation task,并启动 monitor;src/host.rs::invoke_existing_agent:创建 durable delivery,记录current_task_id/ correlation 与 pre-delivery turn baseline;src/host.rs::invocation_delivery_terminal_evidence:沿 delivery activation、attempt、WorkItem/task continuation 推断 terminal evidence;src/runtime/tasks.rs::monitor_spawned_child_agent_task:把推断结果提交为 terminal task result;src/runtime/scheduler_executor.rs::provably_stale_task_rejoin:在 requester 侧独立判断 exact task rejoin 是否 stale。需要把上述分散推断收敛为一个 canonical settlement reducer / ledger,而不是继续在 monitor 和 scheduler 两端增加启发式判断。