摘要
本 issue的目的是重构 runtime final acceptance 主链,收敛为唯一闭环:
session-owned verification profile -> completion gate -> verifier gate -> acceptance decision -> terminal decision
目标是让 runtime 对“什么时候能收尾、什么时候必须继续、什么时候已经失败”给出稳定、唯一、可测试的决定。
背景
当前仓库已经有 completion gate、verifier gate、acceptance decision 这些零件,但它们还没有形成一条唯一、稳定、可解释的收尾链路。
目前 runtime 在 final acceptance 上仍然保留了旧路径、猜测逻辑、metadata 旁路和双真相源状态,导致相似任务在相似状态下并不能稳定得到相似的收尾结果。
本次目标是把 final acceptance 收敛为一条主链,而不是继续叠加补丁式修复。
当前真实存在的问题
1. required todo 失败后仍可能被视作完成项
当前 todo_convergence 仍把 required todo 的 failed/canceled 当做任务完成。
这会把“任务做完了”和“任务做失败了/被取消了”混为一谈,导致必做项已明确失败时,acceptance 主链仍可能继续向 accepted 推进。
具体会发生在这些场景:
- 某个 required todo 执行失败,被 todo_write fail 标记为 failed
- 某个 required todo 没有真正完成,只是被 todo_write set_status canceled 标记为 canceled
具体后果是:
- 必做项已经失败,acceptance 仍可能继续向 accepted 推进
- 必做项只是被取消、但没有明确 replacement,acceptance 仍可能误判为“已收敛”
- 用户最终看到的结果会是:关键任务并未完成,但 agent 仍可能正常收尾并输出“任务完成”
2. acceptance 主链仍允许通过配置绕过 verifier gate
当前 acceptance engine 仍保留:
verification disabled -> compatibility fallback accepted
final_intercept=false -> 本轮 final acceptance 不跑 verifier
这意味着 verifier gate 现在还不是 final acceptance 的必经环节,而仍然可以被配置整体跳过。
从主链语义上讲,这会破坏“检查通过后才能完成任务”的基本约束,也使 acceptance 仍然保留旧兼容逃生门。
3. verifier policy 仍依赖 task 文本猜测
当前 verifier 选择仍依赖:
inferTaskType
resolveTaskType
runtime.verification.default_task_policy
现在系统不是自己保存一份正式清单,而是看任务描述里有没有像“fix bug”、“docs”、“config”、“refactor”这样的词,再临时猜这次该检查什么,而不是建立在 runtime 自己持有的结构化任务状态之上。
这会导致同类任务仅因措辞不同,就可能走出不同的验收路径。
4. file/content 验收仍走 metadata 旁路
当前:
- file_exists 仍读取 metadata.expected_files
- content_match 仍读取 metadata.content_match
- session todo / task state 还不是唯一结构化验收输入来源,artifact/content 验收仍存在 metadata 旁路。
可以把这两者理解成:
- session todo / task state:正式病历、正式工单、正式项目台账
- metadata:便利贴、口头备注、临时小纸条。临时附带,不稳定
这会导致验收规则容易漂移、丢失,或者与 session 中真实保存的任务状态不一致。
5. intercepted final 仍会先落盘
当前 assistant final 仍在 acceptance 前先写入 session.Messages。
如果 final 随后被拦截并 continue,下一轮模型仍会看到自己刚刚说过的“任务完成”,从而更容易进入重复收尾输出。
6. final interception 仍存在双真相源
- runState.finalInterceptStreak:判断agent的结束标志第几次没有通过检查
- progress.LastScore.NoProgressStreak:判断整个 agent 在执行过程中有没有继续推进任务,防止无效空转
当前 continue 分支仍会做一轮空 evidence progress 评估,在什么业务都没有的情况下还做一轮计分操作,并把两个不一样的计分系统混在一起使用。
这意味着 final interception 的计数并不是一个自洽系统,而是会被另一套 progress 状态反向覆盖。
7. git_diff 默认仍漏掉 staged-only 交付
当前 git_diff 默认仍使用能看到工作区相对暂存区变化的命令:git diff --name-only
但 staged-only 场景是:
- 改动已经 git add
- 工作区已经干净
- 真实交付已经存在于 index
在这种情况下:
- git diff --name-only 可能返回空
- 但并不代表“没有改动”
- 只是代表“没有未暂存改动”
8. orchestrator / failure policy / stop reason 语义仍不稳定(第四点指的是输入规则,这里说的是结果解释方式)
当前 verifier orchestrator 存在规则分散在多个层里,且部分规则依赖文案而不是依赖稳定枚举的问题:
- verifier 不按“首个非 pass 即短路”的语义执行:应该是检查是否结束时,如果遇到首个没通过的 verifier,就直接停止后续检查,并根据它是 soft_block / hard_block / fail 来决定是 continue、incomplete 还是 failed,而不是现在先跑完再把结果聚合起来,决定是否通过检查。
- HardBlock 不在执行层立即短路:当前不能收尾,而是要等外部条件,例如权限、用户输入、外部资源。和上一点结合来看,也就是说本来其实应该在执行时就发挥作用,而不是白白等到所有都跑完了,再来看是否允许通过检查
- FailClosed 当前只覆盖很窄的 soft_block + env_missing 情况,没有覆盖 required verifier 未配置、命令不存在、环境缺失、执行被拒绝等“验收根本没法成立”的情况
-FailOpen 当前会在聚合前把任意 VerificationFail 降成 pass,没有区分执行层失败和领域失败;默认配置下未必触发,但代码语义允许错误放行。
- stopReason 仍混用文案与 ErrorClass
- NormalizeResult 仍会污染 pass 结果
9. verifier command 仍依赖 shell string
当前 verifier execution 仍以 shell string 为基础,仍走:
Windows powershell -Command
Unix sh -lc
这与 acceptance 主链收敛和执行语义稳定化的目标冲突,也让命令执行、错误分类和策略约束难以真正收敛。
10. runtime.verification.max_retries 和 acceptance hook stage 仍是旧产物
当前主链中仍真实存在:
- runtime.verification.max_retries
- runtime.verification.default_task_policy
- acceptance hook stage
- hook failure policy 配置面
这些旧配置 / 旧抽象仍在影响主链,不适合继续保留,否则 acceptance 很难真正收敛成唯一链路。
目标方案
本次将 runtime final acceptance 收敛为一条唯一主链:
session-owned verification profile
-> completion gate
-> verifier gate
-> acceptance decision
-> terminal decision
这条主链只负责一件事:
对“现在是否可以结束任务”给出唯一、稳定、可测试的决定。
收敛后的边界固定为:
- session.TaskState 和 session.TodoItem 是唯一结构化验收输入来源
- completion gate 只判断“是否允许尝试收尾”
- verifier gate 只判断“这次收尾是否真的通过检查”
- acceptance engine 只负责聚合 gate 结果并产出终态
- verify/* 只消费结构化输入并返回稳定结果
收敛后的终态规则固定为:
- completion gate 未通过:continue
- verifier 首个非 pass 为 soft_block:continue
- verifier 首个非 pass 为 hard_block:incomplete
- verifier 首个非 pass 为 fail:failed
- 全部 verifier pass:accepted
本次明确删除以下旧语义,不做兼容保留:
- verification disabled -> compatibility fallback accepted
- final_intercept=false -> 跳过 verifier gate
- task_type metadata -> verifier policy mapping
- metadata.expected_files
- metadata.content_match
- intercepted final 先写入会话、再由 acceptance 补救
- acceptance hook 决策链
- shell string verifier command
- FailOpen / FailClosed 这类对 verifier 结果的事后改写策略
实现设计
1. 用 VerificationProfile 固定 verifier 集合
在 session.TaskState 中新增 VerificationProfile,由 runtime/session 持有,作为 verifier 选择的唯一依据,不再从 task_id / goal / next_step 文本猜测任务类型。
允许值固定为:
- task_only
- create_file
- docs
- config
- edit_code
- fix_bug
- refactor
映射固定为:
- task_only -> todo_convergence
- create_file / docs -> todo_convergence, file_exists, content_match
- config -> todo_convergence, file_exists, content_match, command_success
- edit_code -> todo_convergence, git_diff, build, test, typecheck
- fix_bug -> todo_convergence, git_diff, test, build, typecheck
- refactor -> todo_convergence, git_diff, build, test, lint, typecheck
要求:
- VerificationProfile 属于 session-owned 任务状态,不进入 TUI 输入面
- session 的 clone / normalize / clamp / persistence / restore 全链支持该字段
- compact 输入输出必须完整保留该字段
- profile 缺失或非法直接返回结构化错误
- 删除 inferTaskType、resolveTaskType、default_task_policy
2. 用 session 契约统一 todo / artifact / content 验收输入
新增:
type TodoContentCheck struct {
Artifact string
Contains []string
}
type TodoItem struct {
...
Acceptance []string
Artifacts []string
Supersedes []string
ContentChecks []TodoContentCheck
...
}
契约固定为:
- Acceptance 只给人读,不参与机器判定
- Artifacts 是文件交付物声明
- ContentChecks 是唯一内容规则来源
- Supersedes 是 replacement todo 的替代关系声明
同时要求:
- verifier 输入显式带上 Artifacts / ContentChecks / Supersedes
- file_exists 不再读 metadata,只检查 required todo 的 Artifacts 与 TaskState.KeyArtifacts
- content_match 不再读 metadata,只检查 ContentChecks
- content_match 只支持“目标文件包含全部 token”,不支持 regex、不支持语义匹配、不把自然语言 acceptance 解释成规则
- required todo 的 canceled 状态迁移在 session 层收紧,只有存在显式 replacement required todo 时才合法
3. 固定 verifier 结果模型,不再做策略层改写
verifier 结果只允许四种:
- pass:该检查通过
- soft_block:当前不能收尾,但 agent 还能继续补救
- hard_block:当前不能收尾,且需要外部条件
- fail:这次任务或这次验收已经明确失败
关键语义固定为:
todo_convergence
-
required completed -> pass
-
required failed -> fail
-
required canceled 且无显式 replacement -> fail
-
blocked 且原因为 permission_wait / user_input_wait / external_resource_wait -> hard_block
-
其余未完成状态 -> soft_block
-
file_exists
- 目标 artifact 缺失 -> soft_block
- artifact 路径非法或越界 -> fail
-
content_match
- 文件存在但缺少 token -> soft_block
- 规则结构非法 -> fail
-
git_diff
无交付证据 -> soft_block
-
build / test / lint / typecheck / command_success
-
命令成功执行但结果未通过 -> soft_block
-
命令不存在、未配置、环境缺失、执行被拒绝、执行超时、执行层内部错误 -> fail
-
同时删除 FailClosed 和 FailOpen。
verifier 结果语义必须在 verifier 本身固定下来,不能再由策略层事后重写。
4. orchestrator 改为“首个非 pass 即短路”
orchestrator 改为按顺序执行 verifier,并在执行层短路:
- 遇到首个 soft_block,立即停止后续 verifier,返回 continue
- 遇到首个 hard_block,立即停止后续 verifier,返回 incomplete
- 遇到首个 fail,立即停止后续 verifier,返回 failed
- 只有全部 pass,才返回 accepted
同时要求:
- HardBlock 必须在执行层生效,而不是只做聚合标签
- stopReason 只按稳定 ErrorClass 映射
- pass 结果的 ErrorClass 必须为空
- NormalizeResult 不能给 pass 补失败分类
5. intercepted final 改为 candidate final,interception 只保留一个真相源
provider 返回 final 后拆成两步:
- 先持久化 usage / provider / model
- assistant final 仅作为 candidate final 保存在内存
acceptance 决策后:
- accepted / incomplete / failed 才把 candidate final 写入 session.Messages
- continue 时不写 candidate final,只追加 reminder
同时收敛 final interception 状态:
-
runState.finalInterceptStreak 是唯一真相源
-
execute 阶段如产生新的 business/exploration progress,则置 pendingFinalProgress = true
-
AcceptanceContinue 时:
- pendingFinalProgress == true -> streak 清零
- 否则 streak 自增
-
之后清空 pendingFinalProgress
-
删除 continue 分支中的空 evidence progress 评估
-
不再从 progress.LastScore.NoProgressStreak 反写 interception 计数
6. 收敛执行模型并清理旧配置
执行层统一收敛为:
- git_diff 默认改为 git status --porcelain --untracked-files=normal
- VerifierConfig.Command 改为 argv 数组
- verifier execution 改为直 exec
- 删除 powershell -Command / sh -lc
主链同步清理:
- 删除 acceptance hook stage,改为普通校验函数
- 删除 runtime.verification.max_retries
- 删除 runtime.verification.default_task_policy
- 删除 runtime.verification.enabled 和 runtime.verification.final_intercept 对 acceptance 主链的关闭语义
- 删除 acceptance hooks 配置面
- 删除 compatibility fallback 相关状态、事件和测试语义
7. Session 契约收敛
本次不仅调整 runtime / verifier 行为,也同时收敛 session 领域状态,使 verifier 所需信息不再通过 runtime metadata 旁路传递,而是正式进入 session.TaskState 与 session.TodoItem。
要求如下:
- TaskState 新增 VerificationProfile,作为 verifier 选择的唯一结构化来源
- TodoItem 新增 Supersedes 与 ContentChecks
- Artifacts 不再只是展示字段,而是正式文件交付物声明
- Acceptance 保留为人类可读说明,不参与机器判定
- required todo 的取消语义在 session 层收紧,只有存在显式 replacement required todo,且其 Supersedes 包含原 todo ID 时,原 todo 才允许转为 canceled
- compact / normalize / clamp / persistence / restore 必须同步支持上述字段,确保同一任务在存盘、恢复和 compact 后仍保持同一验收契约
这一层的目标是把“验什么、凭什么算替代、文件和内容如何验收”从 runtime 临时拼装逻辑,收敛为 session 自己持有的正式领域契约。
任务清单
测试验证
非目标
- 不扩张到 TUI 产品交互改造
- 不新增新的工具能力
- 不做更智能的 verifier 推断
- 不修复独立的
Tool Facts / Completion Gate bug
- 不保留 compatibility fallback、task 文本猜测、metadata 旁路、shell string verifier、acceptance hook 决策链的长期双轨兼容
验收标准
摘要
本 issue的目的是重构
runtime final acceptance主链,收敛为唯一闭环:session-owned verification profile -> completion gate -> verifier gate -> acceptance decision -> terminal decision目标是让 runtime 对“什么时候能收尾、什么时候必须继续、什么时候已经失败”给出稳定、唯一、可测试的决定。
背景
当前仓库已经有
completion gate、verifier gate、acceptance decision这些零件,但它们还没有形成一条唯一、稳定、可解释的收尾链路。目前 runtime 在 final acceptance 上仍然保留了旧路径、猜测逻辑、metadata 旁路和双真相源状态,导致相似任务在相似状态下并不能稳定得到相似的收尾结果。
本次目标是把 final acceptance 收敛为一条主链,而不是继续叠加补丁式修复。
当前真实存在的问题
1. required todo 失败后仍可能被视作完成项
当前 todo_convergence 仍把 required todo 的 failed/canceled 当做任务完成。
这会把“任务做完了”和“任务做失败了/被取消了”混为一谈,导致必做项已明确失败时,acceptance 主链仍可能继续向 accepted 推进。
具体会发生在这些场景:
具体后果是:
2. acceptance 主链仍允许通过配置绕过 verifier gate
当前 acceptance engine 仍保留:
verification disabled -> compatibility fallback acceptedfinal_intercept=false -> 本轮 final acceptance 不跑 verifier这意味着 verifier gate 现在还不是 final acceptance 的必经环节,而仍然可以被配置整体跳过。
从主链语义上讲,这会破坏“检查通过后才能完成任务”的基本约束,也使 acceptance 仍然保留旧兼容逃生门。
3. verifier policy 仍依赖 task 文本猜测
当前 verifier 选择仍依赖:
现在系统不是自己保存一份正式清单,而是看任务描述里有没有像“fix bug”、“docs”、“config”、“refactor”这样的词,再临时猜这次该检查什么,而不是建立在 runtime 自己持有的结构化任务状态之上。
这会导致同类任务仅因措辞不同,就可能走出不同的验收路径。
4. file/content 验收仍走 metadata 旁路
当前:
可以把这两者理解成:
这会导致验收规则容易漂移、丢失,或者与 session 中真实保存的任务状态不一致。
5. intercepted final 仍会先落盘
当前 assistant final 仍在 acceptance 前先写入 session.Messages。
如果 final 随后被拦截并 continue,下一轮模型仍会看到自己刚刚说过的“任务完成”,从而更容易进入重复收尾输出。
6. final interception 仍存在双真相源
当前 continue 分支仍会做一轮空 evidence progress 评估,在什么业务都没有的情况下还做一轮计分操作,并把两个不一样的计分系统混在一起使用。
这意味着 final interception 的计数并不是一个自洽系统,而是会被另一套 progress 状态反向覆盖。
7. git_diff 默认仍漏掉 staged-only 交付
当前 git_diff 默认仍使用能看到工作区相对暂存区变化的命令:
git diff --name-only但 staged-only 场景是:
在这种情况下:
8. orchestrator / failure policy / stop reason 语义仍不稳定(第四点指的是输入规则,这里说的是结果解释方式)
当前 verifier orchestrator 存在规则分散在多个层里,且部分规则依赖文案而不是依赖稳定枚举的问题:
-FailOpen 当前会在聚合前把任意 VerificationFail 降成 pass,没有区分执行层失败和领域失败;默认配置下未必触发,但代码语义允许错误放行。
9. verifier command 仍依赖 shell string
当前 verifier execution 仍以 shell string 为基础,仍走:
这与 acceptance 主链收敛和执行语义稳定化的目标冲突,也让命令执行、错误分类和策略约束难以真正收敛。
10. runtime.verification.max_retries 和 acceptance hook stage 仍是旧产物
当前主链中仍真实存在:
这些旧配置 / 旧抽象仍在影响主链,不适合继续保留,否则 acceptance 很难真正收敛成唯一链路。
目标方案
本次将 runtime final acceptance 收敛为一条唯一主链:
这条主链只负责一件事:
对“现在是否可以结束任务”给出唯一、稳定、可测试的决定。
收敛后的边界固定为:
收敛后的终态规则固定为:
本次明确删除以下旧语义,不做兼容保留:
实现设计
1. 用 VerificationProfile 固定 verifier 集合
在 session.TaskState 中新增 VerificationProfile,由 runtime/session 持有,作为 verifier 选择的唯一依据,不再从 task_id / goal / next_step 文本猜测任务类型。
允许值固定为:
映射固定为:
要求:
2. 用 session 契约统一 todo / artifact / content 验收输入
新增:
契约固定为:
同时要求:
3. 固定 verifier 结果模型,不再做策略层改写
verifier 结果只允许四种:
关键语义固定为:
todo_convergence
required completed -> pass
required failed -> fail
required canceled 且无显式 replacement -> fail
blocked 且原因为 permission_wait / user_input_wait / external_resource_wait -> hard_block
其余未完成状态 -> soft_block
file_exists
content_match
git_diff
无交付证据 -> soft_block
build / test / lint / typecheck / command_success
命令成功执行但结果未通过 -> soft_block
命令不存在、未配置、环境缺失、执行被拒绝、执行超时、执行层内部错误 -> fail
同时删除 FailClosed 和 FailOpen。
verifier 结果语义必须在 verifier 本身固定下来,不能再由策略层事后重写。
4. orchestrator 改为“首个非 pass 即短路”
orchestrator 改为按顺序执行 verifier,并在执行层短路:
同时要求:
5. intercepted final 改为 candidate final,interception 只保留一个真相源
provider 返回 final 后拆成两步:
acceptance 决策后:
同时收敛 final interception 状态:
runState.finalInterceptStreak 是唯一真相源
execute 阶段如产生新的 business/exploration progress,则置 pendingFinalProgress = true
AcceptanceContinue 时:
之后清空 pendingFinalProgress
删除 continue 分支中的空 evidence progress 评估
不再从 progress.LastScore.NoProgressStreak 反写 interception 计数
6. 收敛执行模型并清理旧配置
执行层统一收敛为:
主链同步清理:
7. Session 契约收敛
本次不仅调整 runtime / verifier 行为,也同时收敛 session 领域状态,使 verifier 所需信息不再通过 runtime metadata 旁路传递,而是正式进入 session.TaskState 与 session.TodoItem。
要求如下:
这一层的目标是把“验什么、凭什么算替代、文件和内容如何验收”从 runtime 临时拼装逻辑,收敛为 session 自己持有的正式领域契约。
任务清单
todo_convergence,收紧 required todo 的failed/canceled语义TaskState新增VerificationProfile,并让 compact / normalize / persistence / restore 全链支持TodoItem新增Supersedes与ContentChecks,同时收紧 required todo cancel 语义inferTaskType / resolveTaskType / default_task_policy,将 verifier 选择改为只基于VerificationProfileArtifacts / ContentChecks / Supersedesfile_exists改为消费Artifacts / KeyArtifacts,将content_match改为消费ContentChecksfinalInterceptStreak收敛为唯一真相源git_diff默认实现切到git status --porcelainFailClosed / FailOpen,收敛stopReason和NormalizeResult语义runtime.verification.max_retries等旧配置,更新 acceptance / verifier / todo 契约文档测试验证
failed或canceled without replacement时不会再 acceptedsession.Messages;continue 后 usage / provider / model 仍正确持久化Artifacts端到端驱动file_exists,ContentChecks端到端驱动content_matchfinalInterceptStreak,无新 progress 时连续拦截单调递增,且不再受progress.LastScore.NoProgressStreak回写影响pass结果短路,pass结果ErrorClass为空git_diff覆盖 staged-only、unstaged-only、untracked-only、ignored-onlygo test ./internal/runtime/... ./internal/session/... ./internal/config/...和go test ./...通过非目标
Tool Facts / Completion Gatebug验收标准
runtime.verification.max_retries已清理