Skip to content

pref(runtime): 统一执行生命周期,并强化任务结束与工作区安全机制#394

Merged
phantom5099 merged 15 commits into1024XEngineer:mainfrom
phantom5099:main
Apr 22, 2026
Merged

pref(runtime): 统一执行生命周期,并强化任务结束与工作区安全机制#394
phantom5099 merged 15 commits into1024XEngineer:mainfrom
phantom5099:main

Conversation

@phantom5099
Copy link
Copy Markdown
Collaborator

Summary

本次改动聚焦 runtime/controlplane、运行态生命周期、completion gate、workspace 安全校验以及对应测试补强,目标是让运行闭环的状态表达更统一、写后验证更严格、权限等待态更清晰,同时避免 workspace 边界在 Windows 权限异常场景下被误放宽。

What Changed

1. 统一 runtime 生命周期状态

  • 将原先偏主链路的 phase 概念扩展为统一的 RunState
  • 新增 plan / execute / verify / compacting / waiting_permission / stopped 生命周期状态
  • 为状态迁移补充显式校验,避免主链 phase 与外围治理态分裂成多套规则
  • 统一事件发射时携带的运行阶段信息,并同步更新 TUI 对 stop reason 与 phase 事件的消费逻辑

2. 新增 completion gate 并收紧完成条件

  • 新增 CompletionState 与 completion blocked reason
  • 将“是否允许本轮完成”从散落逻辑收敛到统一 completion 评估
  • 明确区分:
    • pending_todo
    • unverified_write
    • verify_not_run
    • post_execute_closure_required
  • 收紧写后收尾语义:assistant 的普通收尾文本不再被视为“验证通过”
  • 只有显式验证结果成功时,才会清除 HasUnverifiedWrites
  • 所有未终态 todo(包括 subagent todo)都会阻塞 run 完成,避免 delegated work 未完成时主 run 提前结束

3. 重构 progress 评估输入与循环检测

  • ProgressInputPhase 切换为 RunState
  • progress evidence 重新梳理为更稳定的业务信号:
    • task state 变更
    • todo state 变更
    • write applied
    • verify passed
    • new info
  • 调整 exploration progress 与 no-progress/reminder 的触发逻辑
  • 子目标指纹在缺少 task/todo 语义时回退到 tool signature,降低重复循环检测误差
  • 在“本轮无工具调用但也未完成”的场景下继续评估 progress,确保 reminder 能正确注入

4. 明确权限等待与 compact 的临时运行态

  • 引入临时运行态切换辅助逻辑
  • 在权限审批等待期间显式进入 waiting_permission
  • 在 auto/reactive/manual compact 期间显式进入 compacting
  • 生命周期恢复逻辑与停止态发射逻辑统一到 run_lifecycle

5. 加强 workspace 安全校验

  • 保留 workspace root 在 Windows 权限受限环境下的兼容性回退
  • 收紧 candidate 路径的 symlink 解析策略,避免 EvalSymlinks 权限错误时直接把未解析路径当作安全路径
  • 仅在能够证明 root 到 candidate 的现存路径链路中不存在 symlink 组件时,才允许对 permission-denied 做保守回退
  • 保持对 symlink escape、anchor change、TOCTOU 防护链路的校验

6. 补强测试并修正旧测试语义

  • 新增/补强 completionphaseturn_controlworkspace 相关测试
  • 补充对写后验证、subagent todo 阻塞、permission fallback、invalid transition 等场景的回归覆盖
  • 调整部分 runtime 测试用例,让“写工具”与“非写工具”语义保持一致,避免测试与新 completion 规则相互冲突

Files of Interest

  • internal/runtime/controlplane/completion.go
  • internal/runtime/controlplane/phase.go
  • internal/runtime/controlplane/progress.go
  • internal/runtime/run.go
  • internal/runtime/run_lifecycle.go
  • internal/runtime/turn_control.go
  • internal/runtime/toolexec.go
  • internal/runtime/permission.go
  • internal/security/workspace.go
  • internal/tui/core/app/update.go

Testing

已执行:

  • go test ./internal/security ./internal/runtime ./internal/runtime/controlplane
  • go test ./internal/security ./internal/runtime ./internal/runtime/controlplane -cover
  • go test ./...

相关模块覆盖率:

  • internal/security: 92.3%
  • internal/runtime: 93.5%
  • internal/runtime/controlplane: 79.5%

fennoai[bot]

This comment was marked as outdated.

@phantom5099
Copy link
Copy Markdown
Collaborator Author

phantom5099 commented Apr 21, 2026

@xgopilot 先用通俗易懂的语言解释当前runtime进展是如何计算的(可以尝试举例子,最好有和之前方案的对比)

@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 21, 2026
@fennoai fennoai Bot mentioned this pull request Apr 21, 2026
@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 21, 2026
@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 修复未解决的review

…kspace fallback

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
@phantom5099
Copy link
Copy Markdown
Collaborator Author

@xgopilot 深度分析当前PR的模块边界、方案设计等等方面,分析可能会存在什么风险点,要求尽可能往多了找,严厉的批评这个PR的所有问题

@phantom5099
Copy link
Copy Markdown
Collaborator Author

严格问题清单(按严重级别)

  1. [critical] completion gate 存在“粘滞校验”死锁风险
    文件:internal/runtime/turn_control.go:39-47internal/runtime/controlplane/completion.go:44-46internal/runtime/run.go:172-183
    问题:RequiresVerification 一旦被置为 true 就不会回落;而 LastTurnVerifyPassed 会在有 tool call 的轮次被清零。结果是“写入已验证成功后,只要后续再发生一次非验证工具调用”,最终收尾轮仍会被 verify_not_run 阻塞,即使 HasUnverifiedWrites=false。这会导致 run 无法按真实状态完成。
    建议:把 gate 条件改成“仅当存在未验证写入时要求 verify”,即以 HasUnverifiedWrites 为唯一阻塞真值来源;RequiresVerification 可删除或改为派生字段,避免历史状态污染当前完成判定。
  2. [high] withTemporaryRunState 在并发审批场景会写坏生命周期状态
    文件:internal/runtime/run_lifecycle.go:45-59internal/runtime/toolexec.go:46-68internal/runtime/permission.go:134-142
    问题:工具并发执行时,多个 goroutine 可同时进入 waiting_permission;该函数按“单调用前状态”恢复,没有引用计数或栈语义。并发交错后可能把 lifecycle 恢复到过期状态(例如恢复回 waiting_permission),导致 phase 事件与真实执行态不一致。
    建议:对临时态使用计数器/栈(enter+leave),只在最后一个 waiter 离开时恢复;或将状态切换集中到主循环线程,工具 worker 仅发信号不直接改 lifecycle。
  3. [high] 写后验证策略可被轻易绕过
    文件:internal/runtime/toolexec.go:242-251
    问题:isSuccessfulWorkspaceWrite 只把 filesystem.write_file/edit 视为“写入”。通过 bash(如 echo > filesed -i)、未来新增写工具、甚至 mcp 写工具都不会触发 HasUnverifiedWrites,直接绕过“写后必须验证”的核心约束。
    建议:写入判定不应绑定固定工具名;应基于工具执行元数据(例如 workspace_write=true)或统一 capability/action 分类。
  4. [medium] stop reason 枚举发生协议破坏,但 payload 版本未升级
    文件:internal/runtime/controlplane/stop_reason.go:8-12internal/runtime/controlplane/envelope.go:4
    问题:stop reason 从 success/error/canceled 改为 STOP_*,但 PayloadVersion 仍是 1。这对任何非当前 TUI 的消费者都是 silent breaking change。
    建议:要么保持向后兼容映射,要么升级事件协议版本并在网关/客户端做双栈解析。
  5. [medium] “验证成功”识别策略过度硬编码,易造成误阻塞
    文件:internal/runtime/turn_control.go:244-260, 309-335
    问题:只认 bash 且依赖关键词白名单。项目自定义脚本(如 ./scripts/verify.sh)、非关键词命令、非 bash 验证工具都会被判定为“未验证”,与 completion gate 叠加后会持续阻塞完成。
    建议:把验证识别从“命令字符串猜测”升级为显式信号(工具元数据、provider/tool result 标记、或专门 verify tool)。
  6. [medium][security] session 路径包含校验在权限错误时退化为字符串路径,削弱真实路径语义
    文件:internal/session/storage_helpers.go:43-56
    问题:resolvePathForContainmentos.ErrPermission 时直接返回未解析路径,导致 ensurePathWithinBase 退化到字符串层比较;在权限受限+链接路径混杂场景会出现与正常路径语义不一致的判定。
    建议:保持 fail-closed,或采用与 workspace sandbox 一致的“逐段 Lstat + 无 symlink 证明”策略,避免直接信任未解析路径。

测试盲区

  • 缺少针对“已验证写入后再执行非验证工具调用”的 completion 回归用例(应能完成,但当前会阻塞)。
  • 缺少“并发两个 ask 审批”下 lifecycle 最终态一致性的并发测试。
  • 缺少“bash/mcp 写入是否触发 unverified_write”的跨工具一致性测试。

本次仅做分析

  • 未做任何代码改动。
  • 本地验证了相关模块测试:go test ./internal/runtime ./internal/runtime/controlplane ./internal/security ./internal/tools ./internal/session

View job run

@xgopilot 给出修复方案,不需要考虑兼容旧代码或是其他一切尝试兜底行为

@phantom5099
Copy link
Copy Markdown
Collaborator Author

修复方案(硬切,不做兼容兜底)

你的 6 个问题都成立。下面给的是可直接落地的重构方案,只保留新语义。

1) completion 粘滞校验死锁(critical)

位置:internal/runtime/turn_control.go:38-47internal/runtime/controlplane/completion.go:20-47internal/runtime/run.go:172-183

方案:

  1. 删除 CompletionState.RequiresVerificationCompletionState.LastTurnVerifyPassed

  2. CompletionState 仅保留:HasPendingAgentTodosHasUnverifiedWritesCompletionBlockedReason

  3. EvaluateCompletion 删除 verify_not_run 分支,仅以 HasUnverifiedWrites 作为写后验证阻塞真值。

  4. applyToolExecutionCompletion 改为纯状态机:

    • 有写入信号 -> HasUnverifiedWrites=true
    • 有验证成功信号 -> HasUnverifiedWrites=false
  5. run.go 中 progress 的 verifyPassed 输入不再来自 completion 粘滞字段,改为“本轮工具摘要里的 verify success”。

结果:验证阻塞只反映“当前是否存在未验证写入”,不会被历史轮次污染。

2) 临时运行态并发恢复错乱(high)

位置:internal/runtime/run_lifecycle.go:34-60internal/runtime/permission.go:134-142internal/runtime/toolexec.go:46-68

方案:

  1. 删除 withTemporaryRunState 的“保存旧值再恢复”模式。

  2. runState 增加受 state.mu 保护的临时态引用计数(至少 waitingPermissionCountcompactingCount)。

  3. 新增 enterTemporaryState(kind)/leaveTemporaryState(kind)

    • count0->1 时发 phase_changed 到该临时态;
    • count 归零时统一恢复到主态(主态单独存储,如 baseLifecycle)。
  4. 工具 worker 只调用 enter/leave,不直接恢复 lifecycle。

结果:并发 ask 审批不会把 phase 恢复到过期状态。

3) 写后验证可被 bash/MCP 绕过(high)

位置:internal/runtime/toolexec.go:242-251

方案:

  1. 废弃按工具名硬编码写入判断(删 isSuccessfulWorkspaceWrite 里的 name switch)。
  2. 在工具执行结果统一携带能力元数据:result.Metadata["workspace_write"] = true|false
  3. 生成规则放到 tools 层统一出口(tools/registry.go 执行完成后注入),依据结构化 permission action/type 判定,而不是 runtime 猜工具名。
  4. 对未知/外部(MCP)工具默认 workspace_write=true(严格模式,宁可多验不漏验)。

结果:任何可写路径都进入同一 gate,不依赖固定工具名单。

4) stop reason 协议破坏未升版本(medium)

位置:internal/runtime/controlplane/stop_reason.go:8-12internal/runtime/controlplane/envelope.go:4

方案(二选一,建议 A):

  1. A: 保持新 STOP_*,把 PayloadVersion1 升到 2,并要求事件消费者按版本解析。
  2. B: 保持 PayloadVersion=1,恢复 reason 旧枚举值。

你要求不做兼容兜底,建议直接选 A。

5) verify 识别过度依赖 bash 关键词(medium)

位置:internal/runtime/turn_control.go:244-260, 309-335

方案:

  1. 删除 isExplicitVerificationCall 与关键词白名单机制。

  2. 引入显式验证信号:result.Metadata["verification_passed"] = true|false

  3. 由工具层产出该信号:

    • 专用 verify 工具直接设置;
    • bash/mcp 只有显式声明 verify intent 时才设置;
    • runtime 只消费信号,不再解析命令字符串。

结果:验证语义从“猜测命令”变成“结构化事实”。

6) session 路径包含校验在权限错误时退化(medium/security)

位置:internal/session/storage_helpers.go:33-57

方案:

  1. resolvePathForContainment 遇到 os.ErrPermission 一律 fail-closed 返回错误,不再返回未解析路径。
  2. 如需保留权限受限场景能力,复用 workspace 的逐段 Lstat 无 symlink 证明逻辑;未证明即拒绝。

结果:session containment 不再退化为字符串语义。

实施顺序(建议)

  1. 先改 completion 状态机(问题 1)
  2. 再改 lifecycle 临时态计数(问题 2)
  3. 再收敛 write/verify 元数据协议(问题 3+5)
  4. 升 stop reason payload 版本(问题 4)
  5. 最后收紧 session containment(问题 6)

必补测试(最少)

  1. completion: “写入已验证 -> 后续非验证工具调用 -> 无未验证写入时允许完成”。
  2. lifecycle: 并发两个 ask,按交错顺序完成,最终 phase 恢复一致且事件序列正确。
  3. write gate: bash 写入、MCP 写入、filesystem 写入均触发 HasUnverifiedWrites=true
  4. verify gate: 无显式 verification_passed 不可清除未验证写入。
  5. stop reason: payload v2 事件断言(reason 枚举 + version)。
  6. session security: ErrPermission 场景下 containment fail-closed。

说明

本次按你的要求仅给修复方案,未做代码改动。

View job run

@xgopilot 我这里有新方案,请你评估与你的方案的优劣:
Summary
本方案目标是同时避开两个陷阱:

不是“只求现在修得快”的应急式修补
也不是“模型很漂亮但脱离仓库现实”的过度设计
核心思路是把当前问题收敛到三类稳定事实模型,并且提前加上防腐约束,防止后续继续长成新的屎山:

Completion 只看当前未闭合事实
Lifecycle 由主态 + 临时态覆盖层统一推导
写入/验证只认显式结构化执行事实
本阶段不兼容旧的 stop reason、旧 verify 猜测逻辑、旧写入判定,直接切到新语义。

Implementation Changes

  1. Completion 改为“未闭合验证义务”模型
    删除 CompletionState.RequiresVerification
    删除 CompletionState.LastTurnVerifyPassed
    CompletionState 收敛为:
    HasPendingAgentTodos
    HasOpenVerificationObligations
    CompletionBlockedReason
    CompletionBlockedReason 只保留:
    pending_todo
    unverified_write
    post_execute_closure_required
    EvaluateCompletion 的 completed 条件固定为:
    本轮 assistant 无 tool call
    无 pending agent todo
    无 open verification obligation
    引入最小闭环对象 VerificationObligation
    字段固定为:ID、Scope、Status
    Status 只允许:open / satisfied
    runState 内维护当前 run 范围内的 verification obligations
    工具执行后:
    出现写入事实 -> 新增 open obligation
    出现验证成功事实 -> 按 scope 消费 obligation
    HasOpenVerificationObligations 是 obligation 列表的派生值,不再手工维护历史布尔状态

  2. Lifecycle 改为“主态 + 临时态覆盖层”
    保留正式主链 RunState:
    plan
    execute
    verify
    compacting
    waiting_permission
    stopped
    删除 withTemporaryRunState 的“保存旧值再恢复”机制
    在 runState 中新增:
    baseLifecycle:只允许 plan / execute / verify / stopped
    waitingPermissionCount
    compactingCount
    新增统一方法:
    setBaseRunState(...)
    enterTemporaryState(kind)
    leaveTemporaryState(kind)
    deriveEffectiveRunState()
    refreshEffectiveRunState()
    有效生命周期统一由 deriveEffectiveRunState() 推导
    临时态优先级固定为:
    waiting_permission 高于 compacting
    compacting 高于 baseLifecycle
    所有 phase_changed 事件只允许从 refreshEffectiveRunState() 发出
    baseLifecycle 只允许主循环写
    worker 只允许调用 enter/leave,不得直接改 lifecycle

  3. 写入/验证统一改为 typed facts
    不继续使用松散的 Metadata["workspace_write"] / ["verification_passed"]
    直接扩展 tools.ToolResult,新增:
    Facts ToolExecutionFacts
    ToolExecutionFacts 第一版固定字段冻结为:
    WorkspaceWrite bool
    VerificationPerformed bool
    VerificationPassed bool
    VerificationScope string
    runtime 的写入/验证判断全部只认 result.Facts
    删除:
    isSuccessfulWorkspaceWrite(...) 中基于工具名的判断
    isExplicitVerificationCall(...)
    bash 关键词白名单
    executeAssistantToolCalls 汇总时:
    任一结果 WorkspaceWrite=true -> 打开 verification obligation
    任一结果 VerificationPerformed=true && VerificationPassed=true -> 消费 matching obligation

  4. 工具层统一产出执行事实
    internal/tools 层成为 ToolExecutionFacts 的唯一生产者
    内建工具直接在实现中填写 ToolResult.Facts
    bash 如果承担验证职责,必须显式产出:
    VerificationPerformed=true
    VerificationPassed=true|false
    VerificationScope
    MCP / 外部工具不做 runtime 猜测
    如果工具想参与写入/验证语义,必须显式返回对应 facts
    runtime 不再根据工具名、命令字符串或结果文本猜业务动作

  5. Progress 与 Completion 共用同一事实源
    ProgressEvidence 收敛为:
    TASK_STATE_CHANGED
    TODO_STATE_CHANGED
    WRITE_OBLIGATION_OPENED
    WRITE_OBLIGATION_SATISFIED
    NEW_INFO_NON_DUP
    删除孤立的 VERIFY_PASSED evidence
    HasBusinessProgress=true 的条件改为:
    task/todo 有结构化推进
    或打开了新的 verification obligation
    或消费了 existing obligation
    HasExplorationProgress=true 仍只看 NEW_INFO_NON_DUP
    no-progress / repeat-cycle 继续只作为 soft control,不回到 hard stop

  6. Stop reason 协议直接升级
    保持 stop reason 新枚举:
    STOP_FATAL_ERROR
    STOP_COMPLETED
    STOP_USER_INTERRUPT
    PayloadVersion 从 1 升级到 2
    所有事件消费者统一按 v2 解析,不做双栈兼容
    StopReasonDecidedPayload 保持:
    Reason
    Detail

  7. Session containment 改为 fail-closed
    resolvePathForContainment(...) 遇到 os.ErrPermission 一律返回错误
    不再返回未解析路径继续参与 containment 判断
    本阶段不做字符串回退信任
    如后续要恢复权限受限场景能力,单独复用 workspace 侧逐段 Lstat 证明逻辑
    防腐约束
    以下约束属于方案的一部分,实施时必须一并落实,防止后续继续长屎山。

  8. ToolExecutionFacts 字段冻结
    Phase 1 只允许这 4 个字段:

WorkspaceWrite
VerificationPerformed
VerificationPassed
VerificationScope
新增 facts 字段的门槛固定为:

没有它就无法表达跨工具一致的 runtime 控制语义
否则不允许新增。

  1. VerificationScope 第一版强收敛
    Phase 1 只允许两类 scope:

tool_call:
workspace
默认规则固定为:

普通写入打开 tool_call: obligation
只有明确声明“全局验证”的工具才能产出 workspace 级验证
不允许在本阶段引入:

文件级 scope
目录级 scope
通配式 scope
task 级 scope
3. VerificationObligation 不泛化
Phase 1 不做通用 obligation 平台。

类型名固定为 VerificationObligation
不新增 Kind
不新增复杂状态
Status 只允许 open / satisfied
不得把它扩展成:

review obligation
subagent obligation
approval obligation
其他 workflow obligation
4. 临时态数量冻结
Phase 1 临时态只允许:

waiting_permission
compacting
新增新的临时态必须同时修改:

临时态优先级表
deriveEffectiveRunState()
对应测试
不允许在局部代码里偷偷增加新的 lifecycle 覆盖逻辑。

  1. Progress 记分逻辑保持克制
    Phase 1 不引入复杂权重体系,不做模糊加权打分。

HasBusinessProgress 用明确规则判定
Score 若保留,仅用于展示,不得重新成为模糊控制源
不允许继续给 obligation、verify、write 细分多档分数
Public Interfaces / Types
controlplane.CompletionState
删除 RequiresVerification
删除 LastTurnVerifyPassed
新增/改为 HasOpenVerificationObligations
runtime.runState
新增 baseLifecycle
新增 waitingPermissionCount
新增 compactingCount
新增 verification obligations 容器
tools.ToolResult
新增 Facts ToolExecutionFacts
tools.ToolExecutionFacts
仅允许 WorkspaceWrite / VerificationPerformed / VerificationPassed / VerificationScope
VerificationObligation
固定 ID / Scope / Status
controlplane.ProgressEvidenceKind
改为 obligation 驱动 evidence
controlplane.PayloadVersion
升级到 2
Test Plan
completion:
“写入 -> 验证成功 -> 后续普通工具调用 -> 收尾轮允许 completed”
“存在 open verification obligation 时,不允许 completed”
“assistant 无 tool call 但 pending todo 存在时,不允许 completed”
lifecycle:
两个并发审批交错进入/退出,effective run state 始终正确
waiting_permission 与 compacting 并存时,优先级正确
phase_changed 只在 effective state 真正变化时发出
tool facts:
filesystem 写入返回 WorkspaceWrite=true 会打开 obligation
bash/MCP 若未显式声明 verification,不会清除 obligation
显式 VerificationPerformed=true && VerificationPassed=true 才能消费 obligation
scope:
tool_call: obligation 只能被 matching verification 消费
workspace 级 verification 可以消费所有 open obligations
progress:
obligation opened / satisfied 正确映射为 business progress
仅有 NEW_INFO_NON_DUP 时仍走 exploration
protocol:
stop_reason_decided 事件 payload version 固定为 2
TUI / gateway / remote adapter 按 v2 正确消费
security:
os.ErrPermission 下 containment fail-closed
权限受限路径不会被当作“仍在 base 内”
anti-bloat:
不允许新增未在计划中的 ToolExecutionFacts 字段
不允许新增未声明的临时态
不允许 obligation 类型泛化
Assumptions
本阶段不引入动态 token 预算、ledger/checkpoint、resume
本阶段不将 dispatch、todo.executor、inline subagent 提升为 runtime 主链语义
本阶段不实现通用 workflow/obligation 平台
MCP/外部工具若要参与写入/验证语义,必须显式返回 typed facts;runtime 不猜测
本阶段以“最小但真实的领域事实”替代布尔补丁与字符串猜测,不追求一次性抽象到最终形态

@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 21, 2026
@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 21, 2026
@phantom5099
Copy link
Copy Markdown
Collaborator Author

结论

你的新方案总体优于我上一版,方向更正确:把 completion / lifecycle / write&verify 统一到“结构化事实”上,能消除当前实现里的粘滞状态和字符串猜测问题。 但我建议做一个“硬切的最小闭环版”,避免在本 PR 一次性引入过多新抽象导致迁移面过大。

逐项评估(对比我上一版)

  1. Completion 模型:你这版更好。
    当前实现确实存在粘滞风险:RequiresVerification + LastTurnVerifyPassed 会被后续轮次污染(internal/runtime/turn_control.go:30-47, internal/runtime/controlplane/completion.go:44-46)。
    建议采纳你“只看未闭合义务”的方向,至少先移除 RequiresVerification/LastTurnVerifyPassed
  2. Lifecycle 主态+临时态覆盖:你这版明显更强。
    当前 withTemporaryRunState 是“保存旧值再恢复”(internal/runtime/run_lifecycle.go:34-60),并发审批下存在恢复到过期状态风险。
    建议直接按你说的计数器 + derive effective state 硬切。
  3. 写入识别(typed facts):方向对,但需一处调整。
    你提的 ToolExecutionFacts 比 metadata string 更稳。
    但“外部/MCP 仅显式声明才计写入”会有漏检风险;建议默认保守:无法证明只读时按 WorkspaceWrite=true 处理,避免绕过 gate(当前硬编码名单确实过窄,internal/runtime/toolexec.go:242-251)。
  4. verify 识别:你这版优于关键词方案。
    当前依赖 bash 关键词(internal/runtime/turn_control.goisExplicitVerificationCall + verificationCommandKeywords)语义脆弱。
    建议 runtime 只消费结构化 verify facts,不再解析命令文本。
  5. Stop reason 协议:同意你的硬切方案。
    既然 reason 枚举已切到 STOP_*,应同步升级 payload version(当前仍是 1internal/runtime/controlplane/envelope.go:4)。
  6. Session containment:同意你的 fail-closed。
    当前 os.ErrPermission 回退为未解析路径(internal/session/storage_helpers.go:43-45, :54-55)确实会退化 containment 语义。

建议落地策略(硬切,但分层)

  • 本 PR 必做(建议立即)
  1. completion 去粘滞(删 RequiresVerification/LastTurnVerifyPassed
  2. lifecycle 改计数覆盖层(删 withTemporaryRunState 恢复旧值)
  3. stop payload 升 v2
  4. session containment 改 fail-closed
  5. verify 从关键词切到结构化信号
  • 本 PR 可保留“最小实现”,后续再做
  1. VerificationObligation 的完整 scope 体系(tool_call:<id>/workspace
  2. facts 字段“冻结约束”与 anti-bloat 守卫测试

原因:你的完整方案是对的,但一次引入 obligation 容器 + scope 规则 + 全链路 facts 生产,会显著扩大迁移面;建议先把安全与正确性关键点硬切落地,再做语义增强层。

本次只做评估,没有代码改动。

View job run

@xgopilot 那么请你执行这套新方案吧,第三点允许采纳你的建议。任务完成记得输出具体的执行结果,同时提交PR到fork仓库中

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 21, 2026

执行结果

已按你要求完成这套“硬切最小闭环方案”,并采纳你允许的第 3 点建议(未知/外部工具默认保守按可写处理)。

已完成改动

  1. completion 去粘滞
  • 删除 RequiresVerification / LastTurnVerifyPassed 语义与判定路径。
  • completion gate 改为只看当前事实:pending_todounverified_writepost_execute_closure_required
  1. lifecycle 改为主态 + 临时态覆盖层计数
  • 移除 withTemporaryRunState 的“保存旧状态再恢复”模式。
  • 新增并接入:setBaseRunStateenterTemporaryRunStateleaveTemporaryRunStaterefreshEffectiveRunState
  • 运行态由 baseLifecycle + waitingPermissionCount + compactingCount 派生,统一从刷新入口发 phase_changed
  1. 写入/验证切到结构化 facts(含保守默认)
  • tools.ToolResult 新增 Facts ToolExecutionFacts
  • 新增 tools.EnrichToolResultFacts(...),由 tools 层统一补齐事实。
  • runtime 删除关键词/命令猜测 verify 与工具名写入判定,改为只消费 result.Facts
  • 默认规则:read=falsewrite/bash/mcp/unknown=true,若工具显式给出 workspace_write 则以显式值为准。
  • bash 增加显式验证参数:verificationverification_scope,并产出 verification_* 结构化元数据。
  1. stop 协议升级
  • internal/runtime/controlplane/envelope.goPayloadVersion1 升级到 2
  1. session containment fail-closed
  • resolvePathForContainmentos.ErrPermission 分支不再回退未解析路径,直接返回错误。

测试与验证

已执行并通过:

  • gofmt -w ...(本次改动文件)
  • go test ./internal/runtime/... ./internal/tools/... ./internal/session/... ./internal/security/...
  • go test ./...

新增/补强测试包括:

  • internal/runtime/run_lifecycle_test.go
  • internal/tools/facts_test.go
  • internal/tools/bash/tool_test.go(显式 verification facts)
  • 以及 completion/turn_control/permission 等相关用例同步更新

提交与推送

View job run

xgopilot and others added 2 commits April 21, 2026 18:10
…semantics

- remove sticky verify flags from completion gate and rely on unverified-write fact
- replace temporary lifecycle restore with counter-based effective state derivation
- add typed tool execution facts and consume them for write/verify semantics
- default unknown/mcp/bash actions to conservative workspace_write=true unless explicitly read-only
- upgrade runtime payload version to v2 and fail-close session containment on permission errors
- add lifecycle/facts/verification regression tests

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
refactor(runtime): hard-cut lifecycle/completion/facts semantics
@phantom5099
Copy link
Copy Markdown
Collaborator Author

@xgopilot 批判性review整个PR,分析所有潜在风险点,其中模块边界划分、方案设计思路等等也是你的分析范围,找出所有漏洞,不许输出正向表扬。

@phantom5099
Copy link
Copy Markdown
Collaborator Author

审查结论(仅问题)

  1. [critical][security] 结构化事实信任边界错误,外部工具可伪造“已验证/未写入”并绕过 completion gate

    • 位置:internal/tools/facts.go:21-45internal/tools/manager.go:337-339internal/tools/mcp/stdio_client.go:819-826
    • 问题:EnrichToolResultFacts 直接信任 metadataworkspace_write / verification_*;同时 MCP 客户端会把远端返回字段原样灌入 metadata。结果是外部 MCP 工具可通过返回 verification_passed=trueworkspace_write=false 直接操纵 runtime 的写后验证闭环。
    • 风险:写后验证约束可被远端工具数据面绕过,属于明显 trust-boundary violation。
  2. [high] bash/mcp 默认被判定为“写入成功候选”,会把大量非写操作误判为未验证写入

    • 位置:internal/tools/facts.go:51-60internal/runtime/turn_control.go:35-40internal/runtime/controlplane/completion.go:36-38
    • 问题:defaultWorkspaceWriteFromActionActionTypeBash/ActionTypeMCP 默认设为 WorkspaceWrite=true。只要工具成功返回且未显式覆写 metadata,就会触发 HasUnverifiedWrites=true
    • 风险:读操作也会触发“必须验证”阻塞,completion gate 被系统性误触发,运行容易进入无意义补验证循环。
  3. [high] progress 评分存在逻辑自相矛盾:VERIFY_PASSED 单独即可判定 business progress

    • 位置:internal/runtime/controlplane/progress.go:105internal/runtime/controlplane/progress.go:166-167
    • 问题:你试图用 (write && verify) 约束业务进展,但同时把 EvidenceVerifyPassed 计入 strong evidence,导致 strongCount>0 时无需写入也会被判定为 business progress。
    • 风险:模型可通过“纯验证回合”持续重置 no-progress/repeat 计数,提醒机制被规避。
  4. [high][security] workspace root 的 permission fallback 仍可在“root 为 symlink”时放宽边界

    • 位置:internal/security/workspace.go:252-270(尤其 261-267
    • 问题:resolveCanonicalWorkspaceRootEvalSymlinks(root) 遇到 os.ErrPermission 时直接回退为 absoluteRoot,但这里没有做 Lstat(root) 的 symlink 拒绝。你只在 canFallbackToCandidateOnPermission 增加了 root symlink 防护,未覆盖 root canonicalization 路径。
    • 风险:Windows 权限异常场景下,workspace root 真实锚点可能退化为字符串路径,边界校验变弱。
  5. [medium] TUI 没有完整消费新生命周期态,状态展示与 runtime 实际生命周期脱节

    • 位置:internal/tui/core/app/update.go:1073-1080
    • 问题:phase_changed 仅处理 plan/execute/verify,忽略本 PR 引入的 compacting/waiting_permission/stopped
    • 风险:UI 进度条与真实运行态不一致,权限等待与压缩过程会显示成旧态,排障信息失真。

测试缺口

  • internal/tools/facts_test.go 目前只验证“默认值是什么”,没有验证这些默认值进入 runtime completion 后是否导致误阻塞。
  • internal/security/workspace_test.go 覆盖了 candidate fallback 的 symlink-root 拒绝,但没有覆盖 resolveCanonicalWorkspaceRootErrPermission + symlink-root 的拒绝路径。

本次范围

  • 仅做批判性审查,未做代码修改。

View job run

@xgopilot 评估以下方案如何,看看是否适合直接实施:# Runtime 控制面重构方案(不兼容旧代码版)

Summary

本方案直接重构 runtime 的 completion / progress / lifecycle / tool facts 语义,不兼容旧 stop reason、旧 metadata 协议、旧 verify 猜测逻辑,也不保留任何“先补一下”的过渡层。目标是一次性把控制面改成可信事实驱动模型:

  • completion 只看当前未闭合验证义务
  • lifecycle 只看主循环拥有的主态 + 临时态计数
  • progress 只看结构化推进事实
  • runtime 只消费可信来源的 typed facts,不再信任远端 metadata 或命令字符串猜测

这版方案默认会直接替换现有:

  • RequiresVerification / LastTurnVerifyPassed
  • metadata 驱动 workspace_write / verification_*
  • isExplicitVerificationCall(...)
  • withTemporaryRunState(...)
  • stop payload v1

Key Changes

1. Completion 改成“验证义务”模型

  • 删除 CompletionState.RequiresVerification
  • 删除 CompletionState.LastTurnVerifyPassed
  • CompletionState 只保留:
    • HasPendingAgentTodos
    • OpenVerificationObligationCount
    • CompletionBlockedReason
  • 引入 VerificationObligation
    • 字段固定为:
      • ID
      • Scope
      • OpenedByToolCallID
      • Status
    • Status 只允许:
      • open
      • satisfied
  • runState 持有本次 run 的 obligation 列表,completion 不再维护历史布尔状态
  • completed 条件固定为:
    • 本轮 assistant 无 tool call
    • 无 pending agent todo
    • OpenVerificationObligationCount == 0
  • CompletionBlockedReason 只保留:
    • pending_todo
    • unverified_write
    • post_execute_closure_required

2. 写入/验证改成“可信 typed facts”

  • 删除 tools.EnrichToolResultFacts(...) 对 metadata 的信任路径
  • 删除 runtime 基于工具名和命令字符串的写入/验证猜测
  • tools.ToolResult 新增强类型字段:
    • Facts ToolExecutionFacts
  • ToolExecutionFacts 第一版固定字段冻结为:
    • WorkspaceWrite bool
    • VerificationPerformed bool
    • VerificationPassed bool
    • VerificationScope string
  • 这些 facts 的唯一可信来源:
    • built-in tool executor 自己产出
    • tools manager 基于本地可信 security.Action / capability 结果补全
  • 远端 MCP 返回的 metadata 一律视为非可信显示数据,不得进入 ToolExecutionFacts
  • bash 不再通过命令白名单猜测验证
    • 如果 bash 要承担验证职责,bash 工具本身必须显式产出 verification facts
  • MCP 工具如需参与写入/验证语义,必须经过本地受信任适配层映射;远端 payload 不能直接声明 facts

3. 写后验证闭环改成 obligation 状态机

  • 任一 ToolResult.Facts.WorkspaceWrite=true
    • 打开一个 VerificationObligation
  • 任一 ToolResult.Facts.VerificationPerformed=true && VerificationPassed=true
    • VerificationScope 消费 obligation
  • VerificationScope 第一版固定只允许两类值:
    • tool_call:<id>
    • workspace
  • 默认规则固定为:
    • 普通写入打开 tool_call:<id> obligation
    • 只有明确的全局验证动作才能返回 workspace
  • 不允许引入文件级、目录级、通配级 scope
  • runtime 不再直接维护 HasUnverifiedWrites 布尔真相;它变成 obligation 列表的派生值

4. Progress 与 Completion 共用同一事实源

  • ProgressEvidenceKind 收敛为:
    • TASK_STATE_CHANGED
    • TODO_STATE_CHANGED
    • WRITE_OBLIGATION_OPENED
    • WRITE_OBLIGATION_SATISFIED
    • NEW_INFO_NON_DUP
  • 删除孤立 VERIFY_PASSED
  • HasBusinessProgress=true 的条件固定为:
    • task/todo 有结构化推进
    • 或打开了新的 verification obligation
    • 或消费了 existing obligation
  • HasExplorationProgress=true 只由 NEW_INFO_NON_DUP 驱动
  • no-progress / repeat-cycle 继续只作为 soft control,不回到 hard stop
  • ProgressScore 不再靠“verify 单独强证据”重置 no-progress

5. Lifecycle 改成“主态 + 临时态覆盖层”

  • 删除 withTemporaryRunState(...)
  • runState 新增:
    • baseLifecycle
    • waitingPermissionCount
    • compactingCount
  • baseLifecycle 只允许主循环写,取值仅允许:
    • plan
    • execute
    • verify
    • stopped
  • 新增:
    • setBaseRunState(...)
    • enterTemporaryState(kind)
    • leaveTemporaryState(kind)
    • deriveEffectiveRunState()
    • refreshEffectiveRunState()
  • 有效生命周期固定按优先级推导:
    • waiting_permission
    • compacting
    • baseLifecycle
  • 所有 phase_changed 事件只允许从 refreshEffectiveRunState() 发出
  • worker 不得直接改 lifecycle,只能 enter/leave 临时态

6. stop reason 协议直接升 v2

  • StopReason 固定为:
    • STOP_FATAL_ERROR
    • STOP_COMPLETED
    • STOP_USER_INTERRUPT
  • controlplane.PayloadVersion 直接升级到 2
  • 所有消费者统一按 v2 解析,不做旧值兼容
  • StopReasonDecidedPayload 保持:
    • Reason
    • Detail
  • TUI / gateway / remote adapter 必须完整消费新的 lifecycle 与 stop v2

7. TUI 生命周期展示对齐

  • phase_changed 处理必须覆盖:
    • plan
    • execute
    • verify
    • compacting
    • waiting_permission
    • stopped
  • 进度条/状态文案改成 lifecycle-aware
  • 权限等待与 compacting 必须有独立展示,不再复用旧态

8. Session containment 改成 fail-closed

  • resolvePathForContainment(...) 遇到 os.ErrPermission 一律报错
  • 不再退回未解析路径继续 containment
  • 本阶段不做字符串层 fallback
  • 后续如果要支持权限受限场景,只允许复用 workspace 侧逐段 Lstat 证明逻辑

9. 防腐约束

  • ToolExecutionFacts 第一版字段冻结,不允许额外新增
  • VerificationObligation 不泛化:
    • 不新增 Kind
    • 不扩展成通用 workflow obligation
  • 临时态数量冻结:
    • 只允许 waiting_permission
    • 只允许 compacting
  • VerificationScope 冻结为:
    • tool_call:<id>
    • workspace
  • progress 不引入复杂权重和模糊打分,仍然采用明确规则判定

Public Interfaces / Types

  • controlplane.CompletionState
    • 删除 RequiresVerification
    • 删除 LastTurnVerifyPassed
    • 改为 OpenVerificationObligationCount
  • 新增 runtime.VerificationObligation
  • tools.ToolResult
    • 新增 Facts ToolExecutionFacts
  • 新增 tools.ToolExecutionFacts
    • WorkspaceWrite
    • VerificationPerformed
    • VerificationPassed
    • VerificationScope
  • controlplane.ProgressEvidenceKind
    • 改为 obligation 驱动的 5 类 evidence
  • controlplane.PayloadVersion
    • 1 升为 2

Test Plan

  • completion:
    • 写入后打开 obligation,未验证时不得 completed
    • 验证成功后 obligation 关闭,允许 completed
    • 写入已验证后,后续普通工具调用不得再次阻塞 completed
  • trust boundary:
    • MCP 返回 metadata 中的 workspace_write / verification_* 不得影响 runtime facts
    • 只有本地受信任工具/适配层产生的 facts 才能驱动 obligation
  • lifecycle:
    • 两个并发审批交错进入/退出,effective lifecycle 始终正确
    • waiting_permissioncompacting 并存时优先级正确
    • phase_changed 只在 effective state 变化时发出
  • progress:
    • WRITE_OBLIGATION_OPENED / SATISFIED 正确驱动 business progress
    • 纯 verification 回合不再单独重置 no-progress
  • protocol:
    • stop_reason_decided payload version 固定为 2
    • TUI / gateway / remote adapter 按 v2 解析
  • security:
    • os.ErrPermission 下 containment fail-closed
    • root 为 symlink 且无法 canonicalize 时拒绝通过

Assumptions

  • 本阶段不引入动态 token 预算、ledger/checkpoint、resume
  • 本阶段不把 dispatch / todo.executor / inline subagent 纳入主链控制面
  • MCP 若要参与写入/验证语义,必须通过本地可信适配层映射 typed facts;远端数据面不被信任
  • 本阶段不做旧协议兼容,不保留双栈逻辑
  • 目标是一次性建立可信事实边界和稳定控制面,而不是用补丁继续堆旧模型

@phantom5099
Copy link
Copy Markdown
Collaborator Author

结论

适合直接实施你这版“非兼容硬切”方案,但不建议一次性大爆改;建议按 P0 安全/可信边界P1 语义收敛 两批连续落地(都在当前 PR 内完成),否则回归面过大且难定位。

当前实现对照(基于本分支代码)

  1. 已基本对齐的项
  • completion 已去掉粘滞字段,只剩 HasPendingAgentTodos/HasUnverifiedWrites,并用 pending_todo/unverified_write/post_execute_closure_required 阻塞:internal/runtime/controlplane/completion.go:17-40
  • lifecycle 已是 baseLifecycle + waitingPermissionCount + compactingCount 覆盖模型:internal/runtime/state.go:31-35internal/runtime/run_lifecycle.go:15-112
  • stop payload 已升到 PayloadVersion = 2internal/runtime/controlplane/envelope.go:3-4
  • session containment 在 ErrPermission 已 fail-closed:internal/session/storage_helpers.go:43-56
  1. 仍需按你方案硬切的核心问题
  • trust boundary 仍不成立EnrichToolResultFacts 直接信任 metadata 中 workspace_write/verification_*,而 MCP 客户端把远端字段原样塞 metadata。外部工具可伪造 facts。见:

    • internal/tools/facts.go:21-38
    • internal/tools/manager.go:337-339
    • internal/tools/mcp/stdio_client.go:819-826
  • 默认写入判定过宽ActionTypeBash/ActionTypeMCP 默认 WorkspaceWrite=true,会系统性制造误阻塞。见:internal/tools/facts.go:52-60

  • progress 语义冲突仍在VERIFY_PASSED 被当强证据,导致无写入也可判 business progress。见:internal/runtime/controlplane/progress.go:105166-167

  • workspace root fallback 仍有 root-symlink 漏洞窗口resolveCanonicalWorkspaceRootEvalSymlinks(root) 权限错误时直接回退字符串路径,未做 root Lstat symlink 拒绝。见:internal/security/workspace.go:261-267

  • TUI 生命周期消费不完整phase_changed 只处理 plan/execute/verify,缺 compacting/waiting_permission/stopped。见:internal/tui/core/app/update.go:1073-1080

对你方案的可实施性判断

  1. Completion 改 obligation 模型:可行,且比布尔真值更稳;建议直接上。
  2. Typed facts 只信本地来源:必须做,优先级最高。
  3. progress 与 completion 共事实源:可行,且应删除孤立 VERIFY_PASSED 强证据。
  4. lifecycle 主态+临时态:当前已接近目标,主要是清理旧入口与补并发回归。
  5. stop v2:已完成。
  6. TUI 生命周期对齐:必须补。
  7. session containment fail-closed:已完成。

建议落地顺序(最小风险路径)

  1. P0(先做):切断 metadata->facts 信任链;ToolExecutionFacts 仅允许 built-in executor + 本地受信映射写入;MCP metadata 仅作展示字段。
  2. P0(同批)defaultWorkspaceWriteFromAction 改为最小保守策略(仅确定写能力才置 true),并补 “bash/mcp 只读不阻塞” 回归。
  3. P0(同批):修 resolveCanonicalWorkspaceRoot 的 root symlink + ErrPermission 路径。
  4. P1:引入 VerificationObligationtool_call:<id> / workspace 两种 scope 冻结),completion/progress 统一改 obligation evidence。
  5. P1:TUI 补齐 compacting/waiting_permission/stopped 展示与测试。

必补测试(按你方案)

  • MCP 返回 workspace_write/verification_* 不得驱动 runtime facts。
  • 非写 bash/mcp 成功回合不得触发 unverified_write
  • VERIFY_PASSED 单独出现时不得判定 business progress。
  • resolveCanonicalWorkspaceRootErrPermission + root symlink 必须拒绝。
  • phase_changed 覆盖 6 个 run state。

本次范围

仅做方案评估;未修改代码,未执行新增测试。>
View job run

@xgopilot 直接执行方案,不要有试图兼容或兜底旧代码行为,完成后提交PR到fork仓库

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 22, 2026

执行结果

已按“直接执行方案、不要兼容兜底旧行为”的要求完成硬切改造,并推送到当前 PR 分支。

关键改动

  1. metadata -> facts 信任链硬切
  • internal/tools/facts.goEnrichToolResultFacts 不再读取 metadata.workspace_write / verification_*
  • internal/tools/facts.go:默认写入判定收紧为仅 ActionTypeWrite=truebash/mcp/read/unknown=false
  • internal/tools/registry.go:MCP 透传 metadata 过滤保留键与 workspace_write/verification_*,避免远端覆盖本地安全语义。
  1. 受信验证事实改为本地写入
  • internal/tools/bash/tool.go:bash 在显式 verification 场景下直接写 ToolExecutionFacts,metadata 仅作展示。
  1. progress 语义收敛
  • internal/runtime/controlplane/progress.goVERIFY_PASSED 不再作为强证据;仅与 WRITE_APPLIED 组合才可构成写后业务推进。
  1. workspace root 权限回退加固
  • internal/security/workspace.goresolveCanonicalWorkspaceRootEvalSymlinks(root)=ErrPermission 时,先走逐段无 symlink 证明;root 为 symlink 时拒绝回退(fail closed)。
  1. TUI 生命周期消费补齐
  • internal/tui/core/app/update.gophase_changed 新增 compacting / waiting_permission / stopped 处理。

新增/更新测试

  • internal/tools/facts_test.go
  • internal/tools/manager_test.go
  • internal/tools/registry_test.go
  • internal/tools/bash/tool_test.go
  • internal/runtime/controlplane/progress_test.go
  • internal/security/workspace_test.go
  • internal/tui/core/app/update_runtime_events_test.go

验证

  • go test ./internal/tools/...
  • go test ./internal/runtime/... ./internal/security/... ./internal/tui/...
  • go test ./...

提交与推送

未完成项:无。

View job run

xgopilot and others added 2 commits April 22, 2026 03:05
Generated with [codeagent](https://github.com/qbox/codeagent)

Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(runtime/tools): hard-cut trust boundary and lifecycle state handling
@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 解决冲突,选择合适的方案

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 22, 2026

处理结果

已完成“解决冲突并选择方案”,并直接推送到当前 PR 头分支 phantom5099:main(最新提交:b174fae)。

采用方案:

  • 保留 origin/maininternal/tools/manager.go 的工作区外低风险写入审批链路(不回退安全策略)。
  • 保留本 PR 的受信事实边界:通过执行器包装层统一补齐 EnrichToolResultFacts,避免外部 metadata 伪造写入/验证事实。
  • 补充回归测试:internal/tools/manager_test.go 增加 TestDefaultManagerExecuteMCPMetadataCannotDriveTrustedFacts

关键改动

  • internal/tools/manager.go
    • 恢复并保留 upstream 的 sandbox 外写入审批逻辑。
    • 新增 factsEnrichingExecutor 包装层,在不改动冲突敏感主流程的情况下补齐受信 Facts
  • internal/tools/manager_test.go
    • 新增 MCP metadata 信任边界测试,确保 workspace_write/verification_* 远端字段不会驱动 runtime 事实。

验证

  • go test ./internal/tools/...
  • go test ./...
  • 本地合并验证:git merge-tree --write-tree origin/main HEAD 返回无冲突(exit code 0

当前 #394headRefOid 已更新为 b174fae9aa4aec303a8fb440f31557b4197dd85bmergeStateStatus 已不再是 DIRTY

View job run

- restore low-risk external write approval path from origin/main
- keep trusted facts enrichment on manager execution path
- add regression test to ensure MCP metadata cannot drive trusted facts

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
…conflicts

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 解决冲突,不要引入编译错误

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 22, 2026

处理结果

已完成“解决冲突,不要引入编译错误”的处理与验证。

  1. 冲突检查
  • 基于 origin/main 执行合并预检:git merge-tree --write-tree origin/main HEAD
  • 返回 exit:0,当前分支无未解决冲突
  1. 编译与测试验证
  • 执行 go build ./...:通过
  • 执行 go test ./...:通过(全量包通过)
  1. 变更说明
  • 本次未做额外代码改动(工作区保持干净)
  • 因无需新增修复提交,所以未产生新的 commit/push

View job run

@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 解决workspace_test冲突,不要引入编译错误

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 22, 2026

处理结果

已完成 workspace_test 冲突修复,并确保未引入编译/测试错误。

  • 已修改文件:internal/security/workspace_test.go
  • 变更点:将 TestAbsoluteWorkspaceTarget 的断言对齐为 origin/main 风格(使用 samePathKey(got, wantAbs)),避免同区域断言写法差异导致的合并冲突,同时保持语义不变。
  • 提交:35f51a7

验证结果

  • go test ./internal/security -count=1 通过
  • go test ./... -count=1 通过

代码推送

View job run

xgopilot and others added 2 commits April 22, 2026 03:45
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix: resolve workspace_test merge conflict
@phantom5099
Copy link
Copy Markdown
Collaborator Author

/review

Copy link
Copy Markdown

@fennoai fennoai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed in four passes (code quality, performance, security, documentation). I found two noteworthy issues that are still present in the current head.

Comment thread internal/runtime/turn_control.go Outdated
current.HasUnverifiedWrites = true
}
if summary.HasSuccessfulVerification {
current.HasUnverifiedWrites = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] applyToolExecutionCompletion collapses write/verify into two booleans and then clears HasUnverifiedWrites whenever any verification succeeds in the turn. This is order-insensitive, so a turn containing both verify + write can end with HasUnverifiedWrites=false even when the latest write was not verified (especially with concurrent tool execution). Consider tracking write/verify by execution order or epoch, and only clear unverified writes when verification is proven to cover the latest write set.

Copy link
Copy Markdown
Collaborator Author

@phantom5099 phantom5099 Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xgopilot 修复这条comment

Copy link
Copy Markdown

@fennoai fennoai Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有,如果采用“按工具名轮转重排队列”,确实可能破坏模型原始工具调用顺序(尤其是跨工具存在隐式前后依赖时)。

当前这版代码里,这个风险已经被规避:

  • 派发按原顺序:internal/runtime/toolexec.go:71internal/runtime/toolexec.go:76
  • 结果按原 index 回填:internal/runtime/toolexec.go:59internal/runtime/toolexec.go:63internal/runtime/toolexec.go:84internal/runtime/toolexec.go:89
  • 同名工具仍串行:internal/runtime/toolexec.go:110internal/runtime/toolexec.go:111internal/runtime/toolexec.go:170internal/runtime/toolexec.go:179

所以现在的语义是:

  • 不再做“轮转重排”;
  • 保留 assistant 提供的调用顺序作为调度输入;
  • 但跨工具并发执行下,完成先后依然天然不保证(这是并发本身语义,不是队列重排造成的)。

未做内容:本轮未修改代码,仅回答该顺序语义问题。

View job run

Copy link
Copy Markdown

@fennoai fennoai Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已按你指向的 review comment 完成修复并提交。

修复内容

  • 修复点:internal/runtime/turn_control.goapplyToolExecutionCompletion
  • 原问题:同一轮里只用聚合布尔值(HasSuccessfulWorkspaceWrite/HasSuccessfulVerification)做收敛,忽略顺序,导致 verify + write 同轮时可能错误清除 HasUnverifiedWrites
  • 现行为:改为按 summary.Results 顺序逐条应用事实
    • 遇到成功写入事实:置 HasUnverifiedWrites=true
    • 遇到成功验证事实:置 HasUnverifiedWrites=false
    • 因此 verify -> write 结论为 true(仍有未验证写入),write -> verify 结论为 false

回归测试

  • 更新:internal/runtime/turn_control_test.go
  • 新增覆盖:
    • TestApplyToolExecutionCompletionKeepsUnverifiedWhenVerifyBeforeWrite
    • TestApplyToolExecutionCompletionClearsWhenVerifyAfterWrite

验证

  • go test ./internal/runtime/... 通过
  • go test ./... 通过

提交与推送

未完成项:无。

View job run

@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 22, 2026
@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 22, 2026
xgopilot and others added 2 commits April 22, 2026 04:46
…ify/write turns

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(runtime): preserve unverified writes when verify occurs before write
@phantom5099 phantom5099 merged commit 8aaa5de into 1024XEngineer:main Apr 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants