Skip to content

[Module] Agent 编码正确性工作流:快照回滚、AST 增量守卫与测试强检 #32

Description

@SATA260

[Module] Agent 编码正确性工作流:快照回滚、AST 增量守卫与测试强检

架构落地演进说明 (Architecture Evolution Note):
本 Issue 完整记录了编码正确性工作流最初规划的 7 阶段体系。在实际工程落地与场景验收中(见 PR #33 / Commit 60c4d75 及 docs/architecture.md),团队对收尾链路进行了进一步聚焦与精简:

  1. 移除收工独立旁路复审(Evaluate):原阶段五计划在命令验证通过后调度的二次纯净小模型挑刺复审(evaluating)已正式下线,前端不再绘制复审卡片,收尾质检彻底收敛为工程级命令强检(verifying 门禁),测试通过后直接撰写收尾说明进入 completed。
  2. 复审模型职责收敛:独立评判模型(EvaluatorModel)不再参与收尾,专职收敛于工具出站安全审批(在 auto 审批模式下辅助填单、以及对工作区目录外的越界写操作进行安全审计)。
  3. 下文保留原设计各阶段完整规格与时序图,以便追溯方案演进脉络。

功能职责

编码正确性工作流为 Agent 的任务执行建立全流程质量门禁:从入口探索降本、写时实时拦截,到收尾命令验证、独立旁路复审、Git 快照回滚与文档同步断言,确保编码结果可验证、可审计、可撤销。

  • 阶段一(探索降本,只读子代理):
    • 增加专用的只读探索能力,主模型检索代码时由便宜小模型(如 flash 级别)子代理先行翻阅文件与搜索关键词。
    • 在子代理内部完成信息提炼,仅向主上下文回传带精确 文件:行号 引用的紧凑结论,避免成百上千行原文塞入主对话,在入口处大幅降低 Token 消耗。
  • 阶段二(编辑守卫,写时拦截):
    • 代码写盘后即时进行语法解析与局部编译校验,重点拦截括号缺失、语法断裂、引用未定义等 100% 确定的硬伤错误,不拦截代码风格与非致命告警。
    • 自动比对改动前后的错误差异,只拦截本次修改新引入的增量错误,放行历史遗留错误,避免误伤。
    • 发现新错误时立即自动回滚文件至修改前基线,将工具调用标为失败,并将报错位置、改动前后代码对比及明确的避坑提示组合反馈给模型,防止破损代码沉淀与连续劣质重试。
  • 阶段三(计划契约,测试约束):
    • 规范计划文件的结构化验收清单,强制要求每条验收项明确目标、验证命令(业务逻辑必须绑定具体测试命令)、初始 false 的通过状态以及支撑依据。
    • 在保存计划时强校验验收结构完整性,且对已有计划条目只增不删,禁止模型删除条目或弱化验证命令。
    • 对已有测试文件实行写入保护:修改既有测试文件必须经过人工审批,禁止自动放行,杜绝模型删改旧断言让代码蒙混过关;新测试在旧代码上必须为红,防止生成无意义假测试。
    • 提供单向受控的标记工具,仅允许模型把验收状态从 false 翻转为 true 且必须提供对应的测试或工具凭据。
  • 阶段四(验证门禁,收尾强检):
    • 识别会话写操作行为:当模型单轮不再调用工具准备结束、且本次 Run 存在写文件或执行命令等副作用时,自动拦截结束流程并触发工程级验证;纯只读对话自动放行。
    • 加载工作区约定的验证规则,执行构建、测试、类型检查等自动化命令序列,并向用户实时推送验证进度与结果事实。
    • 细分验证出口:全部通过则放行进入后续阶段;测试或构建失败时裁剪报错日志作为上下文反馈模型继续修复;环境故障、命令不存在或超时等外部问题明确判定为无法验证并挂起转交人工,不归咎于模型。
    • 建立防死循环保护网:限制单次 Run 内最大打回轮次;对失败日志提取特征指纹,连续两次相同指纹立即熔断转人工,防止模型无意义原地空转。
  • 阶段五(旁路复审,独立裁定):
    • 在自动化验证通过后,将本次任务瘦身后的完整代码 diff、带通过状态及证据的验收清单、以及自动化验证摘要打包送审。
    • 采用无写工具、未参与编码的纯净独立上下文进行挑剔审查,排除模型自我辩解与认知盲区,重点筛查偷删既有测试、弱化断言、未履约的虚假通过以及范围越界等隐蔽缺陷。
    • 实施极致 Token 压缩策略:剥离锁文件与构建产物、纯空格改动,并在改动超过阈值时拆分或熔断;同指纹改动不重复送审。
    • 严格输出结构化裁定:全部合格判定通过;存在瑕疵判定需要修改并附带精准到文件与位置的问题清单,打回原模型继续修复;复审异常、超时或无法解析时实施 fail-close 策略,一律转交人工裁定。
  • 阶段六(快照回滚,兜底撤销):
    • 在一次 Run 即将执行首个破坏性写操作(改文件、执行副作用命令)前,直接复用本机 Git 静默捕获当前工作区的轻量暂存快照并记录未跟踪文件清单,不生成垃圾提交,不干扰正常 Git 历史。
    • 提供三种粒度的安全退回机制:仅恢复代码文件(保留对话以调整提示重试)、仅截断失败消息(保留代码继续排查)、全面回滚(代码与对话同步还原至任务开始前)。
    • 在验证失败、复审驳回及打回超限的人工裁决卡片上无缝集成一键回退入口,解除用户开启高度自动化执行的后顾之忧。
  • 阶段七(架构断言,文档同步):
    • 将架构设计文档中约定的模块单向流转、禁止反向依赖与层级穿透等抽象规则,转化为确定性的自动化测试断言。
    • 建立代码事实与文档的双向同步断言:代码新增的核心目录、工具名与事件类型必须在架构文档中出现;文档提及的历史模块在代码中必须存在,违规时输出包含“问题/修复指引/参考文档”的标准三段式诊断。
    • 将架构合规测试纳入收尾验证流水线,作为任务正常完成的基础前置条件。

边界

  • 守卫只拦截确定性的语法与编译破损,不拦截代码风格、命名或测试失败
  • 修改已有测试文件必须人工审批,Agent 模式与 Yolo 模式均不可自动放行;新增测试文件不受限
  • 验收项状态只允许从 false 翻为 true 且必须附带证据,不允许删除条目或弱化验证命令
  • 验证失败喂回模型继续修,不直接标记 Run 失败;连续相同失败或环境不可执行时挂起转人工,不无限打回
  • 复审模型使用只读无工具纯净上下文,不读取编码过程中的历史对话,不与写代码模型共用上下文
  • 复审输入严格过滤锁文件与生成代码;若模型未改代码便重复申请结束,直接复用上次裁决,不再发起模型调用
  • 探索子代理只读,其运行不计为写操作副作用,不触发快照与收尾验证,其调用产生的 Token 单独记账
  • 快照仅用于本次 Run 的故障恢复与人工接管回滚,仅支持 Git 仓库工作区(非 Git 目录降级提示),不搞文件全量复制,不维护 shadow git 冲突源
  • 架构断言与文档同步测试只检查包间依赖方向与层级单向流转,不检查具体业务逻辑正确性

状态机与核心时序(时序驱动)

业务代码的流转完全由 Agent 运行时状态机保证。系统一拍(Step)只干一件事,数据库在每拍结束时保持原子一致性。

1. 三层核心概念

层级 职责 存储位置 关键枚举/取值
状态(RunStatus) 任务当前对外停留在哪个阶段 runs.status queued / loading_context / running_llm / executing_tools / verifying(新增)/ evaluating(新增)/ waiting_approval / 终态(completed / failed / cancelled)
唤醒原因(Phase) 这一拍为什么被触发执行 内存 StepJob.Phase user_input / llm_result / tools_batch_result / verify_result(新增)/ evaluate_result(新增)/ human_approved / human_override(新增)/ human_abort
指令(Instruction) Brain 决策本拍该调度什么执行器 内存 Instruction.Type call_llm / call_tools_batch / verify(新增)/ evaluate(新增)/ finish

2. 分阶段时序设计(极简纯线性,无框选干扰)

为彻底消除图例重叠与多层框选线带来的视觉干扰,各阶段统一采用单线直出的泳道时序,分支结果直接标注在线条文字上:

阶段一:需求输入与只读探索(降 Token)

主模型查阅代码时,调度廉价小模型子代理先行翻阅,返回紧凑的精确引用结论,避免大量原文涌入主模型上下文。

sequenceDiagram
  autonumber
  actor User as 用户
  participant Coord as 协调器
  participant Engine as 执行引擎
  participant Explorer as 探索子代理 (小模型)
  participant MainLLM as 主编码模型

  User->>Coord: 发送编程需求
  Coord->>Engine: 启动任务 (call_llm)
  Engine->>Explorer: 派发探索任务 (只读查阅代码与关键词)
  Explorer-->>Engine: 返回精确行号引用与提炼结论 (Citations)
  Engine->>MainLLM: 携带提炼结论与上下文发起推理
  MainLLM-->>Engine: 产出精确代码修改指令 (write / edit)
Loading

流转要点:

  • 探索子代理只读不写,其调用不计入写操作副作用(HadSideEffects = false),不触发 Git 快照。
  • 子代理消耗的 Token 独立计量展示,主对话上下文保持干净紧凑。

阶段二:写前快照与写时守卫(防写坏)

写代码前静默拍快照;写盘后立即做增量 AST 语法与编译轻检,一旦发现新增硬伤错误立刻自动撤销并恢复原文件。

sequenceDiagram
  autonumber
  participant Engine as 执行引擎
  participant Snapshot as 快照管理器
  participant Guard as 写时守卫
  participant MainLLM as 主编码模型

  Engine->>Snapshot: 首次写代码前,静默拍下工作区暂存快照
  Snapshot-->>Engine: 记录快照 SnapshotOID 与基线文件清单
  Engine->>Guard: 执行代码写入 (write / edit)
  Guard->>Guard: 即时 AST 语法树解析与单包编译对比轻检
  Guard-->>Engine: 【情况A 正常】:语法无硬伤,安全写入工作区磁盘
  Guard-->>Engine: 【情况B 破损】:检测到新增语法/编译错误,当场撤销并回滚原文件
  Engine-->>MainLLM: 喂回结果 (正常则继续下一项;报错则附带改动对比促其修复)
Loading

流转要点:

  • 仅比对增量错误,放行既有历史错误,不发生误杀。
  • 出错瞬间文件基线毫秒级复原,破损代码绝不沉淀在磁盘上。

阶段三:申请结束与收尾命令验证(Verify 强检)

模型自认为做完了(不再调用工具)时,大脑直接拦截结束流程;只要动过代码,强制跑自动化测试与构建。

sequenceDiagram
  autonumber
  actor User as 用户
  participant MainLLM as 主编码模型
  participant Brain as 决策大脑
  participant Engine as 执行引擎
  participant Verifier as 验证门禁

  MainLLM->>Brain: 模型自认完成,不再调用工具 (申请收工)
  Brain->>Brain: 拦截收工,检查 HadSideEffects (动过代码强制安检)
  Brain->>Engine: 下发 verify 指令 (状态迁移至 verifying)
  Engine->>Verifier: 按改动文件定向执行构建与测试 (.cursor/verify.yaml)
  Verifier-->>Engine: 返回验证结果 (测试日志与错误指纹)
  Engine->>Brain: 【验证通过】全绿放行,准备进入独立复审 (evaluating)
  Engine-->>MainLLM: 【测试失败】包装报错日志,打回模型继续修复重试
  Engine-->>User: 【指纹重复/超限】触发防死循环熔断,挂起转人工卡片
Loading

流转要点:

  • 结束权收归系统,模型无权自主单方面收工。
  • 提取失败指纹,连续两次完全相同错误立刻熔断转人工,绝不无意义原地空转。

阶段四:纯净独立旁路复审(Evaluate 挑刺)

自动化测试通过后,由未经编码上下文污染的独立模型执行挑剔审查,排查偷删测试、弱化断言等隐蔽作弊行为。

sequenceDiagram
  autonumber
  actor User as 用户
  participant Coord as 协调器
  participant Engine as 执行引擎
  participant Evaluator as 独立复审 (纯净小模型)
  participant MainLLM as 主编码模型

  Coord->>Engine: 自动化验证通过,下发 evaluate 指令 (evaluating)
  Engine->>Evaluator: 传入瘦身 diff + 验收证据 + 验证摘要 (无历史对话)
  Evaluator->>Evaluator: 挑剔审查:排查偷删测试、弱化断言或范围越界
  Evaluator-->>Coord: 【复审通过】判定合格,任务正常结束 (completed)
  Evaluator-->>MainLLM: 【发现瑕疵】返回结构化驳回清单,打回模型整改
  Evaluator-->>User: 【超限或异常】打回超限或结果说不清,fail-close 挂起转人工
Loading

流转要点:

  • 纯净独立裁判:不读编码历史对话,无认知盲区与自我辩解偏向。
  • 极限降本:剔除 lock 文件与空白改动,同指纹改动直接复用上次裁决。

阶段五:人工兜底与一键撤销(Human Override)

当验证或复审熔断转人工时,用户可通过操作卡片实施裁决,包括一键将工作区复原至修改前。

sequenceDiagram
  autonumber
  actor User as 用户
  participant Coord as 协调器
  participant Snapshot as 快照管理器
  participant MainLLM as 主编码模型

  Coord->>User: 任务挂起并弹出操作卡片 (附带日志、差异与回退按钮)
  User->>Coord: 用户在操作卡片上做出人工裁决
  Coord-->>User: 【裁决 A: Accept】人工确认无碍,强制标记任务完成
  Coord->>MainLLM: 【裁决 B: Retry】补充提示并清空失败指纹,允许模型重试
  Coord->>Snapshot: 【裁决 C: Abort】彻底放弃,调用 Snapshot.Restore
  Snapshot->>Snapshot: git reset 还原代码至 SnapshotOID 并清理多余新增文件
  Snapshot-->>User: 工作区毫秒级无损复原,任务标记为已取消 (cancelled)
Loading

流转要点:

  • 依靠原生轻量 Git stash,秒级回滚且不污染分支历史。
  • 赋予防御性安全感:随时可一键撤销全部写操作。

3. 详细流转规则汇总

  1. 写副作用标记与快照触发:
    • 在 call_tools_batch 中,首次成功调用 write / edit / bash / powershell 前,检查 HadSideEffects 是否为 false。
    • 若为 false,立即通过 Git 捕获轻量快照生成 SnapshotID,随后置 HadSideEffects = true。
  2. 收尾分叉判定(PhaseLLMResult):
    • 模型回复结束且无 pending 工具调用:
      • 若 HadSideEffects == false(纯问答、只读搜索):Brain 下发 finish(completed),直接结束。
      • 若 HadSideEffects == true:Brain 下发 verify 指令,Run 状态迁移为 verifying。
  3. 验证结果裁决(PhaseVerifyResult):
    • 验证通过(passed):Brain 下发 evaluate 指令,状态迁移为 evaluating。
    • 验证失败(failed):
      • 若当前失败日志指纹与 LastVerifyFingerprint 相同,或 VerifyRound >= MaxVerifyRounds:立刻熔断,挂起为 waiting_approval(审批单附带失败日志与一键回滚选项)。
      • 否则:记录指纹,VerifyRound++,将报错信息封装为 tool 消息喂回模型,下发 call_llm,状态转为 running_llm。
    • 无法运行(cannot_run,如环境依赖缺失、命令超时):明确归为外部环境异常,直接挂起为 waiting_approval 转人工。
  4. 旁路复审裁决(PhaseEvaluateResult):
    • 复审通过(pass):Brain 下发 finish(completed),任务成功收尾。
    • 存在问题(needs_work):
      • 若 EvaluateRound >= MaxEvaluateRounds:熔断挂起为 waiting_approval。
      • 否则:EvaluateRound++,将问题清单注入上下文,下发 call_llm 进入修复。
    • 说不清或复审异常(escalate):fail-close 挂起为 waiting_approval 转人工。
  5. 人工介入裁定(PhaseHumanOverride):
    • 人工在验证/复审挂起卡片上裁决:
      • Accept:用户确认无碍,直接 finish(completed) 归档。
      • Retry:清除重复指纹,下发 call_llm 允许模型继续修正。
      • Abort:调用快照恢复文件至初始干净基线,下发 finish(cancelled)。
  6. 重启与断点恢复:
    • 服务重启扫描可恢复状态白名单增加 verifying 与 evaluating。
    • recoverPhase 映射:verifying 安全回退至 llm_result 重新触发验证(命令执行具备幂等性);evaluating 回退至 verify_result{passed} 重新发起复审。

内部拆分

编辑守卫(EditGuard)

管文件修改后的即时增量语法与编译解析检查、新旧错误对比、出错自动恢复文件基线并生成对比反馈。不管业务逻辑正确性,不跑全量测试,不拦截代码风格。

// LintSeverity 诊断级别(严重错误阻断提交,告警信息仅作提示)。
type LintSeverity string

const (
    SeverityError   LintSeverity = "error"   // 语法断裂或编译硬伤,必须阻断并回滚。
    SeverityWarning LintSeverity = "warning" // 代码风格或非致命提示,放行不阻断。
)

// LintDiagnostic 语法与编译轻检诊断明细。
type LintDiagnostic struct {
    Line     int          // 错误发生的起始行号(1-based)。
    Column   int          // 错误发生的列号(1-based)。
    Message  string       // 编译器或语法解析器输出的原始报错描述。
    Severity LintSeverity // 诊断严重程度:error 触发回滚,warning 放行。
}

// EditInspection 文件编辑守卫校验结果报告。
type EditInspection struct {
    FilePath     string           // 被校验的文件路径(工作区相对路径)。
    OldContent   string           // 修改前的文件基线原始内容(用于回滚与生成对比)。
    NewContent   string           // 本次修改后试图写入的新内容。
    NewErrors    []LintDiagnostic // 本次改动新引入的增量错误(已自动排除修改前既有的历史错误)。
    RollbackDone bool             // 是否已自动撤销修改并还原文件(仅当存在 NewErrors 时为 true)。
    DiffSnippet  string           // 用于反馈给模型的错误位置上下文对比代码片段。
}

// InspectEdit 在文件写盘后立即做增量语法与局部编译轻检。
// 参数:
//   - filePath: 被修改的文件路径(工作区相对路径)
//   - oldContent: 修改前的文件原始内容基线
//   - newContent: 本次修改试图写入的新内容
// 返回:
//   - inspection: 检查结果(含增量错误与是否已自动回滚)
//   - err: 执行检查本身的系统异常(非代码语法错误)
func InspectEdit(filePath string, oldContent string, newContent string) (inspection EditInspection, err error)

计划契约与测试约束(PlanContract)

管计划文件中验收条目的结构解析与强校验、受控的状态翻转(由 false 变 true)、事实依据绑定,以及已有测试文件的写入审批拦截与新测试红绿判定。不管业务代码如何修改,不执行实际业务命令。

// PlanItem 计划验收清单中的独立验收项契约。
type PlanItem struct {
    ID          string // 验收项唯一标识(如 "item-1"、"item-2")。
    Description string // 验收目标描述,清晰说明本项要实现或验证的业务功能。
    VerifyCmd   string // 验证命令(业务项强制绑定具体测试命令,如 "go test -run TestX";纯文档/配置项可标 "manual")。
    Passes      bool   // 验收是否通过;创建时强制为 false,仅允许通过 plan_pass 单向翻转为 true,不可逆转。
    Evidence    string // 支撑通过的客观凭据(工具调用 ID、测试运行日志摘要,翻转为 true 时必填,禁止为空)。
    IsTestBound bool   // 是否绑定了自动化测试命令(业务逻辑项强制为 true)。
}

// PlanContract 计划文件的结构化数据契约。
type PlanContract struct {
    PlanName  string     // 计划文件名称或相对路径(如 ".cursor/plans/foo.plan.md")。
    Title     string     // 计划标题。
    Items     []PlanItem // 结构化验收清单;修改保存时只增不删,禁止弱化已有命令。
    UpdatedAt int64      // 契约最后更新的 Unix 毫秒时间戳。
}

// ValidatePlan 校验计划 frontmatter 结构与验收清单合法性。
// 参数:
//   - content: 计划文件全文内容(含 frontmatter 结构化数据)
//   - existing: 之前已保存的旧契约对象(若首次创建则为 nil)
// 返回:
//   - contract: 校验通过后解析出的有效计划契约
//   - err: 校验失败原因(如验收清单缺失、试图删减既有条目、或弱化验证命令)
func ValidatePlan(content string, existing *PlanContract) (contract PlanContract, err error)

// MarkPassed 将指定验收条目标记为通过(受控单向状态翻转)。
// 参数:
//   - planName: 计划文件名或相对路径
//   - itemID: 待标记通过的验收条目 ID
//   - evidence: 证明通过的客观证据(测试运行记录或工具执行 ID,禁止为空)
// 返回:
//   - contract: 更新后的完整计划契约
//   - err: 条目不存在或证据缺失导致的错误
func MarkPassed(planName string, itemID string, evidence string) (contract PlanContract, err error)

// IsExistingTestFile 判定目标文件是否为已存在的既有测试文件。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
//   - filePath: 待检查的目标文件相对路径
// 返回:
//   - true 表示目标是已存在的测试文件(*_test.go / *.test.ts 等),其写操作必须强制进入人工审批
func IsExistingTestFile(workspaceRoot string, filePath string) bool

// VerifyTestFailsOnBase 校验新测试在修改前的基线代码上执行是否失败(防作弊测试验证)。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
//   - baseOID: 本次任务发起前的代码基线快照或提交 OID
//   - testFilePath: 本次任务新编写的测试文件路径
// 返回:
//   - failsOnBase: 在旧代码上是否如期失败(若通过说明测试未测到新行为,判定为假测试)
//   - err: 检出基线或执行测试时的系统异常
func VerifyTestFailsOnBase(workspaceRoot string, baseOID string, testFilePath string) (failsOnBase bool, err error)

验证门禁(Verifier)

管在模型准备结束时调度工作区验证命令,根据退出码判定通过、失败或不可执行;根据改动范围定向缩小测试范围;维护打回轮次与报错指纹,识别原地打转。不管如何修改代码,不负责模型调用。

// VerifyStatus 工作区验证的判定状态枚举。
type VerifyStatus string

const (
    VerifyStatusPassed    VerifyStatus = "passed"     // 全部验证命令退出码为 0,验证全通。
    VerifyStatusFailed    VerifyStatus = "failed"     // 测试断言失败或编译未通过,打回模型修复。
    VerifyStatusCannotRun VerifyStatus = "cannot_run" // 外部环境异常(命令不存在、超时、缺少系统依赖),挂起转人工。
)

// VerifyRule 单个包或模块的验证规则配置(读取自 .cursor/verify.yaml)。
type VerifyRule struct {
    Commands   []string // 待顺序执行的 shell 验证命令序列(如 ["go test ./...", "pnpm test"])。
    TimeoutSec int      // 单次验证执行超时时间(秒,防止挂起死锁,默认 120s)。
    MaxRounds  int      // 允许模型最大打回重试轮次(超出则触发熔断转人工,默认 3 次)。
    ScopeGlob  string   // 关联的文件 glob 匹配规则,用于根据 diff 改动文件做定向按需测试。
}

// VerifyResult 验证流水线执行输出与状态分析报告。
type VerifyResult struct {
    Status        VerifyStatus // 验证判定结论:passed / failed / cannot_run。
    FailedCommand string       // 导致失败的首个命令文本(全通时为空)。
    ExitCode      int          // 失败命令的系统进程退出码(0 表示正常退出)。
    Output        string       // 裁剪后的关键报错日志输出(过滤冗余输出,保留关键堆栈)。
    Fingerprint   string       // 报错特征指纹哈希(基于失败文件与报错信息计算,用于识别死循环)。
    Round         int          // 当前验证所处的打回轮次序号(1-based)。
    DurationMs    int64        // 验证执行耗时(毫秒)。
}

// CheckWorkspace 根据本次任务改动的文件定向执行工程级验证。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
//   - diffFiles: 本次任务已变更的文件相对路径列表(用于缩小测试执行范围)
//   - round: 当前重试轮次序号
//   - lastFingerprint: 上一次失败记录的错误特征指纹(用于防死循环比对)
// 返回:
//   - result: 验证执行结果(状态、日志与特征指纹)
//   - err: 规则文件解析失败等系统故障
func CheckWorkspace(workspaceRoot string, diffFiles []string, round int, lastFingerprint string) (result VerifyResult, err error)

// IsLooping 判断验证失败是否已陷入原地空转或超出最大容忍轮次。
// 参数:
//   - currentFingerprint: 本轮失败提取出的错误指纹哈希
//   - lastFingerprint: 上一轮失败记录的错误指纹哈希
//   - round: 当前处于第几轮打回重试
//   - maxRounds: 配置的最大允许重试轮次上限
// 返回:
//   - true 表示连续两次指纹相同(原地打转)或超出最大轮次,需触发系统熔断转人工
func IsLooping(currentFingerprint string, lastFingerprint string, round int, maxRounds int) bool

旁路复审(Evaluator)

管接收瘦身后的改动 diff、验收条目状态及验证摘要,以无工具纯净上下文进行挑剔审查,重点排查偷删测试与虚假通过;输出结构化决议与驳回清单;维护 diff 指纹防止重复开销。不修改代码,不执行命令。

// EvaluationVerdict 独立旁路复审裁决枚举。
type EvaluationVerdict string

const (
    VerdictPass      EvaluationVerdict = "pass"       // 复审满意,全部合格,准予收工。
    VerdictNeedsWork EvaluationVerdict = "needs_work" // 发现代码或测试瑕疵,打回主模型继续修复。
    VerdictEscalate  EvaluationVerdict = "escalate"   // 复审无法形成一致意见、解析失败或模型异常,转人工裁决。
)

// IssueCategory 复审缺陷分类枚举。
type IssueCategory string

const (
    IssueDeletedTest    IssueCategory = "deleted_test"    // 违规删除既有测试用例。
    IssueWeakenedAssert IssueCategory = "weakened_assert" // 弱化或注释掉关键测试断言。
    IssueFalseClaim     IssueCategory = "false_claim"     // 验收清单宣称通过但证据不符或无事实依据。
    IssueOutOfScope     IssueCategory = "out_of_scope"    // 代码修改范围严重越界,超出需求范畴。
    IssueLogicDefect    IssueCategory = "logic_defect"    // 明显的业务边界漏洞或潜在空指针/泄漏。
)

// EvaluationIssue 审查发现的具体缺陷明细。
type EvaluationIssue struct {
    FilePath     string        // 存在缺陷的源码文件路径。
    Line         int           // 缺陷所在的起始行号。
    Category     IssueCategory // 缺陷类型枚举。
    Reason       string        // 驳回说明:客观指出何处违规或不符。
    SuggestedFix string        // 给模型的具体修复整改建议。
}

// EvaluationResult 独立旁路复审输出的完整决策报告。
type EvaluationResult struct {
    Verdict         EvaluationVerdict // 综合裁定:pass / needs_work / escalate。
    Issues          []EvaluationIssue // 结构化驳回问题列表(needs_work 时必填)。
    Summary         string            // 审查总体意见说明(注入上下文或展示给用户)。
    DiffFingerprint string            // 本次送审 diff 的内容哈希(用于未修改代码直接再申请时的缓存跳过)。
    TokensUsed      int               // 独立复审调用所消耗的 Token 数量(独立记账)。
}

// SanitizeDiff 清洗与压缩用于复审的代码变更 diff。
// 参数:
//   - rawDiff: git 产生的原始完整 diff 文本
//   - maxBytes: 允许送审的最大字节上限(默认 60KB,超出则截断或分片)
// 返回:
//   - sanitizedDiff: 过滤掉 lock 文件、生成代码与纯空白变动后的干净 diff
//   - err: 过滤异常或 diff 严重超限导致的错误
func SanitizeDiff(rawDiff string, maxBytes int) (sanitizedDiff string, err error)

// EvaluateRun 对照任务改动与验收契约发起客观旁路审查。
// 参数:
//   - ctx: 复审执行上下文(含超时控制)
//   - sanitizedDiff: 清洗后的代码变更 diff
//   - items: 计划验收清单及各自绑定的客观证据
//   - verifySummary: 收尾验证通过的日志摘要事实
//   - lastFingerprint: 上一次复审的 diff 指纹(若代码未改动则直接复用上次裁决,不发模型请求)
// 返回:
//   - result: 结构化复审裁定(通过、需要修改或人工升级)
//   - err: 复审模型调用故障
func EvaluateRun(ctx context.Context, sanitizedDiff string, items []PlanItem, verifySummary string, lastFingerprint string) (result EvaluationResult, err error)

探索子代理(Explorer)

管调度轻量模型与只读工具组合翻阅代码、搜索关键词,提取带引用的结构化短结论回传主上下文。不修改任何文件,不执行任何写命令,不污染主会话上下文。

// ExploreInput 只读探索子任务的输入参数。
type ExploreInput struct {
    Query           string   // 探索目标(如 "查找 RunCoordinator 的状态恢复逻辑" 或具体符号)。
    PathHints       []string // 限定搜索的文件或目录路径模式(如 ["server/internal/agent/*.go"],可选)。
    MaxBudgetTokens int      // 本次探索允许消耗的最大 Token 预算上限(默认 10,000 tokens,防止无节制展开)。
}

// Citation 探索子代理提炼出的精准代码引用。
type Citation struct {
    FilePath  string // 引用代码所在的文件相对路径。
    StartLine int    // 引用代码片段的起始行号。
    EndLine   int    // 引用代码片段的结束行号。
    Snippet   string // 精确的代码原文片段,供主模型直接引用定位与替换。
}

// ExploreOutput 探索子代理汇总提炼后的结构化结果。
type ExploreOutput struct {
    Summary    string     // 针对探索目标提炼的高密度短结论(直接解答问题,去除冗余原文)。
    Citations  []Citation // 支撑结论的精准行号引用列表。
    TokensUsed int        // 子代理在整个探索过程中消耗的 Token 总量(单独上报记账)。
    Completed  bool       // 是否在预算内完整完成探索(false 表示由于达到预算上限提前截断)。
}

// Explore 调度轻量只读子代理深入项目探索代码事实。
// 参数:
//   - ctx: 探索执行上下文(支持超时与用户取消)
//   - input: 包含探索目标、路径提示与预算上限的输入配置
// 返回:
//   - output: 包含高密度摘要与精准引用的结构化结论
//   - err: 执行子代理探索时的系统异常
func Explore(ctx context.Context, input ExploreInput) (output ExploreOutput, err error)

快照回滚(Snapshot)

管在首个写操作前捕获基于 Git 的轻量暂存快照并记录未跟踪文件清单,提供代码恢复、对话截断、全量回滚三种粒度的恢复操作。不管任务为何失败,不决定何时回滚,不污染正常提交分支。

// RestoreMode 快照回滚的安全粒度模式枚举。
type RestoreMode string

const (
    RestoreFiles    RestoreMode = "restore_files"    // 仅撤销工作区代码修改并清理新增文件,保留后续对话供重新尝试。
    RestoreMessages RestoreMode = "restore_messages" // 仅截断失败消息与工具记录,代码变更保持不变以便继续调试。
    RestoreAll      RestoreMode = "restore_all"      // 全量回滚:代码还原至任务开始前,对话截断至任务起点。
)

// WorkspaceSnapshot 任务开始前的工作区环境轻量快照。
type WorkspaceSnapshot struct {
    SnapshotOID    string   // git stash create 产出的轻量暂存提交 SHA(悬空提交,不产生提交历史记录)。
    UntrackedFiles []string // 快照生成时工作区内已存在的未跟踪文件路径清单。
    RunID          string   // 本次快照绑定的 Agent Run 任务唯一标识。
    CreatedAt      int64    // 快照捕获时刻的 Unix 毫秒时间戳。
}

// Take 在首次产生破坏性写操作(修改文件、执行命令)前捕获工作区干净快照。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
//   - runID: 当前任务 Run ID
// 返回:
//   - snap: 记录快照提交 SHA 与未跟踪文件清单的快照对象
//   - err: git stash 执行失败异常
func Take(workspaceRoot string, runID string) (snap WorkspaceSnapshot, err error)

// Restore 依据快照与选定模式将工作区或会话安全复原。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
//   - snap: 之前捕获的工作区快照数据(包含 SnapshotOID 与初始未跟踪文件)
//   - mode: 回滚粒度模式(restore_files / restore_messages / restore_all)
// 返回:
//   - err: 工作区重置或清理文件失败的异常
func Restore(workspaceRoot string, snap WorkspaceSnapshot, mode RestoreMode) error

架构与文档断言(ArchChecker)

管遍历模块依赖关系与分层约束,检查代码事实与架构文档的双向一致性,违规时产出包含问题、改法和出处的三段式规范诊断。不改动源码,不参与状态机运行时流转。

// ArchViolation 单条架构规则或文档一致性违规诊断信息。
type ArchViolation struct {
    SourceFile string // 触发违规的源文件路径(如 "server/pkg/agent/foo.go")。
    TargetPkg  string // 被违规引用的目标包路径(如 "server/internal/events")。
    RuleName   string // 触发的规则唯一标识(如 "no-downward-dep" 或 "docsync-missing-tool")。
    Problem    string // 标准三段式诊断之「问题说明」:直白阐述代码破坏了哪项规则。
    Fix        string // 标准三段式诊断之「修复指引」:清晰告知模型应该如何重构以符合规范。
    DocRef     string // 标准三段式诊断之「参考章节」:标注对应的架构文档或规范路径(如 "docs/architecture.md")。
}

// CheckDependencies 执行架构依赖单向流转与分层边界静态检查。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
// 返回:
//   - violations: 违背依赖规则的违规项列表(包含三段式修复指导)
//   - err: 解析依赖包列表本身的系统错误
func CheckDependencies(workspaceRoot string) (violations []ArchViolation, err error)

// CheckDocSync 执行代码事实与架构设计文档的双向同步断言。
// 参数:
//   - workspaceRoot: 工作区根目录绝对路径
// 返回:
//   - violations: 代码与文档不一致的违规项列表(如代码新增工具但文档未记录,或文档记录了已废弃目录)
//   - err: 文档或代码符号扫描异常
func CheckDocSync(workspaceRoot string) (violations []ArchViolation, err error)

流程

用户发起编码需求,Agent 在探索子代理辅助下规划并执行,经由写时守卫、命令验证与旁路复审三重门禁完成收尾;异常时一键安全回退。

// 规划阶段:校验计划与测试验收条目
PlanContract.ValidatePlan(...)

// 编码准备:只读子代理先行探索代码,提炼带引用事实
Explorer.Explore(...)

// 编码阶段:首次写前静默拍快照;动已有测试强制人工审批;修改文件时即时守卫
Snapshot.Take(...)
PlanContract.IsExistingTestFile(...) // 若是已有测试,走人工审批
EditGuard.InspectEdit(...)

// 模型自称完成:Brain 拦截收工,触发工作区定向验证
Verifier.CheckWorkspace(...)

// 验证全通:清洗瘦身 diff,触发独立无工具模型复审
Evaluator.SanitizeDiff(...)
Evaluator.EvaluateRun(...)

// 复审通过:核验新测试在基线上为红,受控标记验收条目并收尾
PlanContract.VerifyTestFailsOnBase(...)
PlanContract.MarkPassed(...)

// 若验证报错、复审驳回或人工判定放弃:一键回退
Snapshot.Restore(...)

实施清单与验收标准(开发驱动)

本 issue 分为 7 个迭代阶段,各阶段均可独立提交 PR 并通过测试验收:

阶段一:写代码实时守卫(EditGuard)

  • 在 internal/agent/tools/register.go 的 Ports 中增加代码检查接口 Lint(filePath, content)。
  • 在 edit.go 与 write.go 中,在写盘操作后接入语法检查逻辑。
  • 实现新旧错误对比逻辑,确保仅拦截本次增量错误;检测到新增错误时自动回退原文件并返回改动对比。
  • 验收:假模型故意写出缺少大括号的 Go 文件,断言文件内容回滚保持不变,工具调用返回失败。

阶段二:状态机与两线骨架升级(State Machine & Runtime)

  • 在 server/pkg/agent/plane.go 中新增 Phase(verify_result, evaluate_result, human_override)与 Instruction(verify, evaluate)。
  • 在 server/pkg/agent/types.go 中新增 RunStatus(verifying, evaluating)及事件类型。
  • 在 AgentState 中扩展 HadSideEffects, SnapshotID, VerifyRound, LastVerifyFingerprint, EvaluateRound 字段。
  • 修改 Brain.Decide:在 PhaseLLMResult 且无 pending 工具时,根据 HadSideEffects 分叉至 verify 或 finish。
  • 更新 CanTransition 允许迁移至新状态;扩展 recoverPhase 确保重启后能恢复断点。
  • 验收:单元测试模拟只读会话直接 finish,有副作用会话正确流转至 verifying 指令。

阶段三:收尾命令验证与防死循环(Verifier)

  • 在 server/pkg/agent/engine.go 中实现 runVerify 执行器,读取工作区 .cursor/verify.yaml。
  • 复用既有 pkg/git 模块,根据改动文件列表支持定向执行相关包测试。
  • 实现失败输出指纹提取与判定,连续两次相同指纹或超限(默认 3 次)自动挂起为 waiting_approval。
  • 扩展前端时间线 reducer,支持展示 verify.started 与 verify.result 卡片。
  • 验收:测试首次失败能自动喂回模型重修,连续两次相同错误时系统熔断转人工。

阶段四:Git 原生快照与三档回滚(Snapshot)

  • 在 server/pkg/git 中增加 CaptureUntracked,并在 RestoreWork 恢复流程中补全多余未跟踪文件的清理机制。
  • 扩展数据库迁移:在 runs 表增加 snapshot_oid 字段,并在首次产生写副作用前捕获快照。
  • 新增 HTTP 端点 POST /runs/{id}/restore,支持 files / messages / all 三种粒度回退。
  • 在前端审批与转人工挂起面板上,集成“回滚到本次任务修改前”操作按钮。
  • 验收:创建测试 Git 仓库,修改并新建文件后调用 restore 端点,断言工作区完全回到改动前干净状态。

阶段五:独立旁路复审与 Token 极限压缩(Evaluator)

  • 在 server/pkg/agent/evaluate.go 中实现无状态的独立复审函数,支持独立配置 SubagentModel。
  • 实现 diff 过滤器,剔除 lock 文件、构建产物与纯空格改动,设定 60KB 保护上限。
  • 复审模型输入仅包含清洗后的 diff、验收清单与验证摘要,输出强制校验为结构化 JSON。
  • 支持 diff 指纹缓存:若模型未修改代码直接再次申请收工,复用上次驳回意见,不额外消耗模型调用。
  • 验收:复审模型指出缺陷时系统正确打回模型修改;复审超时或解析失败时 fail-close 转交人工。

阶段六:计划验收清单与测试纪律约束(PlanContract)

  • 在 server/internal/agent/tools/plan.go 中实现 frontmatter 验收清单的强校验,条目只增不删。
  • 新增受控工具 plan_pass,模型必须提供工具调用 ID 或验证记录作为 evidence 才能翻转状态。
  • 在工具审批流水线中拦截针对既有 *_test.go 和 *.test.ts 的写入操作,强制转入人工审批。
  • 实现新测试防作弊验证:在基线提交上执行新测试,断言新测试在旧代码上必须为红。
  • 验收:无验收清单计划被拒绝保存;模型未给证据调用 plan_pass 被拦截;修改已有测试触发人工审批单。

阶段七:探索子代理与架构/文档同步断言(Explorer & Arch/DocSync)

  • 在 server/internal/agent/tools 中新增 explore 工具,由轻量小模型执行只读探索并输出带引用的结论。
  • 限制子代理调用预算与深度,将其 Token 用量单独上报记账,并在时间线中呈现折叠卡片。
  • 新建 server/internal/archtest/deps_test.go,用 go list 刚性断言架构文档中的依赖单向流转规则。
  • 新建 server/internal/archtest/docsync_test.go,断言核心代码目录、工具和事件在架构文档中双向存在。
  • 将架构与文档测试脚本整合进默认验证配置 .cursor/verify.yaml。
  • 验收:在底层包违规引入上层依赖时架构测试报错并输出三段式指导;文档与代码不一致时测试挂掉。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmoduleSingle-module objects, interfaces, and design

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions