iterate-plugin 2.12.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh-CN.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # iterate-plugin for DeepSeek Harness (dsh)
2
2
 
3
- > dsh 桌面端的 iterate 收敛面板、UI 分诊与审查闭环插件。把 iterate 生态的同一套 review/fix loop 直接搬进 dsh 界面。
4
- > The iterate ecosystem's convergence dashboard, triage UI, and review/fix loop, natively embedded inside the DeepSeek Harness (dsh) desktop client.
3
+ > dsh 桌面端的 iterate 质量指挥中心 + 经验银行插件(v3.2)。把 iterate 生态的同一套 review/fix loop 直接搬进 dsh 界面,新增质量门禁、经验银行、防御事件流与原生指挥操作。
5
4
 
6
5
  <p align="center">
7
6
  <a href="README.md"><strong>English</strong></a> ·
@@ -18,7 +17,7 @@
18
17
  <a href="https://github.com/jingzhao-l/iterate-plugin/releases"><img src="https://img.shields.io/github/v/release/jingzhao-l/iterate-plugin" alt="GitHub release"></a>
19
18
  </p>
20
19
 
21
- > ⭐ 如果这个插件对你的 dsh 工作流有帮助,欢迎为主仓库点亮 Star,这是对开源维护最大的支持!你的 Star 能让 iterate 被更多开发者看见。
20
+ > ⭐ 如果这个插件对你的 dsh 工作流有帮助,欢迎为主仓库点亮 Star,这是对开源维护最大的支持!
22
21
 
23
22
  ---
24
23
 
@@ -26,11 +25,9 @@
26
25
 
27
26
  **iterate 不是一个独立的工具,而是一套附着在现有 AI 助手之上的技能生态。** 它不会替换你的 IDE 或 AI 工具,而是在你已有的工作流里,叠加一层"严格的代码收尾与把关"。整个生态由**三个可互换组件构成,共用同一套 `iterate.config.yaml` 与同一套 9 维度审查体系**:
28
27
 
29
- | 组件 | 形态与位置 | 面向场景 |
30
- |---|---|---|
31
- | **[Core Skill + CLI / 核心技能与命令行](https://github.com/jingzhao-l/iterate-skill)** | 可移植 AI 技能 `/iterate` + `iterate` CLI(源码:iterate-skill 主仓库根目录) | Trae / Claude Code / Cursor / Copilot / Codex 等 25+ 助手中以对话方式进行多轮迭代 |
32
- | **[iterate-harness / 无头引擎](https://github.com/jingzhao-l/iterate-harness)** | 独立无头引擎,命令 `ih`(npm: `iterate-harness`) | 在终端 / CI / Git 钩子里**脱离对话式助手**,运行同一套闭环 |
33
- | **iterate-plugin / dsh 桌面插件(本仓库)** | dsh 桌面客户端插件(npm: `iterate-plugin`) | 把 harness 运行时**直接接入 dsh 界面**:收敛看板、分诊面板、轮次进度,均以原生 dsh 控件形式展示 |
28
+ - **[Core Skill + CLI / 核心技能与命令行](https://github.com/jingzhao-l/iterate-skill)** — 可移植 AI 技能 `/iterate` + `iterate` CLI。在 Trae / Claude Code / Cursor / Copilot / Codex 等 25+ 助手中以对话方式进行多轮迭代。
29
+ - **[iterate-harness / 无头引擎](https://github.com/jingzhao-l/iterate-harness)** — 独立无头引擎,命令 `ih`(npm: `iterate-harness`)。在终端 / CI / Git 钩子里**脱离对话式助手**,运行同一套闭环。
30
+ - **iterate-plugin / dsh 桌面插件(本仓库)** dsh 桌面客户端插件(npm: `iterate-plugin`)。把 harness 运行时**直接接入 dsh 界面**:收敛看板、分诊面板、轮次进度,均以原生 dsh 控件形式展示。
34
31
 
35
32
  三者关系:**Core Skill** 是最通用的跨助手审查/修复引擎("大脑");**iterate-harness** 是同一引擎封装为无头 CLI + WebUI,适合跑在无需交互的场景;**iterate-plugin**(本仓库)把 harness 运行时进一步封装为 dsh 插件,直接在 dsh 桌面客户端内渲染分诊 UI 与收敛看板。配置(`iterate.config.yaml`)与 9 维度审查体系**三者完全一致**——理解其一,即可举一反三。
36
33
 
@@ -59,53 +56,81 @@ dsh plugin --profile web add iterate-plugin
59
56
 
60
57
  > AI 助手往往"说得多、做得浅":一次对话只改几行、看过一个文件就不再管全局,也很少回头复核自己改坏的东西。iterate 把这些收尾工作——逐项审查、分维度排查、修复、验证、再迭代——自动化,让 AI 真正像资深工程师一样把改动做完、做对。
61
58
 
62
- `iterate-plugin` 是 [iterate](https://github.com/jingzhao-l/iterate-skill) 项目在 [DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) 桌面客户端中的落地插件。它把 iterate 的开环审查闭环(review → triage → fix → validate → 收敛)直接带进 dsh 的界面:提供**自治闭环代码迭代**(normal 模式)与 **dry-run 纯多轮审查**(只读)两种能力。
59
+ `iterate-plugin` 是 [iterate](https://github.com/jingzhao-l/iterate-skill) 项目在 [DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) 桌面客户端中的落地插件。它把 iterate 的审查闭环(review → triage → fix → validate → 收敛)直接带进 dsh 的界面:提供**自治闭环代码迭代**(normal 模式)与 **dry-run 纯多轮审查**(只读)两种能力。
63
60
 
64
- 13 个纯函数工具外,还内置一套**免构建的 Web UI 层**(分诊面板、收敛看板、统计卡片、主题皮肤等),直接挂在 dsh 客户端的既有 UI 槽位上。配置方式(`iterate.config.yaml` 与审查维度)与迭代生态的另外两个组件([技能](https://github.com/jingzhao-l/iterate-skill) / [无头引擎](https://github.com/jingzhao-l/iterate-harness))完全一致,迁移零成本。
61
+ **v3.2 质量指挥中心**:插件已从"被动观察面板"升级为"主动指挥中心 + 知识库"。新增质量门禁读取与可写计算(compute)、经验银行(检索 / 采纳 / 新增 add)、防御事件流(记录 + 中英双语标签)、原生命令按钮与 `task_mode` 指示器。
65
62
 
66
- ## 特性
63
+ 17 个纯函数工具外,还内置一套**免构建的 Web UI 层**(收敛看板、分诊面板、统计卡片、10 标签页运行时观测台、主题皮肤等),直接挂在 dsh 客户端的既有 UI 槽位上。配置方式(`iterate.config.yaml` 与审查维度)与迭代生态的另外两个组件([技能](https://github.com/jingzhao-l/iterate-skill) / [无头引擎](https://github.com/jingzhao-l/iterate-harness))完全一致,迁移零成本。
64
+
65
+ ---
66
+
67
+ ## 📑 目录
68
+
69
+ - [✨ 特性](#-特性)
70
+ - [📦 安装](#-安装)
71
+ - [💬 使用](#-使用)
72
+ - [⚙️ 项目配置](#️-项目配置)
73
+ - [🔧 注册工具](#-注册工具)
74
+ - [📁 运行时产物布局](#-运行时产物布局)
75
+ - [🎨 设计](#-设计)
76
+ - [🧪 运行测试](#-运行测试)
77
+ - [⚠️ 免责声明与许可](#️-免责声明与许可)
78
+
79
+ ---
80
+
81
+ ## ✨ 特性
67
82
 
68
83
  ### 两种运行模式
69
84
 
70
- | 功能 | dry-run 模式 | normal 模式 |
71
- |------|-------------|------------|
72
- | 多轮收敛反复审查 | ✅ | ✅ |
73
- | 并行维度评审 | | |
74
- | 确定性聚合去重/排序 | ✅ | ✅ |
75
- | meta-review 报告一致性审计 | ✅ | ✅ |
76
- | 零文件修改(只读) | ✅ | ❌ |
77
- | 原子问题自动修复 | ❌ | ✅ |
78
- | 每轮修复后验证 | ❌ | ✅ |
79
- | 修复失败回滚 | | ✅ |
80
- | 达标自停 | ✅ | ✅ |
81
- | 只修改 atomic 问题,保留 architectural 留待后续 | ❌ | ✅ |
82
- | 断点保存 / 恢复(长迭代续跑) | ✅ | ✅ |
83
-
84
- ### 工具层
85
-
86
- - **13 个注册工具**:`iterate_config` / `iterate_validate` / `iterate_decision_log` / `iterate_context` / `iterate_review` / `iterate_triage` / `iterate_fix` / `iterate_diff` / `iterate_rollback` / `iterate_checkpoint` / `iterate_status` / `iterate_history` / `iterate_prune`
87
- - **findings 分诊闭环**:审查 → UI 分诊(y/n/a)→ `iterate_triage` 写回 `known_intentional` → 下一轮自动过滤
88
- - **结构化修复系统**:每次修复先备份、写注册表、记录 diff,验证失败可 `iterate_rollback` 还原
89
- - **断点续跑**:长迭代在每轮开头保存 checkpoint,中断后可恢复进度
90
- - **历史审计**:`iterate_history` 读取决策日志(按类型/时间/数量过滤)与修复注册表汇总,审查运行过程与修复明细
91
- - **运行时清理**:`iterate_prune` 清理过期的决策日志条目、陈旧断点、孤儿修复备份与空轮次;默认 dry-run 只报告不删除,显式 `dryRun:false` 才真正清理,每次清理写入决策日志
92
- - **配置读写**:`iterate_config` 支持带校验、备份、回滚的局部写入
93
-
94
- ### UI 层(客户端免构建槽位)
95
-
96
- | UI 组件 | 挂载槽位 | 功能 |
97
- |---------|---------|------|
98
- | 收敛看板 `ConvergenceDashboard` | `conversation.input.dock` | 输入框上方实时显示轮次进度条、严重度统计、维度徽章、趋势迷你图,normal 模式另显示修复计数徽章;并显示运行阶段芯片(当前工作流阶段 + 运行中/已结束) |
99
- | 运行时观测台 `ObservatoryPanel` | `conversation.input.dock` | 输入框下方七个标签页:实时活动流(支持按活动类型筛选)、审查线程(支持全部展开/全部收起)、收敛趋势、发现定位(支持按严重度/维度/关键词筛选)、修复与回滚、断点恢复、决策时间线(支持按类型/轮次筛选与关键词搜索);支持一键导出全部观测数据为 JSON(优先下载,失败回退复制) |
100
- | Findings 分诊面板 `TriagePanel` | `conversation.chat.turnTail` | 逐条 y/n/a 判定,支持筛选、批量(含一键全选所有 findings)、键盘快捷键、localStorage 持久化、复制 YAML/应用指令 |
101
- | 收敛统计卡片 `StatsCard` | `conversation.chat.turnTail` | 无 findings 时显示收敛统计、历史轮次表、趋势图、完成摘要 |
102
- | iterate 主题皮肤 | `theme.overrideTokens` | 暖琥珀配色的 13 个 dsw token 覆盖,明暗双模式,可在设置页开关 |
103
- | 进度胶囊 `ProgressCapsule` | `shell.overlay` | 每轮完成/收敛时右下角弹出通知(含收敛确认) |
104
- | iterate 设置区 `SettingsPanel` | `settings.section` | 主题开关、分诊持久化说明、配置管理指引、运行时状态概览(产物布局 + 查看/清理工具指引)、一键清空分诊数据 |
85
+ 插件与生态其他组件运行同一套 iterate 引擎,要么完全只读、要么作为自治修复闭环:
86
+
87
+ - **dry-run(只读)** 反复多维度审查直到收敛,**零文件修改**。
88
+ - **normal(自治)** 审查 原子修复 → 每轮验证 → 失败回滚 → 循环直到收敛。
89
+
90
+ 两种模式共通:
91
+
92
+ - 多轮收敛反复审查
93
+ - 并行维度评审
94
+ - 确定性聚合 / 去重 / 排序
95
+ - meta-review 报告一致性审计
96
+ - 达标自停
97
+ - 断点保存 / 恢复(长迭代续跑)
98
+
99
+ `normal` 模式额外提供:
100
+
101
+ - **只修改 atomic 问题**(architectural 问题保留待后续批准)
102
+ - 每轮修复后验证
103
+ - 修复失败自动回滚
104
+
105
+ ### UI 层(客户端免构建槽位,v3.2:10 个标签页)
106
+
107
+ - **收敛看板 `ConvergenceDashboard`**(`conversation.input.dock`)— 输入框上方实时显示轮次进度条、严重度统计、维度徽章、趋势迷你图;normal 模式另显示修复计数徽章;还有运行阶段芯片(当前工作流阶段 + 运行中/已结束)与 **v3.2 task_mode 指示器(code / iterate)**。
108
+ - **运行时观测台 `ObservatoryPanel`**(`conversation.input.dock`)— 输入框下方**十个标签页**运行时观测台:实时活动流(支持按活动类型筛选)、审查线程(支持全部展开/全部收起)、收敛趋势、发现定位(支持按严重度/维度/关键词筛选)、修复与回滚、断点恢复、决策时间线(支持按类型/轮次筛选与关键词搜索)。**v3.2 新增标签:质量门禁(F8)、经验银行(F9)、防御事件(F10)**;支持一键导出全部观测数据为 JSON(优先下载,失败回退复制)。
109
+ - **Findings 分诊面板 `TriagePanel`**(`conversation.chat.turnTail`)— 逐条 y/n/a 判定,支持筛选、批量(含一键全选所有 findings)、键盘快捷键、localStorage 持久化、复制 YAML / 应用指令。**v3.2:原生命令按钮**(批准架构修复、触发新一轮、回滚到断点)。
110
+ - **收敛统计卡片 `StatsCard`**(`conversation.chat.turnTail`)— 无 findings 时显示收敛统计、历史轮次、趋势图、完成摘要。
111
+ - **iterate 主题皮肤**(`theme.overrideTokens`)— 暖琥珀配色的 13 `--dsw-*` token 覆盖,明暗双模式,可在设置页开关。
112
+ - **进度胶囊 `ProgressCapsule`**(`shell.overlay`)— 每轮完成 / 收敛时右下角弹出通知(含收敛确认)。
113
+ - **iterate 设置区 `SettingsPanel`**(`settings.section`)— 主题开关、分诊持久化说明、配置管理指引、运行时状态概览(产物布局 + 查看/清理工具指引)、一键清空分诊数据。
105
114
 
106
115
  UI 层为**防御式设计**:`slots` / `theme` / `React` 任一不可用时自动降级,不会崩溃客户端。
107
116
 
108
- ## 安装
117
+ ### 工具之外的闭环行为
118
+
119
+ 除 17 个注册工具(完整参考见下文)外,插件还端到端打通了几条关键闭环:
120
+
121
+ - **findings 分诊闭环** — 审查 → UI 分诊(y/n/a)→ `iterate_triage` 写回 `known_intentional` → 下一轮自动过滤
122
+ - **结构化修复系统** — 每次修复先备份、写注册表、记录 diff,验证失败可 `iterate_rollback` 还原
123
+ - **断点续跑** — 长迭代在每轮开头保存 checkpoint,中断后可恢复进度
124
+ - **历史审计** — `iterate_history` 读取决策日志(按类型/时间/数量过滤)与修复注册表汇总,审查运行过程与修复明细
125
+ - **运行时清理** — `iterate_prune` 清理过期的决策日志条目、陈旧断点、孤儿修复备份与空轮次;默认 dry-run 只报告不删除,显式 `dryRun:false` 才真正清理,每次清理写入决策日志
126
+ - **配置读写** — `iterate_config` 支持带校验、备份、回滚的局部写入
127
+ - **v3.2 经验银行** — `iterate_experience` 以检索 / 过滤 / 采纳查阅历史修复与模式,并可 `add` 持久化新的已验证修复——重复添加同一 pattern + dimension 时累加命中次数而非重复写入
128
+ - **v3.2 质量门禁** — `iterate_quality_gate` 读取质量门禁状态(各维度收敛率 + PASS/FAIL),并可基于本轮 findings / 验证结果 `compute` 重新计算并持久化一份新的质量凭证(收敛率来自 `findingsByRound` 的真实收敛序列)
129
+ - **v3.2 防御事件流** — `iterate_defense_events` 查询防御事件(前置条件失败、回滚、不变量违反、假设证伪),并可 `record` 记录新事件;可读标签跟随项目语言(en / zh)
130
+
131
+ ---
132
+
133
+ ## 📦 安装
109
134
 
110
135
  ### 从 npm 安装
111
136
 
@@ -143,11 +168,13 @@ pnpm add /path/to/iterate-skill/harness/iterate-plugin
143
168
 
144
169
  > 插件包自带 `dsh.bundle.patch`(即 `cordis.patch.yml`),npm 包内 `files` 已白名单化(`src` / `lib` / `dist` / `cordis.patch.yml` / `README.md` / `LICENSE`)。其中 `dist/` 为 TypeScript 服务端逻辑的编译产物,随包分发以兼容 dsh 的 `github:owner/repo#ref` git-clone 安装方式(Node 不擦除 `node_modules` 下的 TS 类型)。
145
170
 
146
- ## 使用
171
+ ---
172
+
173
+ ## 💬 使用
147
174
 
148
175
  ### dry-run 模式(纯反复审查,不修改文件)
149
176
 
150
- 当你想要 "只是反复审查,不修改文件",prompt 示例:
177
+ 当你想要"只是反复审查,不修改文件",prompt 示例:
151
178
 
152
179
  ```
153
180
  dry-run review this project, find all issues across all dimensions
@@ -162,7 +189,7 @@ dry-run review this project, find all issues across all dimensions
162
189
 
163
190
  ### normal 模式(自治闭环迭代)
164
191
 
165
- 当你想要 "iterate this project / fix the issues found",prompt 示例:
192
+ 当你想要"iterate this project / fix the issues found",prompt 示例:
166
193
 
167
194
  ```
168
195
  iterate on this project, fix all atomic issues
@@ -174,7 +201,9 @@ iterate on this project, fix all atomic issues
174
201
  2. `loop` → 并行评审 → 聚合去重 → 原子问题并行修复 → 执行验证命令 → 验证失败则回滚 → 记录日志 → 无新问题则停止
175
202
  3. `report` → 输出修复统计
176
203
 
177
- ## 项目配置
204
+ ---
205
+
206
+ ## ⚙️ 项目配置
178
207
 
179
208
  在项目根目录放 `iterate.config.yaml`:
180
209
 
@@ -212,25 +241,31 @@ validation:
212
241
 
213
242
  > 配置可通过 `iterate_config` 工具读取与**校验式局部写入**(自动备份,写入失败自动回滚)。
214
243
 
215
- ## 注册工具(13 个)
216
-
217
- | 工具 | 功能 |
218
- |------|------|
219
- | `iterate_config` | 读取 / 写入 `iterate.config.yaml`。`operation=read` 返回完整配置或指定 section;`operation=write` 做 schema 校验、备份后局部合并写入,失败自动回滚 |
220
- | `iterate_validate` | 运行白名单验证命令,返回结果 |
221
- | `iterate_decision_log` | 追加决策日志(只追加,不改旧),存储于 `.iterate/decision-log.jsonl` |
222
- | `iterate_context` | 读取 `SKILL.md` / `ITERATE.md` 上下文 |
223
- | `iterate_review` | 确定性评审引擎:`plan` 生成计划,`aggregate` 聚合去重 + 收敛统计,`meta-review` 审计报告一致性。纯计算,不触碰文件系统 |
224
- | `iterate_triage` | 管理 `personalization.known_intentional`:`apply` 校验、去重(file\|dimension\|line)、备份后写回配置;`list` 读回当前条目。是浏览器分诊面板写回配置的唯一通道 |
225
- | `iterate_fix` | 应用**一个原子修复**:校验相对路径、备份原文件、按 `atomic.max_lines` 强制原子性(可 `force` 跳过)、写入新内容、记录 FixRecord 与 `atomic_fix` 日志。normal 模式唯一合法的改文件入口 |
226
- | `iterate_diff` | 查看修复累积变更:指定 `file` 返回相对首个备份的 unified diff;省略则返回每个已修复文件的汇总 |
227
- | `iterate_rollback` | 回滚一个已应用的修复:从备份还原文件、从注册表移除该 FixRecord、追加 `revert` 日志。用于某轮验证失败后 |
228
- | `iterate_checkpoint` | 迭代断点:`save` 保存当前进度到 `.iterate/checkpoint.json`,`load` 读回,`clear` 清除。长迭代可中断续跑 |
229
- | `iterate_status` | 汇总当前迭代状态:模式、当前轮/总轮、已修复数、剩余 architectural、决策日志条数、是否存在 checkpoint |
230
- | `iterate_history` | 读取迭代历史(只读):决策日志条目(可按 `type` / `since` / `limit` 过滤,默认取最新 50 条,上限 200 条)+ 修复注册表汇总(各轮 fixed/failed 计数)。用于审查运行过程、审计日志、盘点修复 |
231
- | `iterate_prune` | 清理运行时产物:过期决策日志条目(按 `retainDays`,默认 30 天)、陈旧断点、孤儿修复备份、空轮次。默认 dry-run 只报告不删除;`dryRun:false` 才真正清理,每次清理写入决策日志 |
232
-
233
- ## 运行时产物布局
244
+ ---
245
+
246
+ ## 🔧 注册工具(v3.2:17 个)
247
+
248
+ - `iterate_config` 读取 / 写入 `iterate.config.yaml`。`operation=read` 返回完整配置或指定 section;`operation=write` 做 schema 校验、备份后局部合并写入,失败自动回滚
249
+ - `iterate_validate` 运行白名单验证命令,返回结果
250
+ - `iterate_decision_log` 追加决策日志(只追加,不改旧),存储于 `.iterate/decision-log.jsonl`
251
+ - `iterate_context` 读取 `SKILL.md` / `ITERATE.md` 上下文
252
+ - `iterate_review` 确定性评审引擎:`plan` 生成计划,`aggregate` 聚合去重 + 收敛统计,`meta-review` 审计报告一致性。纯计算,不触碰文件系统
253
+ - `iterate_triage` 管理 `personalization.known_intentional`:`apply` 校验、去重(file|dimension|line)、备份后写回配置;`list` 读回当前条目。是浏览器分诊面板写回配置的唯一通道
254
+ - `iterate_fix` 应用**一个原子修复**:校验相对路径、备份原文件、按 `atomic.max_lines` 强制原子性(可 `force` 跳过)、写入新内容、记录 FixRecord 与 `atomic_fix` 日志。normal 模式唯一合法的改文件入口
255
+ - `iterate_diff` 查看修复累积变更:指定 `file` 返回相对首个备份的 unified diff;省略则返回每个已修复文件的汇总
256
+ - `iterate_rollback` 回滚一个已应用的修复:从备份还原文件、从注册表移除该 FixRecord、追加 `revert` 日志。用于某轮验证失败后
257
+ - `iterate_checkpoint` 迭代断点:`save` 保存当前进度到 `.iterate/checkpoint.json`,`load` 读回,`clear` 清除。长迭代可中断续跑
258
+ - `iterate_status` 汇总当前迭代状态:模式、当前轮/总轮、已修复数、剩余 architectural、决策日志条数、是否存在 checkpoint
259
+ - `iterate_history` 读取迭代历史(只读):决策日志条目(可按 `type` / `since` / `limit` 过滤,默认取最新 50 条,上限 200 条)+ 修复注册表汇总(各轮 fixed/failed 计数)。用于审查运行过程、审计日志、盘点修复
260
+ - `iterate_prune` 清理运行时产物:过期决策日志条目(按 `retainDays`,默认 30 天)、陈旧断点、孤儿修复备份、空轮次。默认 dry-run 只报告不删除;`dryRun:false` 才真正清理,每次清理写入决策日志
261
+ - `iterate_transcript` — 运行时观测台:把审查转录、线程、修复与 nudge 指令持久化到 `.iterate/transcript.json`,供客户端观测台读取
262
+ - `iterate_experience` — **v3.2** 查询经验银行(list / search / get),或 `add` 一条新的已验证修复:重复添加同一 pattern + dimension 累加命中次数而非重复写入。持久化到 `.iterate/experience.json`
263
+ - `iterate_quality_gate` — **v3.2** 读取质量凭证(`read`),或基于 findings、验证结果、`findingsByRound` 与 `fixedByDimension` 重新计算并持久化一份新凭证(`compute`)。真实的逐维度收敛率
264
+ - `iterate_defense_events` — **v3.2** 查询防御事件(list / counts),或 `record` 记录一条新事件。可读标签跟随项目语言(en / zh)
265
+
266
+ ---
267
+
268
+ ## 📁 运行时产物布局
234
269
 
235
270
  所有运行时状态都落在项目根目录的 `.iterate/` 下(可由 `.gitignore` 排除):
236
271
 
@@ -238,23 +273,33 @@ validation:
238
273
  .iterate/
239
274
  decision-log.jsonl # 追加式决策日志(plan/review/fix/revert…)
240
275
  checkpoint.json # 迭代断点(断点续跑)
276
+ transcript.json # 运行时观测台清单(各 reviewer 线程、趋势、修复、时间线、nudge)
277
+ transcript-live.ndjson # 追加式近实时 reviewer 活动流(read/fix/rollback/validate…),带字节上限
278
+ experience.json # v3.2:经验银行(跨会话积累的历史修复与模式)
279
+ quality-gate.json # v3.2:质量门禁快照(维度收敛率、验证通过率、PASS/FAIL)
280
+ defense-events.json # v3.2:防御事件流(前置条件失败、回滚、不变量违反、假设证伪)
241
281
  fixes/
242
282
  registry.json # 修复注册表(FixRecord 列表,按轮次组织)
243
283
  <fix-id>_<ts>.bak # 每次修复前的原文件备份
244
284
  ```
245
285
 
246
- ## 设计
286
+ ---
287
+
288
+ ## 🎨 设计
247
289
 
248
290
  插件遵循 dsh "everything-is-a-plugin" 架构:
249
291
 
250
- - **只做两件事**:注入系统 prompt 教模型写 iterate workflow + 注册 13 个纯函数工具
292
+ - **只做两件事** — 注入系统 prompt 教模型写 iterate workflow + 注册 17 个纯函数工具
251
293
  - **所有 orchestration 通过 dsh 原生 `workflow` + `agent` + `parallel` 完成**
252
- - **核心逻辑全部纯函数**(去重/过滤/排序/收敛/meta-audit/diff 计算/历史过滤/清理报告),可单元测试,无 I/O
253
- - **安全模型**:文件写入限定在解析后的项目根目录内(路径遍历防护);写文件前必备份,失败回滚;配置写入同样备份 + 回滚;`iterate_prune` 默认 dry-run、只清理 `.iterate/` 下产物、每次清理写日志;`iterate_fix` 对 content 设字符上限、`iterate_triage` 对 entries 设数量上限,防止异常超大负载
254
- - **UI 免构建**:`lib/client.js` 用 `React.createElement` 树 + 注入 `<style>` 标签,全部颜色走 `--dsw-*` 令牌,缺服务自动降级
294
+ - **核心逻辑全部纯函数**(去重 / 过滤 / 排序 / 收敛 / meta-audit / diff 计算 / 历史过滤 / 清理报告),可单元测试,无 I/O
295
+ - **安全模型** — 文件写入限定在解析后的项目根目录内(路径遍历防护);写文件前必备份,失败回滚;配置写入同样备份 + 回滚;`iterate_prune` 默认 dry-run、只清理 `.iterate/` 下产物、每次清理写日志;`iterate_fix` 对 content 设字符上限、`iterate_triage` 对 entries 设数量上限,防止异常超大负载
296
+ - **UI 免构建** — `lib/client.js` 用 `React.createElement` 树 + 注入 `<style>` 标签,全部颜色走 `--dsw-*` 令牌,缺服务自动降级
297
+ - **v3.2 质量指挥中心** — 把插件从"被动观察面板"升级为"主动指挥中心 + 知识库"——质量门禁(读 + compute)、经验银行(读 + add)、防御事件(读 + record)与原生指挥按钮
255
298
  - 遵循 iterate 原技能的设计原则:确定性收敛,可审计,最小权限
256
299
 
257
- ## 运行测试
300
+ ---
301
+
302
+ ## 🧪 运行测试
258
303
 
259
304
  ```bash
260
305
  cd harness/iterate-plugin
@@ -265,10 +310,14 @@ npm test
265
310
 
266
311
  所有测试通过:
267
312
 
268
- - **212 个单元测试全绿**,类型检查通过
269
- - 覆盖:去重、过滤、排序、多轮收敛、meta-review 审计、路径安全、超时钳制、配置读写与回滚、triage 合并、diff 计算、checkpoint 校验、修复注册表、历史读取与过滤、prune 清理报告与 dry-run 语义、UI 纯函数(select-all 键、运行时状态指引)等
313
+ - **466 个单元测试全绿**,类型检查通过
314
+ - 覆盖:去重、过滤、排序、多轮收敛、meta-review 审计、路径安全、超时钳制、配置读写与回滚、triage 合并、diff 计算、checkpoint 校验、修复注册表、历史读取与过滤、prune 清理报告与 dry-run 语义、UI 纯函数(select-all 键、运行时状态指引)、**v3.2:经验银行、质量门禁、防御事件、审批门禁 fail-open 路径**等
315
+
316
+ ---
317
+
318
+ ## ⚠️ 免责声明与许可
270
319
 
271
- ## ⚠️ 免责声明
320
+ ### 免责声明
272
321
 
273
322
  本项目按「现状」(AS IS)提供,不附带任何明示或暗示的担保,包括但不限于对适销性、特定用途适用性及不侵权性的担保。
274
323
 
@@ -281,6 +330,6 @@ npm test
281
330
 
282
331
  使用者需为本项目使用过程中所产生、修改或提交的代码负全部责任。使用本项目即表示你同意:维护者与贡献者不对因使用本项目而导致的任何损失、损害或法律后果承担责任。
283
332
 
284
- ## License
333
+ ### License
285
334
 
286
335
  MIT
@@ -46,12 +46,26 @@ function describe(toolName, arguments0) {
46
46
  * deployments, and for any non-iterate tool.
47
47
  */
48
48
  export function decideApproval(execution, policy) {
49
- const name = typeof execution?.name === 'string' ? execution.name : '';
49
+ // Defensive reads: a hostile/proxied execution object must degrade to "not
50
+ // our tool" (allow) rather than throw inside the gate.
51
+ let name = '';
52
+ try {
53
+ name = typeof execution?.name === 'string' ? execution.name : '';
54
+ }
55
+ catch {
56
+ name = '';
57
+ }
50
58
  if (!name)
51
59
  return { kind: 'allow' };
52
60
  if (!DESTRUCTIVE_TOOLS.has(name))
53
61
  return { kind: 'allow' };
54
- const rawArgs = execution.arguments;
62
+ let rawArgs;
63
+ try {
64
+ rawArgs = execution.arguments;
65
+ }
66
+ catch {
67
+ rawArgs = undefined;
68
+ }
55
69
  const args = rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
56
70
  ? rawArgs
57
71
  : {};
@@ -73,20 +87,3 @@ export function decideApproval(execution, policy) {
73
87
  export function isDestructiveIterateTool(name) {
74
88
  return typeof name === 'string' && DESTRUCTIVE_TOOLS.has(name);
75
89
  }
76
- /**
77
- * Evaluate an iterate tool's own boundary gate for a destructive call.
78
- * `approvedArg` is the caller-supplied `approved: true` flag (human consent
79
- * already obtained). Returns a run / refuse result without any I/O.
80
- */
81
- export function toolGate(policy, execution, approvedArg) {
82
- const decision = decideApproval(execution, policy);
83
- if (decision.kind === 'allow')
84
- return { ok: true };
85
- if (decision.kind === 'deny') {
86
- return { ok: false, error: `Blocked by observatory approval policy: ${decision.reason}` };
87
- }
88
- // ask
89
- if (approvedArg === true)
90
- return { ok: true };
91
- return { ok: false, requiresApproval: true, reason: decision.reason };
92
- }
@@ -191,6 +191,11 @@ export function validateConfig(config) {
191
191
  */
192
192
  export function resolveProjectRoot(input, sessionCwd) {
193
193
  const raw = (input ?? '').trim();
194
+ // A NUL byte can never name a real path and makes `resolve()` (and every
195
+ // downstream fs call) throw — treat it as unsafe input, not a throw path.
196
+ if (raw.includes('\0')) {
197
+ return { ok: false, reason: 'Refusing project root containing NUL bytes.' };
198
+ }
194
199
  const root = raw ? resolve(raw) : resolve(effectiveCwd(sessionCwd));
195
200
  if (!root || root === sep) {
196
201
  return { ok: false, reason: 'Refusing filesystem root as project root.' };
package/dist/index.js CHANGED
@@ -2,13 +2,16 @@
2
2
  * iterate-plugin — dsh plugin for the iterate autonomous closed-loop workflow
3
3
  *
4
4
  * Architecture:
5
- * - The plugin registers 14 tools (config, validate, decision-log, context, review,
6
- * triage, fix, diff, rollback, checkpoint, status, history, prune, transcript)
5
+ * - The plugin registers 17 tools (14 original + 3 v3.0 quality command center tools)
6
+ * Original: config, validate, decision-log, context, review, triage, fix, diff,
7
+ * rollback, checkpoint, status, history, prune, transcript
8
+ * v3.0: experience, quality_gate, defense_events
7
9
  * - The plugin injects a system prompt section teaching the iterate workflow pattern
8
10
  * - The model (prompted by the skill) writes a workflow script using dsh's `workflow` tool
9
11
  * - The workflow script uses `agent()` / `parallel()` / `phase()` / `log()` to orchestrate
10
- * - Subagents use the 14 tools to do real work (read config, run validation, log decisions,
11
- * review, triage, apply/rollback/fixing, checkpoint, status, history, prune, transcript)
12
+ * - Subagents use the 17 tools to do real work (read config, run validation, log decisions,
13
+ * review, triage, apply/rollback/fixing, checkpoint, status, history, prune, transcript,
14
+ * query experience bank, check quality gates, query defense events)
12
15
  * - A `tools/pre-execute` hook gates destructive iterate calls behind human approval
13
16
  * (F8 observatory approval policy: ask / deny / allow).
14
17
  *
@@ -20,7 +23,7 @@
20
23
  *
21
24
  * Key files:
22
25
  * - src/index.ts — Plugin entry: register tools + inject skill prompt
23
- * - src/tools/ — 13 tool implementations + meta-review/review engines
26
+ * - src/tools/ — 17 tool implementations (14 original + 3 v3.0)
24
27
  * - src/config-loader.ts — YAML config loading
25
28
  * - src/types.ts — Shared types
26
29
  */
@@ -35,13 +38,16 @@ import { registerCheckpointTool, registerStatusTool } from "./tools/checkpoint.j
35
38
  import { registerHistoryTool } from "./tools/history.js";
36
39
  import { registerPruneTool } from "./tools/prune.js";
37
40
  import { registerTranscriptTool } from "./tools/transcript.js";
41
+ import { registerExperienceBankTool } from "./tools/experience-bank.js";
42
+ import { registerQualityGateTool } from "./tools/quality-gate.js";
43
+ import { registerDefenseEventsTool } from "./tools/defense-events.js";
38
44
  import { registerSessionHooks } from "./session-hooks.js";
39
45
  import { registerLiveCapture } from "./live.js";
40
46
  import { ITERATE_SKILL_PROMPT } from "./skill-prompt.js";
41
47
  export const name = 'iterate-plugin';
42
48
  export const inject = ['tools', 'systemPrompt'];
43
49
  export function apply(ctx) {
44
- // 1. Register the 14 tools
50
+ // 1. Register the 17 tools (14 original + 3 v3.0)
45
51
  registerConfigTool(ctx);
46
52
  registerValidateTool(ctx);
47
53
  registerDecisionLogTool(ctx);
@@ -56,6 +62,10 @@ export function apply(ctx) {
56
62
  registerHistoryTool(ctx);
57
63
  registerPruneTool(ctx);
58
64
  registerTranscriptTool(ctx);
65
+ // v3.0: Quality Command Center tools
66
+ registerExperienceBankTool(ctx);
67
+ registerQualityGateTool(ctx);
68
+ registerDefenseEventsTool(ctx);
59
69
  // 2. Wire the observatory approval gate onto dsh's tools/pre-execute waterfall,
60
70
  // and the live reviewer-activity feed onto tools/result.
61
71
  registerSessionHooks(ctx);
@@ -32,17 +32,36 @@ import { decideApproval, isDestructiveIterateTool } from "./approval-gate.js";
32
32
  * Returns a dsh `PreToolDecision` so the caller can short-circuit the caller.
33
33
  */
34
34
  export function gateDecision(exec) {
35
- // Importing the decision, and only inspecting our own tools, keeps unrelated
36
- // tooling untouched. Anything we cannot classify is allowed by default.
37
- if (!isDestructiveIterateTool(exec.name))
35
+ // Defensively read the tool name: an exec handed to the waterfall is an
36
+ // ordinary object, but a hostile/proxied exec must degrade to "not our tool"
37
+ // (allow) instead of throwing before classification. The gate only ever
38
+ // inspects iterate tools, so an unreadable name also must not alter
39
+ // unrelated tooling.
40
+ let name = '';
41
+ try {
42
+ name = exec?.name ?? '';
43
+ }
44
+ catch {
45
+ name = '';
46
+ }
47
+ if (!isDestructiveIterateTool(name))
38
48
  return { kind: 'allow' };
39
49
  // Resolve the project root (use the call's own `path` arg, else the agent's
40
50
  // session cwd) to read the effective observatory policy.
41
- const argPath = typeof exec.arguments === 'object' && exec.arguments && !Array.isArray(exec.arguments)
42
- && typeof exec.arguments.path === 'string'
43
- ? exec.arguments.path
44
- : undefined;
45
- const sessionCwd = exec.agent?.session?.header?.cwd;
51
+ let argPath;
52
+ let sessionCwd;
53
+ try {
54
+ const args = exec?.arguments;
55
+ if (args && typeof args === 'object' && !Array.isArray(args)) {
56
+ const p = args.path;
57
+ if (typeof p === 'string')
58
+ argPath = p;
59
+ }
60
+ sessionCwd = exec?.agent?.session?.header?.cwd;
61
+ }
62
+ catch {
63
+ // hostile/proxied exec — fall through with both undefined (defaults to ask)
64
+ }
46
65
  const resolved = resolveProjectRoot(argPath, sessionCwd);
47
66
  let policy = 'ask';
48
67
  if (resolved.ok) {
@@ -67,13 +86,19 @@ export function gateDecision(exec) {
67
86
  */
68
87
  export function registerSessionHooks(ctx) {
69
88
  ctx.on('tools/pre-execute', (exec, next) => {
70
- // Never let a throwing gate break the pipeline degrade to allow.
89
+ // Fail-safe: a throwing gate must never fail OPEN. Degrade to `ask` so a
90
+ // destructive call still routes through human consent instead of running
91
+ // via `next()`'s allow default (matches the header's documented contract).
71
92
  let decision;
72
93
  try {
73
94
  decision = gateDecision(exec);
74
95
  }
75
- catch {
76
- return next();
96
+ catch (err) {
97
+ console.warn('[iterate] approval gate failed; degrading to ask.', err);
98
+ return Promise.resolve({
99
+ kind: 'ask',
100
+ reason: 'iterate approval gate unavailable — require consent',
101
+ });
77
102
  }
78
103
  if (decision.kind === 'ask') {
79
104
  // Delegate the actual human-consent prompt + audit to dsh's approval
@@ -23,6 +23,9 @@ You have the iterate plugin installed, which registers these tools:
23
23
  - \`iterate_history\` — inspect the runtime state in detail: decision-log entries and applied fixes (optionally scoped to a round or a fixed file)
24
24
  - \`iterate_prune\` — remove stale runtime artifacts (\`.iterate/\` entries). Defaults to a read-only dry-run that reports what WOULD be removed; pass \`dryRun:false\` to actually prune.
25
25
  - \`iterate_transcript\` — runtime observatory file (\`.iterate/transcript.json\`). \`read\` fetches the persisted manifest including any steering \`nudge\` for this run's reviewers; \`capture\` (call once after the final report) persists the per-reviewer threads, convergence trend, findings, fixes, checkpoint, and timeline so the client observatory panel reflects the run; \`nudge\` sets/clears steering text the next round's reviewers read. Purely local, never touches source files.
26
+ - \`iterate_experience\` — experience bank (\`.iterate/experience.json\`): \`list\`/\`search\`/\`get\` recall verified fixes and patterns from past sessions (read the bank before fixing so proven fixes are applied first); \`add\` records a new verified fix — re-adding the same pattern+dimension bumps its hit count instead of duplicating it.
27
+ - \`iterate_quality_gate\` — quality certificate: \`read\` loads the persisted dimension convergence rates / verification pass rate / PASS-FAIL status; \`compute\` recomputes a fresh snapshot from this round's findings + validation results (supply \`findingsByRound\` for real convergence) and persists it to \`.iterate/quality-gate.json\`.
28
+ - \`iterate_defense_events\` — defense event stream (\`.iterate/defense-events.json\`): \`list\`/\`counts\` review precondition failures, rollbacks, invariant violations, and falsified assumptions; \`record\` logs a new event when a defense fires. Human-readable labels follow the project \`language\` (en/zh).
26
29
 
27
30
  ### When to use
28
31
  When the user asks to review or iterate on the project (e.g. "review this project", "iterate on error handling", "check the codebase for issues", "dry-run review", "反复审查"), run an iterate **workflow** by calling the \`workflow\` tool.
@@ -11,11 +11,11 @@
11
11
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
12
12
  import { defineTool } from '@deepseek-ai/dsh-tools';
13
13
  import { resolveProjectRootForExec } from "../config-loader.js";
14
- import { checkpointPath, iterateDir } from "../paths.js";
14
+ import { checkpointPath, iterateDir, transcriptPath } from "../paths.js";
15
15
  import { readRegistry } from "./fix.js";
16
16
  import { readDecisionEntries } from "./decision-log.js";
17
17
  // ─── Pure helpers (exported for unit tests) ─────────────────────────────────
18
- /** Read a checkpoint from disk (missing/corrupt → null). */
18
+ /** Read the current checkpoint from disk (missing/corrupt → null). */
19
19
  export function readCheckpoint(projectRoot) {
20
20
  const file = checkpointPath(projectRoot);
21
21
  if (!existsSync(file))
@@ -34,6 +34,20 @@ export function readCheckpoint(projectRoot) {
34
34
  return null;
35
35
  }
36
36
  }
37
+ /** Read the harness task_mode from the persisted observatory transcript (code|iterate|null). */
38
+ export function readTranscriptTaskMode(projectRoot) {
39
+ const file = transcriptPath(projectRoot);
40
+ if (!existsSync(file))
41
+ return null;
42
+ try {
43
+ const parsed = JSON.parse(readFileSync(file, 'utf-8'));
44
+ const m = parsed && typeof parsed === 'object' ? parsed.taskMode : null;
45
+ return m === 'code' || m === 'iterate' ? m : null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
37
51
  /** Validate a checkpoint payload (returns error string or null). */
38
52
  export function validateCheckpoint(input) {
39
53
  if (input.mode !== 'dry-run' && input.mode !== 'normal') {
@@ -63,6 +77,7 @@ export function validateCheckpoint(input) {
63
77
  */
64
78
  export function computeStatus(input) {
65
79
  const checkpoint = input.checkpoint;
80
+ const taskMode = input.taskMode ?? null;
66
81
  const entries = input.decisionEntries;
67
82
  const registry = input.fixRegistry;
68
83
  const lastEntry = entries.length > 0 ? entries[entries.length - 1] : null;
@@ -85,6 +100,7 @@ export function computeStatus(input) {
85
100
  const architecturalCount = checkpoint?.architecturalCount ?? 0;
86
101
  return {
87
102
  mode: checkpoint?.mode ?? null,
103
+ taskMode,
88
104
  currentRound,
89
105
  totalRounds,
90
106
  fixedCount,
@@ -225,6 +241,7 @@ export function registerStatusTool(ctx) {
225
241
  properties: {
226
242
  ok: { type: 'boolean', required: true },
227
243
  mode: { oneOf: [{ type: 'string' }, { type: 'null' }] },
244
+ taskMode: { oneOf: [{ type: 'string' }, { type: 'null' }], description: 'Harness execution mode from the observatory transcript ("code" | "iterate").' },
228
245
  currentRound: { type: 'integer' },
229
246
  totalRounds: { type: 'integer' },
230
247
  fixedCount: { type: 'integer' },
@@ -242,7 +259,7 @@ export function registerStatusTool(ctx) {
242
259
  if (!value.ok)
243
260
  return [{ type: 'text', text: `status failed: ${value.error}` }];
244
261
  const lines = [
245
- `Mode: ${value.mode ?? 'none'}`,
262
+ `Mode: ${value.mode ?? 'none'}${value.taskMode ? ` (${value.taskMode})` : ''}`,
246
263
  `Round: ${value.currentRound} / ${value.totalRounds}`,
247
264
  `Fixed: ${value.fixedCount} · Architectural remaining: ${value.architecturalCount}`,
248
265
  `Findings in checkpoint: ${value.findingsCount}`,
@@ -260,12 +277,14 @@ export function registerStatusTool(ctx) {
260
277
  const projectRoot = resolved.root;
261
278
  const status = computeStatus({
262
279
  checkpoint: readCheckpoint(projectRoot),
280
+ taskMode: readTranscriptTaskMode(projectRoot),
263
281
  decisionEntries: readDecisionEntries(projectRoot),
264
282
  fixRegistry: readRegistry(projectRoot),
265
283
  });
266
284
  return {
267
285
  ok: true,
268
286
  mode: status.mode ?? null,
287
+ taskMode: status.taskMode ?? null,
269
288
  currentRound: status.currentRound,
270
289
  totalRounds: status.totalRounds,
271
290
  fixedCount: status.fixedCount,
@@ -57,7 +57,7 @@ export function appendDecisionEntry(projectRoot, entry) {
57
57
  appendFileSync(filePath, line, 'utf-8');
58
58
  }
59
59
  catch (err) {
60
- return { count: -1, path: join(projectRoot, LOG_DIR, LOG_FILE), error: `failed to append decision log: ${String(err)}` };
60
+ return { count: 0, path: join(projectRoot, LOG_DIR, LOG_FILE), error: `failed to append decision log: ${String(err)}` };
61
61
  }
62
62
  // Count entries
63
63
  let count = 0;
@@ -202,6 +202,15 @@ export function registerDecisionLogTool(ctx) {
202
202
  data,
203
203
  };
204
204
  const result = appendDecisionEntry(projectRoot, entry);
205
+ if (result.error) {
206
+ return {
207
+ operation: 'append',
208
+ success: false,
209
+ entryCount: 0,
210
+ logPath: result.path,
211
+ error: result.error,
212
+ };
213
+ }
205
214
  return {
206
215
  operation: 'append',
207
216
  success: true,