@@ -1,323 +0,0 @@
# N1 / Q1 / I1 实施方案:内核 harness、图执行、命令化识别
状态:**proposed( 2026-10-07) **; owner: weichen;主任务:Dev 仓 `feature/agent-stack-n1q1i1-plans` 。
上游:[2026-10-02-agent-stack-completion.md ](2026-10-02-agent-stack-completion.md ) §5 分期(下称「总提案」,行号以该文件为准);审计:[2026-10-02-agent-stack-audit.md ](../records/2026-10-02-agent-stack-audit.md )。
规范依据:Development-Guidelines( workflows/delivery.md、standards/collaboration.md、standards/agent-execution.md、standards/verification.md);本文是项目级消费,不复制其正文。
本文把总提案的 N1、Q1、I1 三个阶段落成可启动的实施方案:现状基线、工作包分解、文件级落点、测试与验收、风险。本文不实施代码;三个阶段的实现各走各的任务(总提案「每阶段单独任务」)。
---
## 1. 共同基线与三任务协调
### 1.1 现状基线(撰写日实测)
| 仓 | pin | 说明 |
|---|---|---|
| Agent-Native | `3a110a728cf520cfbbbafc0c4c75f14bf78bec3f` | durable-rewind-3 与 permission-hooks( PR #173 )之后 |
| Agent-Squad | `5a57636cc0b8426a86a72bbbdd5b7f30e4fcac88` | 同上 |
| Agent-Intent | `65aaebca1ea69ab7d48eda51d33ef1d4eea5a0bf` | agent-stack-d1( R2)之后 |
| Backend | `e7fb276ee9b3af51329a0c6df27517184b26edee` | v0.1.0-4 |
| Core | `0b5c7747a3165bf0d0f2219204b7b70ec3ed7645` | v0.1.0 |
两个要点:
- **审计基线已旧**。审计写于 native `c6d867c` / squad `590fd86` ;R1/R2 之后多条审计项在当前 pin 已修(native 侧 N1/N7/N8/N21/N22/N30/X4, squad 侧 Q1– Q5/Q7/Q14– Q16/Q18/Q19)。本文逐节标注「已修 / 仍开放」,实施时不得以审计原文为唯一依据,须以当前代码为准。
- **D1 的借力点已成**:三库核心互不依赖(native `go.mod` 只依赖 error-code-system 与 logging-system)、Options 已按端口拆分、跨库类型只出现在 adapters 子模块。N1/Q1/I1 都假设这个地基,不再返工。
### 1.2 分支与合入协调
按 AGENTS.md「同批互不依赖的子任务用同一 R0 的兄弟分支」:
- 三个任务分支:`feature/agent-stack-n1` 、`feature/agent-stack-q1` 、`feature/agent-stack-i1` ,从同一权威 `origin/main` (同一 R0)切出,互不嵌套。
- 每个任务的 PR 链固定为三段、按序合入:**库仓 PR → Backend 仓 PR( bump 嵌套 pin + 适配)→ Dev 仓 gitlink PR( bump 顶层 pin 与 modules/backend) **。R0 已落地的检查脚本核对 Backend `deps/` 与 Dev 指针一致,不允许只推一层。
- 三任务并行时的冲突面:N1 与 I1 都动 Backend( N1 在 `internal/app` 、`internal/runtime` 、`internal/runmgr` ; I1 在 `internal/server` 、`internal/intentgate` 、`internal/runmgr` ),`internal/runmgr` 是唯一重叠文件,**后合入者按 `make task-sync` 并入最新 main 后解决**; Dev 仓 gitlink pin 同理,后到者 sync 后重指。
- Q1 若涉及 `modules/backend/internal/squadadapter` 消费方适配,改动限制在「驱动循环跟上新引擎 API」范围内;编排收敛(WorkflowDef 编译、internal/workflow 退役)是 Q3,不在 Q1。
### 1.3 共同验收纪律
- 每阶段都带 E 评测(总提案 §5 E 节):Native 的对照用脚本化模型输出,不把模型自报当通过。
- 证据按 AGENTS.md「证据与验证门禁」:完成声明的 precheck / task-sync / 测试三段输出由 `make evidence` 写入任务卡;测试通过的权威源是 CI(PR HEAD 上 `make test` +`make build` ),本地输出是 SELF 级辅助线索。
- 各阶段验收条款在本文 §2.6 / §3.6 / §4.6 给出机器可核对的形式,与总提案对应条目逐条映射。
---
## 2. N1 内核 harness
总提案原文(§5 N1):「AgentSpec、HookBus、PermissionPolicy、三类 guardrail。CompletionGate 与提醒从 Backend 下沉。`adapters/native` 透传钩子。Backend 改为消费,A/B 对照直连行为。」
### 2.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| AgentSpec | **不存在 ** (全库无此类型)。最接近的是 `deps/agent-native/runtime/subagent/subagent.go:21-33` 的委派 `Request` ( Goal/Inputs/WriteScope/NonGoals/DoneCriteria/Deliverables + Deadline/MaxToolCalls);backend 子代理画像是 `prompts.SubagentSpec` + `fallbackSubagentSpec` ( `modules/backend/internal/runtime/sub_loop.go:18-27` ) | 类型、文件格式、插件校验入口全缺 |
| HookBus | 已有:`runtime/hooks/hooks.go:56-107` ( On/Emit,决策优先级 block/deny>ask>modify>allow)。事件枚举 hooks.go:9-20 有 SessionStart/UserPromptSubmit/PreToolUse/PermissionRequest/PostToolUse/PostToolUseFailure/Stop/SubagentStart/SubagentStop/PreCompact。**实际发射点只有 3 处**:`runtime/loop.go:512` ( Stop)、`:532` ( PreToolUse)、`:557` ( PostToolUse/Failure) | 缺 PostToolBatch、SessionEnd 两个事件;6 个已枚举事件无发射点;无「已在续跑中」防递归标志(hooks.go:33-43);**Ask 决策被当拒绝处理**( loop.go:534-540),无 ask→宿主审批→放行回路 |
| PermissionPolicy | 以函数式存在:`runtime/policy/policy.go` 三档 Mode( :9-13)、Rule( :30-34)、Decide( :55-93, deny>计划禁写>本会话放行>ask>auto_edit>allow)、TraitsFor( :113-128)、ClassifyCommand( :145-158)、Visible( :96-101)。backend 直接消费(`internal/runmgr/runmgr.go:681-685` ) | 无 Policy 命名类型、无规则序列化形态;会话放行记忆在 backend 内存 map( runmgr.go:265-286),未下沉 |
| 三类 guardrail | **不存在 ** (输入/输出/工具三层全无,grep 无命中);内核只有 `RepeatGuard` 布尔(loop.go:93-95) | 三层端口与执行点全缺 |
| CompletionGate 下沉 | 在 backend 直连路径三层串联:`deliverableGate` ( `internal/runtime/llm.go:477-510` ,上限 `maxDeliverableGuards=2` )→ `checkDeliverables` ( `runtime/verify.go:48-84` ,工件存在+非空+`quality.ScoreBytes` )→ `runVerifier` ( verify.go:86-122,开关 `runtime.verify` ,提示词 `aux.verify.final` )。经自建 hooks 总线挂一个 Stop 处理器注册进内核(`internal/runtime/llm_direct.go:162-172` ) | 依赖 `core.ArtifactRef` 、`rc.Artifact` 、`quality.ScoreBytes` 、提示词库——未端口化;**子代理(sub_loop.go:98-112 无 Stop/Hooks)与小队步骤(app.go:202-217 未注入 Stop)都无此门禁**;小队只有事后 `claimsFalseDeliverable` 判失败(`runtime/squad_runner.go:90-92` ) |
| 提醒下沉 | backend 侧 `directRun.reminders` ( verify.go:223-250,连续 3 个工具轮未更新计划/待办则提醒,`reminderToolGap=3` 、`maxChecklistReminders=3` ,用库侧 `reminders.Bus` 节流去重)。内核有通用 `Reminders func(turn)` 回调(loop.go:90-92) | `rc.Todos()/Plan()` 与 `core.TodoList/PlanSnapshot` 是产品接口,未端口化;小队/子代理路径未注入 |
| adapters/native 透传 | **库侧管道已修 ** : `GateOptions` ( `adapters/native/backend.go:70-80` ,含 BeforeTool/AfterTool/Stop/Reminders/RepeatGuard/Hooks/Ask)、透传(:370-372)、`gateBus` ( :464-496);squad 侧映射(`deps/agent-squad/adapters/native/register.go:39-49,92-96` ) | **装配未接通(N31 剩余部分) ** : backend 组合根只注入 `RepeatGuard:true` 与 plan 模式 BeforeTool( `internal/app/app.go:207-217` );直连与子代理绕过 `exec.Backend` 直接调 `nruntime.Run` ( llm_direct.go:173、sub_loop.go:98) |
| Backend 改为消费 | `adapters/native.Backend` 只实现旧门面 `exec.Backend` ( backend.go:130/140),未实现 `ContractBackend` ; `conformance.Suite` ( conformance/fake.go:106,三条用例)无真实实现调用方 | ContractBackend 实现与 conformance 接入 |
### 2.2 工作包分解
**WP1 AgentSpec 与插件校验 ** (新类型,库侧 `runtime/subagent` 或新 `runtime/agentspec` 包)
- 定义 `AgentSpec{Name, Description, Prompt, Tools, DisallowedTools, Model, PermissionMode, Skills, MaxTurns, MemoryScope, Effort, Background, WorktreeIsolation, OutputSchema}` ,字段对齐总提案 §2.1 子代理行(Claude subagents frontmatter 集 + `outputSchema` )。
- 文件格式:Markdown frontmatter( `agent-*.md` ),加载器在库侧;backend 现有 `prompts.SubagentSpec` 迁移为读 AgentSpec 文件,`fallbackSubagentSpec` 保留为内置兜底。
- **校验红线**:插件清单里的 AgentSpec **不得声明 hooks、MCP、权限模式 ** (总提案 §2.1 权限行),校验在插件装载期 fail-closed。
- 测试:字段 round-trip、缺省值、插件声明 hooks/权限模式被拒、backend 旧画像到 AgentSpec 的等价映射。
**WP2 HookBus 补全与 ask 回路 **
- 事件枚举加 `PostToolBatch` 、`SessionEnd` (对齐总提案 §2.1 事件名单);`Call` 增加「已在续跑中」标志位,hook 触发续跑时下游可见、禁止递归触发 Stop 系事件。
- 发射点补齐:`SessionStart` ( Run 入口)、`UserPromptSubmit` (用户消息入列)、`PermissionRequest` ( gateTool ask 时)、`SubagentStart/SubagentStop` (委派创建/收口)、`PreCompact` ( compact 前)、`SessionEnd` ( Run 出口)、`PostToolBatch` (工具批结束)。
- **ask 回路(本阶段最关键的行为变化)**:`gateTool` 收到 ask 不再当拒绝(loop.go:534-540 现状),改为发 `PermissionRequest` 并等待宿主决策端口;宿主默认实现 = backend HITL 确认卡(runmgr.go:384-424 现有 AskHITL/waitHITL 机制接为端口实现)。`deny` 语义不变;`block` 不变。
- 风险与兜底:ask 从「拒绝」变「挂起审批」是行为变化;用 A/B 对照(WP6)证明「无 hook 注册时行为与现状逐字节一致」,把行为变化严格限制在「注册了审批端点」的场景。
- 测试:每个新发射点一例;ask→批准→执行、ask→拒绝→配对结果两条回路;续跑标志防递归;与既有 `ab_gate_test.go` 对照不回归。
**WP3 PermissionPolicy 命名类型与会话放行下沉 **
- 在 `runtime/policy` 增加 `Policy` 结构体:`{Mode, Rules []Rule, SessionAllow AllowStore}` , `Decide` 收为方法;`AllowStore` 是端口(Get/Add/List, `key = 工具+参数模式` ),库内给内存实现,backend 把 `sessionAllows` ( runmgr.go:265-286)替换为该端口的实现(本会话内存即可,持久化形态留给 N3 的 Memory/RunState)。
- `Visible` 、模式切换(含 `ExitPlanMode` , runmgr.go:257-263)改走 `Policy` 。
- 测试:规则优先级矩阵在结构体形态下全等复现现有 `policy_test.go` 四例 + backend `perm_test.go` ;会话放行跨调用存活。
**WP4 三类 guardrail 端口 **
- 端口定义:`InputGuardrail` (只作用于入口智能体的用户输入)、`OutputGuardrail` (只作用于给出最终结果的那个智能体)、`ToolGuardrail` (每次工具调用)。形态 = `func(ctx, payload) (allow / deny / modify + 原因)` ,与 HookBus 决策词汇一致。
- 执行点:输入在 UserPromptSubmit 处、输出在 Stop 判定前、工具在 PreToolUse 链内。guardrail 与 hook 的关系写清:guardrail 是类型化语义层,hook 是通用事件层,guardrail 以 hook 实现注册。
- 测试:三层各自 deny/modify/allow 各一例;只入口、只最终输出的作用域限定各一例。
**WP5 CompletionGate 与提醒下沉 **
- 下沉的不是实现而是**组合点**:库侧新增「输出 guardrail 注册位 + 提醒源注册位」,产品依赖端口化:
- 工件存在/非空/质量分:端口 `ArtifactVerifier` ( backend 用 `rc.Artifact` + `quality.ScoreBytes` 实现,verify.go:48-84 逻辑迁入 adapters 侧实现);
- 独立验证器:端口 `FinalVerifier` ( backend 用 `aux.verify.final` 提示词 + 独立模型调用实现,verify.go:86-122 迁入);
- 计划/待办提醒源:端口 `ProgressProbe` ( backend 用 `rc.Todos()/Plan()` 实现,verify.go:223-250 迁入);`maxDeliverableGuards=2` 、`maxVerifyContinuations=3` 、`reminderToolGap=3` 、`maxChecklistReminders=3` 等阈值随端口配置走,默认值不变。
- 迁移后 `deliverableGate` /`checkDeliverables` /`runVerifier` /`directRun.reminders` 在 backend 只剩端口实现与装配,判定逻辑(正则族、续跑上限、节流)进库。
- 测试:库内脚本化夹具复现 backend `llm_test.go` /`llm_verify_test.go` 的全部门禁用例(GuardTwiceThenNotice、验证器拒绝后通过、垃圾失败关闭、提醒节流等),确保下沉后逐条等价。
**WP6 Backend 装配接通与 A/B 对照(N31 收口) **
- `internal/app/app.go` :直连、子代理(sub_loop.go:98-112)、小队(squadadapter `AgentNativeOptions` , app.go:207-217)三条路径注入同一套 `Hooks/Stop/Reminders/Ask` ;小队经 squad→native 两级 GateOptions 透传(管道已在,只装配)。
- 直连与子代理改为经 `adapters/native` 消费(不再裸调 `nruntime.Run` );`adapters/native.Backend` 实现 `ContractBackend` (补 Descriptor),`conformance.Suite` 挂到 native 后端跑(现有三条用例起步)。
- **A/B 对照(提案验收核心)**:同一组脚本化模型输出(含停止、提醒、工具拒绝、HITL 分支),分别在「旧直连装配」与「新下沉装配」下跑,断言停止原因、提醒次数、工具拒绝序列一致。`runtime/ab_gate_test.go` 是库内雏形,本 WP 把它扩成 backend 侧对照。
- 小队路径行为变化明示:小队步骤首次获得 Stop 门禁与提醒——这是本阶段有意的行为拉齐,写进 PR 影响段。
### 2.3 涉及仓与落点汇总
- Agent-Native: `runtime/hooks` 、`runtime/policy` 、`runtime/loop.go` 、`runtime/subagent` (或新 agentspec 包)、新 `runtime/guardrails` (或并入 hooks)、`adapters/native` 、`conformance` 。
- Backend: `internal/app/app.go` 、`internal/runtime/{llm.go,llm_direct.go,verify.go,sub_loop.go,squad_runner.go}` 、`internal/runmgr/runmgr.go` ( AskHITL 接端口、sessionAllows 替换)、`internal/squadadapter` (透传配置)。
- Dev:两层 pin。
### 2.4 实施顺序
WP2( ask 回路)与 WP3(Policy 类型)先行(其余都依赖决策与权限语义)→ WP1 AgentSpec、WP4 guardrail 端口 → WP5 下沉 → WP6 装配与 A/B。每 WP 单独提交,库内测试绿了再进下一 WP。
### 2.5 风险
- ask 语义变化是最大风险点:必须有 A/B 对照证明默认行为不变(见 WP2/WP6)。
- 下沉搬移正则族与阈值时容易丢行为:WP5 的「逐条等价」测试先行(先在库内写好对现网行为的刻画,再搬)。
- 小队首次上门禁可能暴露存量提示词的虚假交付习惯:`claimsFalseDeliverable` 与 Stop 门禁并存期要观察误报率,必要时给小队入口单独的宽松阈值(配置项,不硬编码)。
### 2.6 N1 验收(对照总提案 §5 N1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 同一组脚本化模型输出,下沉前后停止原因、提醒次数、工具拒绝一致 | backend 侧 A/B 对照测试在 CI 通过;对照夹具覆盖 deliverableGate 命中/未命中、verifier 拒绝/通过/垃圾、提醒触发/节流、工具拒绝 |
| 插件里的 AgentSpec 不能带钩子和权限模式 | 插件装载测试:声明 hooks/permissionMode 的 AgentSpec 被拒且错误可定位到字段 |
## 3. Q1 图执行
总提案原文(§5 Q1):「superstep、三档检查点、attempt id、interrupt / resume。`Needs` 驱动依赖。读写集。可组合 Termination。Store 接口。platform 的 SQLite 成为 Store 的一个实现,不再单独驱动一套运行。」
### 3.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| superstep | 无。执行是「线性步骤数组 + 状态机扫描」:`flowDriver.advance` ( `deps/agent-squad/squad/workflow.go:22-101` )一个大循环扫到挂起/终态;leader 是独立大循环(leader.go:178-372);`WorkflowPlan.CurrentStepIndex` ( models.go:124)是从未写入的死字段 | 整个推进模型 |
| 三档检查点 | 无 checkpoint 类型。最接近的是「步骤完成即 `st.save()` 全量落盘」(workflow.go:367-371,同步单档)+ 挂起时 pending.json。原子写已修(`AtomicWrite` , workspace.go:307-349) | 检查点类型与退出时/异步/同步三档全缺 |
| attempt id | 只有计数器 `Attempts` ( models.go:93-94)与不变的 `ExecutionID = runID+"-"+stepID` ( workflow.go:286);恢复靠「running + Attempts>=2 + HITL 模式」启发式(engine.go:879-925),无产物校验 | attempt id、幂等键、「副作用次数=1」的判定基础 |
| interrupt/resume 同一对原语 | 挂起原语面向人:`hitlGate` /pending.json + `Respond` /`Resume` ( engine.go:481,928);事件唤醒是旁路 `WaitCondition` + `SweepWakeups` ( engine.go:1080-1163);`SweepExpired` ( engine.go:1107-1130) **无调用方,是死代码** | 统一原语;SweepExpired 接线 |
| Needs 驱动依赖 | 字段已声明(models.go:90)但**调度完全不读**(全仓唯一引用即声明行);调度只看「相邻 Parallel 标记」(workflow.go:44-72) | DAG、拓扑排序全缺 |
| 读写集 | 无 Reads/Writes 字段;「写同一工件必须串行」无判定;并行安全靠 `st.mu` 串行化写回 | 字段与调度判定全缺 |
| 可组合 Termination | 无该类型。四个停止条件硬编码分散:leader 轮次(leader.go:207/371,达上限直接 leaderFail 无降级,Q23 仍开放)、修复预算(engine.go:36-37 默认 1, workflow.go:583-596 扣减,engine.go:698 人工 retry 加预算无上限,Q26 仍开放)、唤醒上限(hitl.go:68 `MaxWakeupFires=5` )、StepTimeout/HITL 超时(engine.go:32-33、hitl.go:23-30、hitl.go:119-137) | 类型与 AND/OR 组合全缺 |
| Store 接口 | squad 核心无 Store 端口;运行状态由引擎直接管文件(runstate.go:52-76 一次写六文件);`store.FileStore` 只管 roles/blueprints; `store/eventsink.go` 是事件 jsonl 装饰器 | 接口不存在 |
| platform 收编 | `adapters/sqlite` (包名 platform)是**独立第二套运行系统**: 23 张表无 run/step/checkpoint 表(db.go:51-149);task 队列自有一套 Claim/FinishTask/RecoverOrphans( queue.go:194-348);`RunInProcess` 绕过 Executor 端口直驱 agent-native( runtime.go:201-229, X6 仍开放);与 squad 文件态 runs 双真相源并列挂载(cmd/agent-squad/server/server.go:31-41,94-96) | 双系统合一 + schema 扩展 + X6 |
| 顺带瑕疵(Q1 会踩到) | 并行组 `wg.Wait()` 等全部结束才收口(workflow.go:172);黑板是快照不是增量(blackboard.go:12-38);动态调整直接 splice Steps( workflow.go:510-534)——与「图结构不变」的恢复要求冲突;产品侧消费方是 squadadapter 的外层 Run/Resume 循环(`modules/backend/internal/squadadapter/adapter.go:92-117` 、entry.go:115-139) | 在设计中显式裁定,见 §3.2 WP1/WP4 |
已修不再返工(R1/R2 已落):原子写、授权/预算落盘、批准键写入(Q1)、中断步骤挂起分流(Q4 部分)、跨进程锁(Q5)、Respond 幂等(Q7)、WAL( Q19)、事件 seq 续号(Q18)。
### 3.2 工作包分解
**WP1 图模型与 Needs 调度 **
- `StepSpec` 增补 `Writes []string` (写集,工件路径)与 `Reads []string` (读集,可空=不声明);`Needs` 从死字段激活为依赖声明。
- 编译期(`planAndExecute` 入口)把 Steps 编译成图:节点=步骤,边=Needs;拓扑分层(Kahn),同层可并行。**兼容映射**:无 Needs 的旧蓝图按「相邻 Parallel」规则自动分层(相邻且 Parallel=true 的步骤入同层),行为不变量由现有测试保证。
- **动态调整裁定**(与恢复语义冲突的解决):`AllowDynamicAdjustment` 步骤的 splice( workflow.go:510-534)改为「追加节点生成新图版本」——图带 `version` ,已完成的 superstep 结果按节点 id+图版本键保存;恢复时要求图版本与检查点一致,不一致则拒绝恢复并提示重新规划(对齐总提案 §2.2「恢复要求图结构不变」)。
- 调度隔离:同层内**写同一工件(Writes 交集非空)的步骤强制串行**;只读步骤并行。交集判定在分层时做,拒绝或串行化在调度时做(默认串行化 + 事件提示,不拒绝)。
- 测试:Needs 分层顺序、环检测拒绝、旧蓝图兼容映射等价、写集交集串行化、图版本不一致拒绝恢复。
**WP2 superstep 推进 **
- `advance` 重写为 superstep 循环:每 superstep = 「取当前可执行层 → 层内调度(写集串行)→ 层结束做检查点」。leader 循环映射为「leader 节点 + 动态追加成员节点」的图形态(委派=追加,close=终止边),leader.json 的状态并入检查点。
- `CurrentStepIndex` 死字段删除或改作「当前 superstep 序号」。
- 并行组 fail-fast 裁定:保持 cancel 传播(现状),superstep 边界统一收口失败,不再 `wg.Wait()` 空等——失败即终止本 superstep 剩余节点。
- 测试:多蓝图(standard/router/leader)在新循环下现有 engine_test/leader_test 全绿;层边界事件序列断言。
**WP3 attempt id 与幂等 **
- 每次执行生成 `AttemptID = runID/stepID/N` ( N=Attempts+1),随步骤状态落盘;执行事件、产物、副作用记录都挂 AttemptID。
- 恢复判定升级:`recoverInterrupted` ( engine.go:879-925)从启发式改为「attempt 记录 + 产物校验」——步骤标 running 且其 attempt 无完成记录时,按 HITL 模式分流(现状三分支保留),选择 retry 时新 attempt 执行前校验该步骤已登记副作用(产物文件存在性)并在事件流标注「第 N 次尝试」。
- 验收硬指标「副作用次数=1」的判定基础:已完成(有完成记录)的 attempt 永不重跑;瞬态重试(MaxTransientStepInvocations=2, workflow.go:260)共享同一 AttemptID 的「子尝试」标记,不计为新 attempt。
- 测试:杀进程于每个 superstep 边界(见 §3.6);retry/skip/abort 三分支;瞬态重试不增 AttemptID。
**WP4 Store 端口与三档检查点 **
- 新端口 `Store` ( squad 包内):`SaveRun/LoadRun/SaveStep/LoadStep/SaveAttempt/AppendEvent/LoadEvents/SaveCheckpoint/LoadCheckpoint/ListRuns` ,以 run( thread)为键。
- 现状文件态收编为 `FileStore` ( runs/ 目录六文件 + pending.json + leader.json 的全部读写走 Store 接口);检查点三档:`sync` (superstep 边界同步写,默认)、`async` (批量刷盘)、`on_exit` (进程退出时)——档位在 RunOptions 声明,默认 sync。
- 黑板裁定:Q1 只把黑板快照纳入检查点载荷;「可回放增量」留给 Q2 拓扑(总提案 §2.2 把黑板增量列在拓扑行)。
- 测试:三档各自的一致性;检查点以 run 为键的隔离;FileStore 与现状文件布局的读取兼容(老 runs 目录可恢复)。
**WP5 SQLiteStore 与 platform 收编 **
- `adapters/sqlite` schema 增加 `run / step / attempt / checkpoint / event` 五表;`SQLiteStore` 实现 Store 端口。
- **收编裁定**(双真相源合一):platform 的 task 队列不再自行驱动执行——`Claim` 到的任务翻译为 squad run 并交给引擎(Store=SQLiteStore),`RunInProcess` 改走 `Executor` 端口(修 X6);platform 的 wakeup 表与 squad 的 WaitCondition 合并为一处(保留 squad 语义,platform 表改为投影或下线,迁移期双写只读旧表)。
- 过渡:platform 现有 23 表中非运行类(workspace/member/issue/skill/autopilot/webhook/chat/project 等)不动;本 WP 只合一「运行」这一层。
- 测试:同一蓝图分别在 FileStore 与 SQLiteStore 上跑通 conformance(见 §3.6);孤儿恢复(RecoverOrphans)在合并后语义不变;server 并列挂载改为单引擎。
**WP6 可组合 Termination **
- 新类型 `Termination = And(...)|Or(...)|MaxTurns(n)|RepairBudget(n)|MaxWakeups(n)|Timeout(d)|StepTimeout(d)` ;四处硬编码收编为默认组合:`And(MaxLeaderTurns(8), RepairBudget(1), MaxWakeups(5))` + 步骤级 StepTimeout(默认 10min,语义不变:重试共享截止,Q24 维持现状并写进文档)。
- 顺手关账:leader 达上限的降级收口(Q23:不再裸 leaderFail,走一次收口质检或挂起人工);人工 retry 加预算设上限(Q26:默认累计不超过初始值的 2 倍,可配);`SweepExpired` 接入 serve 周期循环(与 SweepWakeups 并列),补 Q6 另一半。
- 测试:AND/OR 组合矩阵;三个关账项各一例回归。
**WP7 interrupt/resume 统一原语 **
- 把「HITL 挂起(pending.json)」与「事件等待(WaitCondition/SweepWakeups)」统一为 `Interrupt{Kind: human|event|expired, Key, Payload}` / `Resume(Key, Response)` 一对原语;pending.json 与 wakeup 成为 interrupt 的两种持久化形态。
- 对外 API( Respond/Resume/steer)语义不变,内部改走统一原语。
- 测试:两种 interrupt 的挂起/恢复/过期三态;API 兼容(现有 server_test、engine_test 绿)。
### 3.3 涉及仓与落点汇总
- Agent-Squad: `squad/{workflow.go,engine.go,leader.go,models.go,runstate.go,blackboard.go,hitl.go}` (重写面大)、新 `squad/{graph.go,superstep.go,store.go,termination.go,interrupt.go}` 、`adapters/sqlite` ( schema+Store)、`store/` ( FileStore 归位)、`cmd/agent-squad/server` (挂载合一、SweepExpired 接线)。
- Backend: `internal/squadadapter` (驱动循环跟上引擎 API;入口/步进循环适配)。
- Dev:两层 pin。
### 3.4 实施顺序
WP1 图模型 → WP2 superstep(旧行为兼容先绿)→ WP3 attempt id → WP4 Store+检查点 → WP7 统一原语 → WP6 Termination → WP5 SQLite 收编排最后(风险最高,前面 WP 已各自可独立交付)。
### 3.5 风险
- **advance 重写是心脏手术**:WP2 必须先建立「旧蓝图兼容等价」的测试网(现有 engine/leader/recovery/r2_regression 套件就是网底),重写中任何一绿变红即停。
- 动态调整与图版本:产品端目前依赖动态调整的蓝图(如有)在恢复时会被要求重新规划——行为变化写进 PR 影响段,并把「图版本不一致」的报错做成可操作提示。
- platform 收编(WP5)触及双系统合一与 X6,体量接近一个小阶段:允许 WP5 在 Q1 任务内再拆子 PR 序列(Store 实现先行、驱动合一随后),但不允许只交一半——「不再单独驱动一套运行」是验收条款。
- 消费方只有 squadadapter 一处(backend)与 squad CLI/serve 一处,适配面可控。
### 3.6 Q1 验收(对照总提案 §5 Q1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 在每个 superstep 边界杀进程再恢复,已成功步骤的副作用次数为 1 | 新测试:脚本化蓝图(≥3 个 superstep),逐边界 kill -9 后 Resume,断言每个已完成步骤的产物写入次数=1、事件流无重复完成记录 |
| 两个写同一工件的步骤不会并行 | 调度测试:写集交集步骤的执行区间不重叠(事件时间戳断言) |
| 文件 run 与 SQLite 不再各记一份步骤状态 | conformance 套件在 FileStore 与 SQLiteStore 双实现上通过;server 单引擎挂载;platform 侧无独立 run 状态表残留(schema 断言) |
## 4. I1 命令化识别
总提案原文(§5 I1):「命令列表与 ActionRef。中文匹配的词界或最短词长。同分时可按 priority 决胜。Catalog 白名单与 top-k。Backend 唯一 Router 消费 ActionRef。」验收点名审计 I1、I2、I3、I5 有回归(I1/I2 在当前 pin 已修并有测试,I3/I5 开放,见下)。
### 4.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| 命令列表 | **不存在 ** 。`Result = {Input, Branch, Top *Candidate, Candidates, SnapshotRevision}` ( `deps/agent-intent/intent/result.go:44-50` ),输出是单 Top 意图;`ActionRef` /`Start` /`SetSlot` 全仓零代码命中 | 命令类型、序列化、校验全缺 |
| ActionRef 统一 | **四种动作键并存 ** : (a) `IntentSpec.Handler` ( spec.go:81, squadintent Runner 消费);(b) `Metadata["blueprint"]` ( adapters/intent/gate.go:123-131 → engine.go:228-237);(c) `CommandSpec.ID` ( agent-command, backend server.go:653-654 把意图 ID 当命令 ID);(d) `WorkflowDef.IntentID` ( core plugin.go:60, workflow/registry.go:40-42 索引) | 统一表示与迁移映射 |
| Backend 唯一 Router | **不存在 ** 。消费分散三处:chat 路由内联 if-else( `internal/server/server.go:647-660` ,命令优先于工作流写死)、HITL 自由输入转轨(`internal/runmgr/runmgr.go:1203-1248` )、装配(`internal/app/app.go:300-301,316,381` )。squad 的蓝图意图链 backend **完全没接 ** ( squadadapter/entry.go:98-110 不设 IntentGate/IntentCatalog/IntentRunner;全 backend grep `squadintent` 零命中) | Router 组件与优先级裁定 |
| 中文词界/最短词长(I3) | 纯子串 `strings.Contains` ( registry.go:142/147/154);无词界设施、无最短词长告警(grep 零命中);NFKC 已落地(fold.go:11-28)但**两个宿主都没开**( backend intentgate gate.go:57 只设 TopN; squadintent gate.go:37-44 只设 Guard/Protected 域);语料中文负例只有一条靠 excludes_any 硬回避(testdata/corpus.json 第 6 行 + specs/base.json:10) | 词界设施、lint 告警、负例语料、宿主开 NFKC |
| 同分 priority 决胜(I5) | `Priority` 字段已在(spec.go:74)且是排序第二键(registry.go:327-335、recognizer.go:172-183);但裁决要求分差 ≥ margin( recognizer.go:92),`AmbiguityMargin <= 0` 强制回 0.1( registry.go:61-63)——**同分恒 ambiguous, priority 不参与决胜** | 裁决逻辑调整 |
| Catalog 白名单与 top-k | Metadata 白名单已有(fold.go:61-64,只放 blueprint/guidance 两键);但 `CatalogSpec.Handler` 仍进 JSON 视图(catalog.go:20-30);目录全量输出无 top-k、无相关度(catalog.go:34-62) | top-k 与相关度参数;Handler 是否进目录的裁定 |
| eval | `intentctl eval` 单轮(cmd/intentctl/main.go:229-292, recognizer 以 nil expectation/classifier 构建于 main.go:254);语料 10 条(testdata/corpus.json);输出只有 PASS/FAIL 计数(main.go:285-290) | 多轮夹具格式与逐轮命令期望(precision/recall 指标输出属 I2, I1 先扩格式) |
| 命令层前置概念 | 否定只认 excludes_any( recognizer.go:66 注释明说「不要写入」交给命令层 Cancel——命令层尚不存在,审计 I12);ExpectationProvider 接口在(expectation.go:7-19)但宿主侧零实现;L2 Classifier 同理(classifier.go:9-15,全部 NewRecognizer 传 nil) | I1 只需命令层与宿主编排;L2 实现、语义端口在 I2 |
另外注意:overlay 解析 `DisallowUnknownFields` ( loader.go:21-24、51)——spec 加字段不做显式注册会硬失败旧配置;backend 插件读的是**裸 spec 数组**( internal/plugins/host.go:216-222),不是 `{intents, disable}` overlayDoc——两种载体的兼容都要照顾。
### 4.2 工作包分解
**WP1 命令模型(库侧核心) **
- 新类型:`Command` 联合 `{Kind, ...}` ——`Start{ActionRef, Slots}` / `SetSlot{Name, Value}` / `ChangeSlot{Name, Value}` / `Cancel{Scope}` / `Clarify{Question, Missing}` / `Chitchat{}` / `Handoff{Target}` ; `ActionRef{Kind: command|workflow|blueprint|handler, ID string}` 。
- `Result` 增加 `Commands []Command` ( Branch/Top 保留作诊断与兼容,逐步退役由 Router 封装);从候选到命令的展开规则:Top 命中 → Start(带 ActionRef 推导);槽位缺失 → Clarify(或 ExpectationProvider 命中时 SetSlot/ChangeSlot);多候选 → Clarify 消歧或 Ambiguous 保持(见 WP4/WP5 裁决);否定/取消语义 → Cancel(命令层首次承认否定,收编 recognizer.go:66 的注释承诺)。
- 命令的校验与现有候选校验同级(ID 集合、槽位作用域);序列化进 schema 包并给 JSON 样例(对齐 D1 的「三库发布 JSON Schema 样例」做法)。
- 测试:每种命令的展开路径;校验拒绝;round-trip。
**WP2 ActionRef 推导与四种动作键迁移 **
- spec 增加显式字段承载 ActionRef( `action: {kind, id}` ),`DisallowUnknownFields` 下注册;推导规则(兼容期):`action` 缺省时按旧键推导——`Handler` → `{handler, <值>}` 、`Metadata["blueprint"]` → `{blueprint, <值>}` 、命中 backend 命令总线 → `{command, <ID>}` 、命中 workflow 索引 → `{workflow, <IntentID>}` 。
- backend 插件装载(host.go:216-222 裸数组)与库 overlay 两种载体都接受新字段;旧字段在 I1 不删除(退役留给 I2 之后的发版任务)。
- 测试:四种旧键的推导等价于现状路由结果(对照 server.go:647-660 现行为刻画测试先行);新字段优先于旧键。
**WP3 Backend 唯一 Router **
- 新组件(建议 `internal/route` ,或收进 `internal/server` 的独立文件):输入 = 命令列表 + 会话状态,输出 = 路由决策(command 分发 / workflow 启动 / squad 入口 / 普通回合 / 澄清回问)。
- 收编两处现有消费:server.go:647-660 的内联 if-else 与 runmgr.go:1203-1248 的 HITL 自由输入转轨,统一改调 Router; slash 命令(server.go:741-758)保持旁路不过在优先级文档里写明。
- **优先级明文**(提案 §6 要求 I1 写明):`command > workflow > blueprint > 普通回合` ; `PromptTrigger` (技能触发)与 Intent 同命中时的裁定:**技能触发是提示词装配、不参与路由决胜;若 Intent 产出 Start/SetSlot 类命令则路由优先,纯提示词命中不产生命令**——写进 Router 文档与测试。
- squad 蓝图链(`{blueprint, key}` )经 ActionRef 到达 Router 后,Router 调 squad 入口;**backend 接入 squadintent 适配器(entry.go:98-110 的缺口)在本 WP 一并补上**——这是「blueprint 经 ActionRef 到 Router」验收的必要条件,范围可控(装配 + 映射,不改 squad 库)。
- 测试:命令优先于工作流(现有 `TestCommandBusBeatsWorkflow` 迁移)、workflow 经 ActionRef 启动、blueprint 经 ActionRef 进 squad 入口、HITL 自由输入转轨走同一路由、fallback 进普通回合。
**WP4 中文词界与最短词长(I3) **
- `MatchSpec` 增加 `boundary` (词界模式:CJK 按「非汉字或首尾」为界,拉丁按词字符边界)与 `min_len` (词条最短长度,低于则 lint 告警)——二选一还是并存由实施期原型定,但 lint 告警(`intentctl lint` )必须覆盖「短词条 + 无词界」组合。
- 两宿主开启 NFKC: backend `intentgate.Rebuild` ( gate.go:57)与 squadintent( gate.go:37-44)的 Options 显式开 `NFKC:true` ,并补开启前后对照测试。
- 语料补负例:中文子串误命中系列(如「配置」误中「配置项」、「写入」误中「不要写入」的否定场景交给 Cancel 的正例对照)。
- 测试:词界开/关矩阵、最短词长告警、新负例全过、既有 10 条语料不回归。
**WP5 同分 priority 决胜(I5) **
- 裁决调整:前两名置信度同分(差 < margin)时,若 `Priority` 不同则高优先者判 `matched` ; priority 也相同才 ambiguous。`AmbiguityMargin` 语义不变(仍防分数过近误判),margin 强制下限 0.1 保留。
- 测试:同分高 priority 命中、同分同 priority 标歧、margin 边界三例;既有漏斗测试不回归。
**WP6 eval 格式扩展 **
- 语料格式 v2:用例带 `turns: [{input, expect: {commands: [...]}}]` ,逐轮期望命令列表;`intentctl eval` 读 v2 时构建 ExpectationProvider 夹具实现(库内给测试用 Provider),逐轮比对命令序列;v1 单轮格式兼容读取并自动映射为单 turn。
- 指标输出在本阶段只加「逐轮命令匹配率」明细(precision/recall 的正式定义与输出属 I2,不在 I1 提前承诺)。
- 测试:v2 多轮夹具端到端(含 SetSlot+Start 组合用例——验收条款的载体)、v1 兼容、误期望 FAIL。
### 4.3 涉及仓与落点汇总
- Agent-Intent: `intent/{result.go,recognizer.go,registry.go,spec.go,loader.go,catalog.go}` 、新 `intent/command.go` 、`schema/` 、`cmd/intentctl` 、`testdata/` 。
- Backend: `internal/server/server.go` 、`internal/server/` (新 Router)、`internal/intentgate/` ( Recognize 返回命令、NFKC 开启)、`internal/runmgr/runmgr.go` ( HITL 转轨改调 Router)、`internal/squadadapter/entry.go` (接 squadintent)、`internal/plugins/host.go` ( spec 新字段)。
- Agent-Squad: `adapters/intent` 只在映射 ActionRef 时动(blueprint 键推导),核心不动。
- Dev:两层 pin。
### 4.4 实施顺序
WP1 命令模型 → WP2 ActionRef 迁移(刻画测试先行)→ WP5 决胜 + WP4 词界(库内独立,可并行)→ WP3 Router(依赖 WP1/WP2)→ WP6 eval(收尾,验收载体)。
### 4.5 风险
- **路由行为变化面**:Router 收编两处消费 + squad 蓝图链接入,是产品可见行为。对策:WP2 的刻画测试先把现路由行为钉死,Router 切换后逐条等价;蓝图链接入属新增能力,单独一组测试,不改动有路由。
- overlay 严格解析:spec 新字段必须同步注册进 loader 与 backend 裸数组装载两处,漏一边即硬失败——测试覆盖两种载体。
- NFKC 默认开启是行为变化:只动两个宿主的显式配置,库默认不变(库保持 fail-closed 哲学,宿主显式选择)。
- 命令列表与「单 Top」并存期,`Result.Top` 的消费方(squadintent gate 等)保持可用;退役由后续发版任务评估,不在 I1。
### 4.6 I1 验收(对照总提案 §5 I1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 审计 I1、I2、I3、I5 有回归 | I1(分类器篡改拒绝,recognizer_test.go:188/238/269 现存)与 I2(作用域校验器,slots_catalog_test.go:70 现存)保持绿;I3 新增词界/负例测试;I5 新增决胜测试——四个测试名写进任务卡 |
| 一条用户话可以产出 SetSlot 加 Start | eval v2 多轮夹具含该用例并通过 |
| 命令 id、工作流 IntentID、小队 blueprint 都通过 ActionRef 到达 Router | Router 测试三条各一;插件清单字段名不变的 diff 断言(装载测试比对字段名清单) |
| 插件清单不改字段名 | 插件装载兼容测试:现有插件 intents/workflows 清单零修改装载通过 |
## 5. 三任务之外的说明
- N2/Q2/I2 及更后的阶段不在本文范围;本文的 WP 划分假定 N1/Q1/I1 全部合入后才启动它们。
- 三任务的验收都依赖「脚本化模型 + 库内夹具」,不依赖真实模型密钥;真实任务 A/B(总提案 E 节「少量真实任务加人工复核」)在各阶段验收时由所有者安排。
- 本文档本身不修任何代码;如评审要求改设计,在本文档上修订后再启动对应任务。
## 6. 验收(本文)
- 三个阶段的实施方案各自满足:工作包可独立派生任务卡、验收条款可机器核对、与总提案逐条映射(§2.6 / §3.6 / §4.6 的表)。
- 现状陈述均有 file:line 证据(撰写日 pin 见 §1.1);评审重放 = 按表中证据抽查。
- 三任务协调规则(§1.2)与 AGENTS.md 分支层级一致。