← Back to home@bbaz123

dsh-auto-plan-executor

DSH 自动方案执行插件:四条件触发、方案冻结、任务图与步骤账本、门禁预算、完整度闸门与八部分报告。

Stars
0
Language
JavaScript
Created
Oct 22, 2026
Updated
Oct 22, 2026

Introduction

dsh-plugin-auto-plan-executor

DSH(DeepSeek Harness)的自动方案执行宿主插件。

当真实用户提交一份方案时,该插件把当前会话切换到方案执行模式:冻结原方案、建立任务图、 受控连续轮次执行、门禁与预算守卫、独立验收,最后产出一次性完整报告。普通聊天保持原样。

目标宿主:0.2.0-rc.2。包名:dsh-plugin-auto-plan-executor。宿主行 id:auto-plan-executor。

现状(2026-10-19):源码、单元测试、装配验证与双插件共载联调均已完成;插件已装入日常 web profile——bundles=14、dependencies 为 link:。本轮新增的步骤账本 / 派生问题 / 边界复核 / OpenViking 实时镜像 / 收尾退出方案模式属于宿主插件代码改动,必须在重启 DSH 后才会生效 (ESM 模块缓存按文件 URL 记,禁用→启用只会重放旧模块)。仍未接线的能力见下文「已知缺口」。


它做什么

能力实现位置运行时是否接线
四条件触发 + 明确否定优先 + 去重lib/trigger.js✅ 已接线(agent/pre-step)
原方案冻结(哈希、修订、不可变核心)lib/plan-snapshot.js✅ 已接线(激活时冻结一次)
任务图(环检测、缺依赖、同目标并发写)lib/task-graph.js✅ 已接线(auto_plan_task,含 start / list 的就绪判定)
状态机与持久化(原子写、脱敏在 store 边界)lib/plan-state.js✅ 已接线(schema v3:步骤账本字段)
方案步骤清单(原文提取 + 逐字 declare + 受限降级)lib/plan-steps.js✅ 已接线(激活时冻结;需求/交付/验收段也提取)
逐步处置(DONE / OPEN / EXEMPT,收尾重验)lib/step-coverage.js✅ 已接线(收尾时重算,不信持久化 verified;未关闭问题阻塞 DONE)
步骤账本(派生问题 / 生命周期 / 边界复核 / 回退)lib/step-gate.js✅ 已接线(auto_plan_step 的 begin / issue / close_issue / review)
OpenViking 实时镜像(写/读/清理,失败不阻塞)lib/ov-client.js✅ 已接线(激活时写清单,每个步骤事件同步一次)
完整度闸门(缺口拒绝收尾 + 缺口任务 + 收敛上限)lib/completeness.js✅ 已接线(auto_plan_finish)
门禁分级 G0–G4 + 决策落盘lib/policy.js✅ 已接线(auto_plan_gate)
费用守卫(单次/累计 2 元,不可核价即拒绝)lib/budget.js✅ 已接线
附件读取(白名单、字节/字符/时间上限)lib/attachment-reader.js✅ 已接线
Goal 能力发现(按可观测的 goal 工具 + goals 服务)lib/goal-adapter.js✅ 已接线(并如实报告在 auto_plan_status)
独立验收(只认观测证据)lib/verifier.js✅ 已接线(auto_plan_task action="verify")
Cordis 装配、Prompt 段落、工具、tools.guardlib/index.js✅ 已接线
与待确认插件的只读观察lib/confirmation-adapter.js⚠️ 部分:能力发现已接线并在状态里报告;镜像(confirmationMirror)当前没有运行时写入方,所以 confirmations 恒为 []。见下方"已知缺口"
最终统一报告(八部分)lib/reporter.js✅ 已接线(auto_plan_finish 收尾:验收 → 终态 → 报告落盘)
Goal 目标文本lib/goal-adapter.js✅ 已接线并在收尾时产出(auto_plan_finish 返回 goalHandoff)

触发条件(四选一)

  1. 用户明确说「按照方案执行」「按这个方案实施」等;
  2. 用户明确说明当前提交的是方案(「这是我的实施方案」);
  3. 本轮真正上传的附件文件名包含「方案」;
  4. 本轮消息首部或可读取附件正文的标题为「××方案」。

优先级例外:同轮出现「不要执行/仅审阅/只是讨论」等明确否定,或本会话仍有未关闭的 待确认事项时,一律不触发。助手输出、工具结果、Goal 合成轮次不会触发。

与 dsh-confirmation-resolution 的边界

  • 本插件拥有方案生命周期与自己的状态文件;
  • 待确认插件独占确认项账本;
  • 本插件没有任何 register / decide / complete / approve 路径,也不导入其私有 ledger.js;
  • 不伪造用户回复、不制造 C 项、不假设 V2 已启用。

安装

dsh plugin --profile web add C:\Users\a1941\Desktop\DeepSeek\dsh-auto-plan-executor
dsh --profile web --dump-config

--dump-config 需要写入 $DSH_HOME/profiles/web/cordis.yml,因此必须在不被沙箱限制的终端执行。

配置

行配置(cordis.patch.yml 已给出默认值):

字段默认含义
enabledtrue总开关;false 时完全不介入普通对话
sectionName / orderauto-plan:policy / 118Prompt 段落名与顺序
budgetLimitCny / perOperationCny2 / 2累计与单次费用上限(人民币)
maxConsecutiveFailures3同一任务连续失败上限
stateDirName.dsh-auto-plan状态目录名
workspaceRoot""(进程 cwd)状态目录根
enforceWriteToolstrue方案激活时,write/edit/ops_replace/apply_patch 等文件改写工具必须落在 RUNNING 任务的 allowed_scope / writes 内;没有 RUNNING 任务直接拒绝。shell/pwsh 无法按路径匹配,仍可能绕过
enforceScopefalse额外开启后,所有带路径参数的工具(含读)都受 allowed_scope 限制;默认关闭以免影响普通任务
costArgName""声明费用数值的工具参数名(供预算守卫)
maxRounds12交给原生 Goal 的轮次上限
maxCompletenessRounds6完整度闸门允许的补做轮次;同一缺口指纹重复且无进展 → STALLED,按 BLOCKED 如实收尾
maxSteps400步骤提取上限;超出判 TRUNCATED 并在 extraction 中如实报告(当前不会因此自动拒绝收尾,见已知缺口 9)
openvikingEnabledtrue是否把步骤账本实时镜像到 OpenViking;写失败只记录,不阻塞方案
openvikingBaseUrlhttp://127.0.0.1:1933本机 OpenViking HTTP 端点
openvikingHomeviking://~/plans/auto-plan镜像根目录(~ 展开为当前用户根;不写进 memories/,避免污染召回)
openvikingTimeoutMs1000单次镜像请求超时;短超时避免拖住回合
serialStepsfalsefalse:保留独立分支并行,只禁止关闭仍被问题阻塞的步骤;true:严格串行,下一步开始前强制复核上一步
cleanupOnFinishtrue通过收尾并退出方案模式后,删除该方案在 OpenViking 的镜像目录;失败如实写进报告

方案完整度闸门(2026-10-18)

它修的是什么:收尾过去只看"模型自己登记的任务图",于是 20 步方案登记 3 步、3 步过了就是 "全部完成并验证"。现在收尾按冻结的方案步骤判。

  1. 步骤清单冻结在原文上:激活时从 snapshot.sourceText 确定性提取(编号列表、- [ ]、 步骤N、P0/P1/P2 与标题分段;代码块跳过;需求 / 目标 / 交付 / 验收 / 范围 / 功能 / 任务 段落里的编号、项目符号、表格行与带职责动词的散文句也算步骤;表头含「任务/待办/步骤/交付」 的表格在任意段落都算;「已知问题 / 现状」表格不算工作)。模型只能用 auto_plan_step action="declare" 追加,且每条必须 source_quote 逐字引用原文(只忽略标点差异);已提取步骤不能删除。
  2. 降级可选要引用原文:classify 把某步降为 OPTIONAL,必须引用原文里真实存在的可选措辞 (可选 / 如需 / 视情况 / 可随后 …);P2 段默认可选,出现「必须 / 强制」则重新必需。
  3. 收尾重验:auto_plan_finish 逐项判定 DONE / OPEN / EXEMPT,DONE 由当场重跑 verifier 决定——持久化的 verified: true 不算数。
  4. 缺口即拒绝:存在阻塞缺口时不写报告、不推终态,幂等登记 GAP-PS-xx 缺口任务、记录一轮 完整度、返回续跑指令(下一次 agent/pre-step 注入一次,不新起续轮循环)。
  5. 两类用户侧豁免:USER_AUTHORIZATION 必须引用本方案真实的 DENIED G3/G4 记录; USER_ACTION 必须写明需要用户提供什么。无证据的豁免判 OPEN:EXEMPTION_UNEVIDENCED。
  6. 收敛:同一缺口集合 + 相同原因 + 完成数不变,连续两次 → STALLED;轮次用尽 → ROUNDS_EXHAUSTED;两者都写成 阻塞,绝不写成完成。
  7. 终态:全部完成且无用户侧待办 → COMPLETED_VERIFIED(全部完成并验证); 仅剩有证据的用户侧待办 → COMPLETED_USER_PENDING(完成,不含用户侧待办),报告单列「待用户处理」。
  8. 未覆盖段落阻塞收尾:「必须 / 交付物 / 验收标准 / 验收条件」段落如果提取不出任何步骤, 会作为 SEC:<section>:UNCOVERED_SECTION 阻塞收尾;必须用 auto_plan_step action="declare" 补齐, 或用 action="dismiss_uncovered" 提交该段落正文的逐字引用 + 理由,才能放行(报告会列出)。

步骤账本与服务器镜像(2026-10-19)

它修的是什么:完整度闸门只保证"清单里的每一步都被覆盖",但执行中派生的问题、以及"某一步 到底算不算完"仍由模型口头决定。现在每一步有独立账本,问题归属该步,未关闭就不能算完成。

  1. 激活即写清单:检测到方案、进入执行前,把冻结步骤写成 viking://~/plans/auto-plan/<plan_id>/ledger.json(机器账本)与 plan.md(人读清单)。 写入失败只记录 lastSync,不阻塞方案(镜像不是权威源)。
  2. 每个步骤事件同步一次:task-start / task-verified / step-begin / issue-added / issue-closed / review-* / finish-refused 都会递增 revision,并同时重写 ledger.json 与 plan.md(人读清单不再是激活快照)。
  3. 派生问题归属步骤:auto_plan_step action="issue" 把问题记到 step_id 名下; 该步还有 OPEN 问题 → 步骤状态为 RUNNING,完整度判 ISSUES_OPEN,不能关闭。
  4. 复核闭环:action="review" 要求为每条未关闭问题给出 resolved 判定—— resolved=true 必须附 evidence_ref,resolved=false 必须写 reason。只要有一条未解决, 复核判 REVIEW_REJECTED,方案回退到该步(rollbacks 留痕),不会推进。
  5. 两种执行模型:
    • 默认 serialSteps=false(本轮确认):独立分支可并行开始,只禁止关闭被阻塞的步骤;
    • serialSteps=true:严格串行,开始下一步前必须对上一步提交 APPROVED 复核; 缺复核时 auto_plan_task action="start" 直接拒绝,并返回复核指令。串行模式下 一个任务不得同时覆盖多个必需步骤(否则会跳过上一步复核)。
  6. 边界读服务器:start 开始步骤、提交复核、以及 auto_plan_finish 收尾前都会先读一次 镜像账本,把服务器上本地没有的问题并入本地(本地状态优先;server-only 问题一律按 OPEN 入账,必须本地凭证据关闭),并作废受影响步骤的复核;读不到时按本地账本继续,并标 degraded。
  7. 收尾退出方案模式:完整度通过 → 推终态 →(cleanupOnFinish)删除该方案镜像目录 → 写报告 → 从会话 runtime 表移除。只有**报告写成功且清理成功(或已跳过)**才退出;报告写失败或清理失败时 保留 runtime,auto_plan_status 仍可见、可重试 auto_plan_finish,不会留下没人管的孤儿。 清理失败会在报告与 SERVER_CLEANUP 里如实写 FAILED,不写成已清理。
  8. 重启恢复:宿主重启后 runtime 表为空;第一次 agent/pre-step 会扫描本地 state.json, 找到同会话、未终态的 plan 后重建 runtime(任务图、步骤、问题、复核、stepBegins、 evidenceByTask、预算记录与已拒绝的 operation key 全部从磁盘恢复),注入一次 「已恢复未完成方案」指令,不要求用户重新提交方案。重启不会把已用预算归零,恢复后的 剩余额度仍受同一 2 元上限约束。 只有服务器镜像、没有本地 state 的方案不会被自动恢复——这是有意的(无法证明它的任务与证据); 可以用 auto_plan_status 的 include_mirrors: true 查看 orphan 镜像,再人工核对/清理。
  9. 证据引用绑定:关闭问题或 review 的 resolved=true 时,evidence_ref 必须精确匹配 本方案已记录的 evidenceRef,或是 file:<工作区相对路径> 且文件存在;随手编一个字符串会被 EVIDENCE_NOT_FOUND 拒绝。它仍然只是"这条引用指向的观测"的声明,不能证明命令真的执行过 (与 verifier 的 evidenceRef 同一局限)。
  10. 写入工具默认门禁:方案激活期间,write/edit/ops_replace/apply_patch 等文件改写工具 必须落在当前 RUNNING 任务的 allowed_scope / writes 内;没有 RUNNING 任务时直接拒绝。 这是方案内的轻量门禁,不影响普通会话;shell/pwsh 无法按路径可靠匹配,仍是已知绕过口。

镜像不是强制执行器:OpenViking 只是被读写的账本,不会自己驱动 AI。真正的"强制"由插件在 auto_plan_task start / auto_plan_step review / auto_plan_finish 三处执行;默认并行模式下 只禁止关闭被阻塞的步骤,需要"第二步前强制复核上一步"时必须显式 serialSteps: true。 模型若绕过这些工具直接改文件,只有 enforceScope=true 的路径门禁能拦住(默认关闭)。 这是已知边界,不是隐性保证。

验证状态(实测)

层命令结果
语法node --check lib/index.js(含 step-gate / ov-client)PASS
单元(212 用例)node --import ./test/host-hooks.mjs --test ...(显式文件清单)212 pass / 0 fail
装配(57 项)node --import ./test/host-hooks.mjs verify/wiring.mjs57/57 PASS(含 STEP-REVIEW / 并行模型 / 镜像脱敏 / 收尾清理成功与失败 / 重启恢复+预算 / 未覆盖段落 / 写入工具门禁 / orphan 列表 / 退出方案模式)
双插件共载(21 项)node --import ./test/host-hooks.mjs verify/dual-plugin.mjs21/21 PASS
完整门禁上述三段串行执行(等价 npm run check)exit 0
真实 OpenViking 往返content/write → content/read → fs/ls → fs DELETE200/200/200/200,删后读 404(2026-10-19 实测)
安装到 web profileplugin_manager install_bundle包已装入(bundles=14、link:)+ dsh --profile web --dump-config exit 0
运行时激活Inspector Tool.listToolsauto_plan_status / auto_plan_task / auto_plan_gate 已注册

已知缺口(不隐瞒)

  1. confirmationMirror 没有运行时写入方:lib/confirmation-adapter.js 具备能力发现与只读镜像, 但 index.js 目前没有任何地方调用 mirror.observe()。因此:
    • auto_plan_status 里的"已观测确认项"恒为空,state.confirmations 恒为 [];
    • §4.1「正在回复已发布 C 项时不触发」这一条,在生产里只靠正文里的 C1/C2 文本模式生效, 「本会话仍有未关闭 C 项」那条判据目前不生效。
    • 这不是安全绕过:镜像只是"更早地不再触发",缺了它仍然不会误建方案(文本模式仍在)。
    • 要关闭它需要挂一个只读的 tools/post-execute 观察器;我没有擅自加,因为它会改变 最敏感的触发路径语义(一条被观察为 open 的 C 项会一直阻塞后续触发,直到观察到它关闭)。
  2. auto_plan_finish 是唯一的收尾闸门:它先跑完整度检查,缺口未清时拒绝收尾 (不写报告、不推终态),通过后才用同一套 verifier 重新验收、推进终态 (COMPLETED_VERIFIED / COMPLETED_USER_PENDING / BLOCKED / PARTIAL)、写八部分报告, 并返回交给原生 Goal 的目标文本。它不会自己创建 Goal——创建 Goal 是有副作用的主机动作, 所以这里只交文本(方案 §6.1)。 「失败」这一终态不会由本工具推导:任务状态 FAILED 是可重试态,把它映射成"方案失败" 会在还有重试额度时报成失败;失败 只保留给显式中止。
  3. snapshot.requiredDeliverables 仍恒为空(激活时按字面量 [] 创建,模型无法自行声明), 因此 validateRequiredCoverage 仍是"空集真空通过";覆盖改由步骤清单承担: snapshot.planSteps 是新的权威来源,任务用 plan_steps 声明覆盖哪一步。
  4. goalObjective 描述的轮次上限(maxRounds)没有被消费,因为没有任何路径创建 Goal。
  5. 复核判定仍由模型提交:插件不能读取对话原文,review 的 resolved 是模型基于上下文给出的 判断;插件能强制的是"每条问题必须有判定、resolved 必须带证据、未解决就回退",不能替模型判断 "这段对话是否真的解决了它"。
  6. 镜像不是强制执行器:OpenViking 不会自己驱动 AI;serialSteps=false(默认)下步骤可并行开始, 插件只保证"被问题阻塞的步骤不能关闭"。需要严格"第二步前强制复核上一步"时必须显式 serialSteps: true,否则那个边界不会发生。
  7. 绕过插件工具仍可能发生(已收窄):write/edit/ops_replace/apply_patch 等文件改写工具 在方案激活期间默认受 allowed_scope / writes 约束;但 shell/pwsh 命令无法按路径可靠匹配, 仍可能直接写文件。要硬隔离需要在宿主沙箱/OS 层限制,而不是本插件。
  8. 清理只删服务器镜像:cleanupOnFinish 删除的是 viking://~/plans/auto-plan/<plan_id>/; 本地 .dsh-auto-plan/<plan_id>/ 的 state.json 与报告仍保留,作为本地审计痕迹。
  9. TRUNCATED 不会自动阻塞收尾:提取超过 maxSteps 时会在 extraction 里如实标记 truncated/ok=false 并给出告警,但当前闸门仍按已提取的前缀判定;需要把未提取部分用 auto_plan_step action="declare" 补齐,插件不会替模型猜剩余步骤。
  10. 恢复依赖本地 state/evidence:重启恢复只扫描本地 state.json + evidence 文件; 只有服务器镜像、没有本地 state 的方案不会自动恢复,也不会被自动删除(可能是另一台机器/会话的)。 可用 auto_plan_status(include_mirrors: true)查看 orphan 镜像,人工核对后再清理。
  11. evidence_ref 已绑定,但仍是声明:现在必须精确匹配本方案已记录的 evidenceRef,或 file:<工作区相对路径> 且文件存在;但一条已记录的 ref 仍只说明"模型声称执行过这个观测", 不能证明命令真的执行了(与 verifier 的 evidenceRef 同一局限)。

验收证据契约(2026-10-15 收紧,直接对应审查发现的阻塞项)

一条证据只有携带它那个 kind 应有的字段才算"观测",否则只能是"断言":

kind必须携带说明
command / test整数 exitCode + 非空 evidenceRef退出码非 0 → 任务判 FAIL(不是 UNVERIFIED)
diff / probe非空 evidenceRef指向真实产物
manual非空 attributedTo人工断言必须写明由谁作出,否则不算
self-report / model-claim / tool-called / generated-code / planned-command——永远只能是断言,不能使任务通过

为什么:收紧前,{kind:"command", criterion:"<验收原文>", ok:true}(无退出码、无引用) 就能把任务判成 verified=true 并让 COMPLETION: COMPLETE,进而支持"全部完成并验证"的假结论。 现在这种输入一律 UNVERIFIED,并在理由里点名缺了哪些字段。

node_modules/ 是必需的,不是可选项。lib/ 用裸说明符 import 宿主包,装进 profile 后由 Node 从本目录向上解析,所以这里必须有指向 DSH 安装的 Junction: node_modules/@deepseek-ai/{cordis,dsh-tools,dsh-llm,schemastery,dsh-attachment}。

2026-10-15 实测事故:最初因沙箱拒绝建 Junction 而改成"零 node_modules + 开发期 resolve 钩子", 结果装进 profile 后 failed to import(application: failed / 1 entry did not activate)。 而且首次解析失败会被长驻进程的解析缓存记住——禁用→启用只会重放失败,必须重启 DSH。 判据:同一个包用 node -e "import('<包名>')" 在新进程里成功、宿主仍报 failed to import。

test/host-hooks.mjs 保留为兜底(没有 Junction 时也能跑测试),它同样指向 profile 中同一份 宿主包实例,因此不会产生第二份模块身份。该文件不属于发布内容。

目录

dsh-auto-plan-executor/
├── package.json / cordis.patch.yml
├── lib/                纯逻辑模块 + Cordis 装配
├── test/               单元测试与开发期 resolve 钩子
├── verify/             装配验证、双插件共载验证
└── artifacts/          环境报告、基线、操作手册、回滚、测试矩阵

回滚

dsh plugin --profile web remove dsh-plugin-auto-plan-executor

详见 artifacts/rollback.md。