iterate-plugin 3.2.1 → 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
@@ -87,20 +87,3 @@ export function decideApproval(execution, policy) {
87
87
  export function isDestructiveIterateTool(name) {
88
88
  return typeof name === 'string' && DESTRUCTIVE_TOOLS.has(name);
89
89
  }
90
- /**
91
- * Evaluate an iterate tool's own boundary gate for a destructive call.
92
- * `approvedArg` is the caller-supplied `approved: true` flag (human consent
93
- * already obtained). Returns a run / refuse result without any I/O.
94
- */
95
- export function toolGate(policy, execution, approvedArg) {
96
- const decision = decideApproval(execution, policy);
97
- if (decision.kind === 'allow')
98
- return { ok: true };
99
- if (decision.kind === 'deny') {
100
- return { ok: false, error: `Blocked by observatory approval policy: ${decision.reason}` };
101
- }
102
- // ask
103
- if (approvedArg === true)
104
- return { ok: true };
105
- return { ok: false, requiresApproval: true, reason: decision.reason };
106
- }
@@ -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,
@@ -213,7 +213,10 @@ export function registerDefenseEventsTool(ctx) {
213
213
  ...(typeof args.file === 'string' && args.file.length > 0 ? { file: args.file } : {}),
214
214
  ...(typeof args.line === 'number' ? { line: args.line } : {}),
215
215
  });
216
- writeDefenseEvents(projectRoot, next);
216
+ const write = writeDefenseEvents(projectRoot, next);
217
+ if (!write.ok) {
218
+ return { ok: false, kind: 'defense_events', operation: 'record', error: write.error };
219
+ }
217
220
  const event = next.events[next.events.length - 1];
218
221
  return {
219
222
  ok: true,
@@ -52,7 +52,12 @@ export function readDefenseEvents(projectRoot) {
52
52
  }
53
53
  return emptyStream();
54
54
  }
55
- /** Write the defense events stream to disk. */
55
+ /**
56
+ * Write the defense events stream to disk.
57
+ * Returns `{ ok: true }` on success or `{ ok: false, error }` when the write
58
+ * fails — a caller must surface the failure instead of reporting success for
59
+ * an event that was never persisted.
60
+ */
56
61
  export function writeDefenseEvents(projectRoot, stream) {
57
62
  const dirPath = path.join(projectRoot, '.iterate');
58
63
  const filePath = path.join(dirPath, DEFENSE_EVENTS_FILE);
@@ -62,9 +67,10 @@ export function writeDefenseEvents(projectRoot, stream) {
62
67
  }
63
68
  fs.writeFileSync(filePath, JSON.stringify(stream, null, 2), 'utf-8');
64
69
  }
65
- catch {
66
- // Silently fail - defense events are not critical
70
+ catch (err) {
71
+ return { ok: false, error: `unable to write ${filePath}: ${String(err)}` };
67
72
  }
73
+ return { ok: true };
68
74
  }
69
75
  /** Add a defense event to the stream. */
70
76
  export function addDefenseEvent(stream, event) {
@@ -186,7 +186,10 @@ export function registerExperienceBankTool(ctx) {
186
186
  }
187
187
  const bank = readExperienceBank(projectRoot);
188
188
  const { bank: next, added, entryId } = upsertExperience(bank, normalizeExperienceInput(raw));
189
- writeExperienceBank(projectRoot, next);
189
+ const write = writeExperienceBank(projectRoot, next);
190
+ if (!write.ok) {
191
+ return { ok: false, kind: 'experience', operation: 'add', error: write.error };
192
+ }
190
193
  const entry = next.entries.find((e) => e.id === entryId);
191
194
  return {
192
195
  ok: true,
@@ -30,7 +30,12 @@ export function readExperienceBank(projectRoot) {
30
30
  }
31
31
  return emptyBank();
32
32
  }
33
- /** Write the experience bank to disk. */
33
+ /**
34
+ * Write the experience bank to disk.
35
+ * Returns `{ ok: true }` on success or `{ ok: false, error }` when the write
36
+ * fails — a caller must surface the failure instead of reporting success for
37
+ * an entry that was never persisted.
38
+ */
34
39
  export function writeExperienceBank(projectRoot, bank) {
35
40
  const dirPath = path.join(projectRoot, '.iterate');
36
41
  const filePath = path.join(dirPath, EXPERIENCE_FILE);
@@ -40,9 +45,10 @@ export function writeExperienceBank(projectRoot, bank) {
40
45
  }
41
46
  fs.writeFileSync(filePath, JSON.stringify(bank, null, 2), 'utf-8');
42
47
  }
43
- catch {
44
- // Silently fail - experience bank is not critical
48
+ catch (err) {
49
+ return { ok: false, error: `unable to write ${filePath}: ${String(err)}` };
45
50
  }
51
+ return { ok: true };
46
52
  }
47
53
  /** Search experience entries by query string. */
48
54
  export function searchExperienceEntries(entries, query, opts = {}) {
@@ -160,7 +160,10 @@ export function registerQualityGateTool(ctx) {
160
160
  findingsByRound,
161
161
  fixedByDimension,
162
162
  });
163
- writeQualityGate(projectRoot, snapshot);
163
+ const write = writeQualityGate(projectRoot, snapshot);
164
+ if (!write.ok) {
165
+ return { ok: false, kind: 'quality_gate', operation: 'compute', error: write.error };
166
+ }
164
167
  return {
165
168
  ok: true,
166
169
  kind: 'quality_gate',
@@ -41,7 +41,12 @@ export function readQualityGate(projectRoot) {
41
41
  }
42
42
  return emptySnapshot();
43
43
  }
44
- /** Write the quality gate snapshot to disk. */
44
+ /**
45
+ * Write the quality gate snapshot to disk.
46
+ * Returns `{ ok: true }` on success or `{ ok: false, error }` when the write
47
+ * fails — a caller must surface the failure instead of reporting success for
48
+ * a snapshot that was never persisted.
49
+ */
45
50
  export function writeQualityGate(projectRoot, snapshot) {
46
51
  const dirPath = path.join(projectRoot, '.iterate');
47
52
  const filePath = path.join(dirPath, QUALITY_GATE_FILE);
@@ -51,9 +56,10 @@ export function writeQualityGate(projectRoot, snapshot) {
51
56
  }
52
57
  fs.writeFileSync(filePath, JSON.stringify(snapshot, null, 2), 'utf-8');
53
58
  }
54
- catch {
55
- // Silently fail - quality gate is not critical
59
+ catch (err) {
60
+ return { ok: false, error: `unable to write ${filePath}: ${String(err)}` };
56
61
  }
62
+ return { ok: true };
57
63
  }
58
64
  /**
59
65
  * Compute the convergence rate for a dimension.
@@ -124,6 +124,11 @@ export function registerTranscriptTool(ctx) {
124
124
  description: 'For `capture`: run mode ("dry-run" | "normal"). Default dry-run.',
125
125
  enum: ['dry-run', 'normal'],
126
126
  },
127
+ taskMode: {
128
+ type: 'string',
129
+ description: 'For `capture`: harness execution mode ("code" | "iterate"). Default derives from the review loop (iterate).',
130
+ enum: ['code', 'iterate'],
131
+ },
127
132
  goal: { type: 'string', description: 'For `capture`: run goal.' },
128
133
  maxRounds: { type: 'integer', description: 'For `capture`: round cap.' },
129
134
  roundsExecuted: { type: 'integer', description: 'For `capture`: number of rounds actually executed.' },
@@ -215,9 +220,10 @@ export function registerTranscriptTool(ctx) {
215
220
  }
216
221
  // capture
217
222
  const mode = args.mode === 'normal' ? 'normal' : 'dry-run';
223
+ const taskMode = args.taskMode === 'code' || args.taskMode === 'iterate' ? args.taskMode : undefined;
218
224
  const goal = typeof args.goal === 'string' ? args.goal : '';
219
225
  const maxRounds = typeof args.maxRounds === 'number' ? Math.floor(args.maxRounds) : 0;
220
- const builder = new ReviewTranscriptBuilder({ project: projectRoot, mode, approval, goal, maxRounds });
226
+ const builder = new ReviewTranscriptBuilder({ project: projectRoot, mode, taskMode, approval, goal, maxRounds });
221
227
  const report = args.report;
222
228
  const reportFindings = report && typeof report === 'object' && Array.isArray(report.findings)
223
229
  ? report.findings
@@ -283,6 +289,7 @@ function rehydrateBuilder(manifest, approval) {
283
289
  const builder = new ReviewTranscriptBuilder({
284
290
  project: manifest.project,
285
291
  mode: manifest.mode ?? null,
292
+ taskMode: manifest.taskMode ?? null,
286
293
  approval,
287
294
  goal: manifest.goal,
288
295
  maxRounds: manifest.maxRounds,
@@ -93,6 +93,7 @@ function mergeReportIntoThread(thread, findings, readFiles) {
93
93
  export class ReviewTranscriptBuilder {
94
94
  project;
95
95
  mode;
96
+ taskMode;
96
97
  approval;
97
98
  goal = '';
98
99
  phases = [];
@@ -111,6 +112,14 @@ export class ReviewTranscriptBuilder {
111
112
  this.project = input.project || '';
112
113
  this.mode =
113
114
  input.mode === 'dry-run' || input.mode === 'normal' ? input.mode : null;
115
+ // v3.0: task_mode indicator. An explicit valid value wins; otherwise a
116
+ // run that exercises the review loop (any mode) defaults to "iterate".
117
+ this.taskMode =
118
+ input.taskMode === 'code' || input.taskMode === 'iterate'
119
+ ? input.taskMode
120
+ : input.mode !== null && input.mode !== undefined
121
+ ? 'iterate'
122
+ : null;
114
123
  this.approval =
115
124
  input.approval === 'ask' || input.approval === 'deny' || input.approval === 'allow'
116
125
  ? input.approval
@@ -341,6 +350,7 @@ export class ReviewTranscriptBuilder {
341
350
  updatedAt: this.updatedAt,
342
351
  active: this.active,
343
352
  mode: this.mode,
353
+ taskMode: this.taskMode,
344
354
  goal: this.goal,
345
355
  phases: this.phases,
346
356
  round: this.round,