iterate-plugin 3.11.0 → 3.12.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.md CHANGED
@@ -61,7 +61,7 @@ dsh plugin --profile web add iterate-plugin
61
61
 
62
62
  **v3.2 Quality Command Center**: the plugin has grown from a "passive observation panel" into an "active command center + knowledge base". It now ships quality gate view + writable compute, an experience bank with search / adopt / add, a defense events stream (record + bilingual labels), native command buttons, and a `task_mode` indicator.
63
63
 
64
- Besides 17 pure-function tools, it carries a **build-free Web UI layer** (convergence dashboard, triage panel, stats card, 11-tab runtime observatory — live activity stream + F1–F10 —, theme skin, etc.) that plugs straight into dsh's existing UI slots. Configuration (`iterate.config.yaml` and the review dimensions) is identical across the other two components of the iterate ecosystem ([skill](https://github.com/jingzhao-l/iterate-skill) / [headless engine](https://github.com/jingzhao-l/iterate-harness)) — zero migration cost.
64
+ Besides 19 pure-function tools, it carries a **build-free Web UI layer** (convergence dashboard, triage panel, stats card, 12-tab runtime observatory — live activity stream + F1–F11 —, theme skin, etc.) that plugs straight into dsh's existing UI slots. Configuration (`iterate.config.yaml` and the review dimensions) is identical across the other two components of the iterate ecosystem ([skill](https://github.com/jingzhao-l/iterate-skill) / [headless engine](https://github.com/jingzhao-l/iterate-harness)) — zero migration cost.
65
65
 
66
66
  ---
67
67
 
@@ -104,10 +104,10 @@ Both modes share:
104
104
  - Validates after each round's fixes
105
105
  - Rolls back failed fixes, and can save / resume progress
106
106
 
107
- ### UI layer (build-free client slots, v3.7: 11 tabs, bilingual)
107
+ ### UI layer (build-free client slots, v3.12: 12 tabs, bilingual)
108
108
 
109
109
  - **ConvergenceDashboard** (`conversation.input.dock`) — live round progress bar, severity stats, dimension badges, trend mini-chart above the input; normal mode also shows fix-count badges; plus a live workflow-phase chip (current phase + running/stopped) and a **task_mode indicator (code / iterate)**, now backed by the persisted transcript (`iterate_status` / `iterate_transcript` emit `taskMode`). While a run is active the dashboard switches to a running state (round + phase) instead of the static empty state, and the start buttons are hidden.
110
- - **ObservatoryPanel** (`conversation.input.dock`) — an **eleven-tab** runtime observatory below the input: **live activity stream** (default tab, type filter) + **F1** review threads (expand / collapse all), **F2** convergence trend (incl. the per-round comparison table: findings raised / fixed / severity mix / validation outcomes), **F3** finding locations (severity / dimension / search filter), **F4** fixes + rollback, **F5** checkpoint resume, **F6** run console, **F7** decision timeline (type / round filter + search), **F8** Quality Gate, **F9** Experience Bank, **F10** Defense Events. Number-key shortcuts: `1`–`9` jump to F1–F9, `0` to F10, `-` back to the live stream. **F8/F9/F10 are live** — they scan the session stream for the latest `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` results and render real data (PASS/FAIL chip + per-dimension score bars, experience entries with adopt buttons + client-side search, defense type-count chips + filtered event stream); when the session stream has none they distinguish "no data on disk" from "not pulled yet" and offer a **拉取磁盘快照 (pull disk snapshot)** instruction that has the model call the read-side tools and echo the `.iterate/` artifacts back into the stream. One-click JSON export of all observatory data — manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report` (download, copy fallback). **v3.9: report-export buttons** (导出报告 / 导出 SARIF, **v3.11 + 导出 JSON 报告**) render the *same shared data model* as the `iterate_report` tool — a PR-pasteable Markdown run report, a SARIF 2.1.0 log for CI / GitHub Code Scanning, and (v3.11) the complete normalized run model as machine-readable JSON. **v3.5.3: reset actions surfaced** — F5 copies an `iterate_checkpoint clear` instruction, F8 an `iterate_quality_gate clear` instruction, F10 an `iterate_defense_events clear` instruction.
110
+ - **ObservatoryPanel** (`conversation.input.dock`) — a **twelve-tab** runtime observatory below the input: **live activity stream** (default tab, type filter) + **F1** review threads (expand / collapse all), **F2** convergence trend (incl. the per-round comparison table: findings raised / fixed / severity mix / validation outcomes), **F3** finding locations (severity / dimension / search filter), **F4** fixes + rollback, **F5** checkpoint resume, **F6** run console, **F7** decision timeline (type / round filter + search), **F8** Quality Gate, **F9** Experience Bank, **F10** Defense Events, **F11** Preflight. Number-key shortcuts: `1`–`9` jump to F1–F9, `0` to F10, `-` back to the live stream (F11 is click-only). **F8/F9/F10 are live** — they scan the session stream for the latest `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` results and render real data (PASS/FAIL chip + per-dimension score bars, experience entries with adopt buttons + client-side search, defense type-count chips + filtered event stream); when the session stream has none they distinguish "no data on disk" from "not pulled yet" and offer a **拉取磁盘快照 (pull disk snapshot)** instruction that has the model call the read-side tools and echo the `.iterate/` artifacts back into the stream. One-click JSON export of all observatory data — manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report` (download, copy fallback). **v3.9: report-export buttons** (导出报告 / 导出 SARIF, **v3.11 + 导出 JSON 报告**) render the *same shared data model* as the `iterate_report` tool — a PR-pasteable Markdown run report, a SARIF 2.1.0 log for CI / GitHub Code Scanning, and (v3.11) the complete normalized run model as machine-readable JSON. **v3.5.3: reset actions surfaced** — F5 copies an `iterate_checkpoint clear` instruction, F8 an `iterate_quality_gate clear` instruction, F10 an `iterate_defense_events clear` instruction. **v3.12: pre-run readiness (Preflight)** — F11 renders the latest `iterate_preflight` checklist scanned from the session stream (validation commands configured / git repo / dirty tree / stale FAIL gate / interrupted checkpoint), each warn/fail row offering the exact fix instruction (configure `validation.commands`, clear the stale gate, resume the checkpoint) so the environment is repaired BEFORE a round is spent on it; the empty state offers a **运行预检** instruction that has the model call `iterate_preflight`.
111
111
  - **TriagePanel** (`conversation.chat.turnTail`) — per-finding triage with `y` (keep → fix), `n` (skip), `a` (known-intentional) keyboard shortcuts and `↑`/`↓` navigation (focus-guarded: only active while the panel has focus), filtering, batch ops (incl. select-all), localStorage persistence. **Write-back closed loop**: the primary button copies an `iterate_triage apply` instruction for the marked entries (the tool validates, dedupes, backs up, rolls back on failure); a secondary button copies the raw `known_intentional` YAML; **回读已写入条目** copies an `iterate_triage list` instruction to confirm what actually landed. Copy payloads are aligned to the target tool schemas. **v3.2: native command buttons** (approve architectural fix, trigger new round, rollback to checkpoint). **v3.3: §8 指派修复 (assign)** — one click copies an `iterate_fix` instruction carrying every in-scope finding (respecting the batch toggle).
112
112
  - **StatsCard** (`conversation.chat.turnTail`) — convergence stats, round history, trend chart and completion summary when no findings remain.
113
113
  - **iterate theme skin** (`theme.overrideTokens`) — a warm-amber 13-token `--dsw-*` override, light/dark modes, togglable in settings.
@@ -196,9 +196,9 @@ To write verdicts back, mark entries with `a` and click **生成应用指令**
196
196
 
197
197
  ### Observatory tabs & shortcuts
198
198
 
199
- The observatory below the input carries **11 tabs**: the live activity stream (default) plus **F1**–**F10**:
199
+ The observatory below the input carries **12 tabs**: the live activity stream (default) plus **F1**–**F11**:
200
200
 
201
- `F1` review threads · `F2` convergence trend (incl. per-round comparison) · `F3` finding locations · `F4` fixes + rollback · `F5` checkpoint resume · `F6` run console · `F7` decision timeline · `F8` quality gate · `F9` experience bank · `F10` defense events
201
+ `F1` review threads · `F2` convergence trend (incl. per-round comparison) · `F3` finding locations · `F4` fixes + rollback · `F5` checkpoint resume · `F6` run console · `F7` decision timeline · `F8` quality gate · `F9` experience bank · `F10` defense events · `F11` preflight
202
202
 
203
203
  Press `1`–`9` to jump to F1–F9, `0` for F10, and `-` to return to the live stream.
204
204
 
@@ -288,7 +288,7 @@ validation:
288
288
 
289
289
  ---
290
290
 
291
- ## 🔧 Registered tools (17)
291
+ ## 🔧 Registered tools (19)
292
292
 
293
293
  - `iterate_config` — read / write `iterate.config.yaml`. `operation=read` returns the full config or a named section; `operation=write` schema-validates, backs up, then merges and writes — auto rollback on failure
294
294
  - `iterate_validate` — run a whitelisted validation command, return the result
@@ -308,6 +308,7 @@ validation:
308
308
  - `iterate_quality_gate` — **v3.2** read the quality certificate (`read`), or recompute + persist a fresh one (`compute`) from findings, validation results, `findingsByRound`, and `fixedByDimension`. Real per-dimension convergence rates. **v3.5: `clear` removes the persisted certificate to reset a stale gate before a fresh iteration**
309
309
  - `iterate_defense_events` — **v3.2** query defense events (list / counts), `record` a new one, or **v3.5.3 `clear` the persisted stream** so a fresh iteration starts without stale defensive data. Human-readable labels follow the project language (en / zh)
310
310
  - `iterate_report` — **v3.9** export the run as a portable report. `format:"markdown"` (default) renders a PR-pasteable report (run header, quality gate, findings, fixes, validations, defense events, experience bank); `format:"sarif"` renders a SARIF 2.1.0 log for CI / GitHub Code Scanning (one rule per review dimension, severity → error/warning/note, file+line locations, suggested fix attached). **v3.11: `format:"json"` renders the COMPLETE normalized run model as machine-readable JSON** (findings, fixes, validations, convergence, quality gate, experience bank, defense events — the lossless format for CI dashboards / PR bots). `write:true` persists it to `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json`; `lang` (zh | en) selects the report language. Read-only apart from the optional report file
311
+ - `iterate_preflight` — **v3.12** read-only pre-run readiness check. Returns a structured checklist: `validation.commands` configured (an empty set means the loop cannot prove fixes — **blocking**), whether the project is a git repository, how many files are uncommitted, whether a stale FAIL quality gate is persisted, and whether an interrupted checkpoint is waiting. Each check carries structured facts (`{ id, status: pass|warn|fail, detail }`) the observatory F11 panel localizes into a sentence + offers the exact fix instruction. Never mutates config, files, or artifacts
311
312
 
312
313
  ---
313
314
 
@@ -466,7 +467,7 @@ only `prepublishOnly` (build) runs on publish.
466
467
 
467
468
  The plugin follows dsh's "everything-is-a-plugin" architecture:
468
469
 
469
- - **Does exactly two things** — injects the system prompt teaching the model the iterate workflow + registers 17 pure-function tools
470
+ - **Does exactly two things** — injects the system prompt teaching the model the iterate workflow + registers 19 pure-function tools
470
471
  - **All orchestration runs through dsh native `workflow` + `agent` + `parallel`**
471
472
  - **Core logic is entirely pure functions** (dedupe / filter / sort / converge / meta-audit / diff computation / history filtering / cleanup reporting) — unit-testable, no I/O
472
473
  - **Security model** — file writes confined to the resolved project root (path-traversal protection); always back up before writing, roll back on failure; config writes also back up + roll back; `iterate_prune` is dry-run by default and only clears artifacts under `.iterate/` with every cleanup logged; `iterate_fix` caps content length and `iterate_triage` caps entry count to fend off abnormal oversized payloads
package/README.zh-CN.md CHANGED
@@ -60,7 +60,7 @@ dsh plugin --profile web add iterate-plugin
60
60
 
61
61
  **v3.2 质量指挥中心**:插件已从"被动观察面板"升级为"主动指挥中心 + 知识库"。新增质量门禁读取与可写计算(compute)、经验银行(检索 / 采纳 / 新增 add)、防御事件流(记录 + 中英双语标签)、原生命令按钮与 `task_mode` 指示器。
62
62
 
63
- 除 17 个纯函数工具外,还内置一套**免构建的 Web UI 层**(收敛看板、分诊面板、统计卡片、11 页签运行时观测台——实时活动流 + F1–F10——、主题皮肤等),直接挂在 dsh 客户端的既有 UI 槽位上。配置方式(`iterate.config.yaml` 与审查维度)与迭代生态的另外两个组件([技能](https://github.com/jingzhao-l/iterate-skill) / [无头引擎](https://github.com/jingzhao-l/iterate-harness))完全一致,迁移零成本。
63
+ 除 19 个纯函数工具外,还内置一套**免构建的 Web UI 层**(收敛看板、分诊面板、统计卡片、12 页签运行时观测台——实时活动流 + F1–F11——、主题皮肤等),直接挂在 dsh 客户端的既有 UI 槽位上。配置方式(`iterate.config.yaml` 与审查维度)与迭代生态的另外两个组件([技能](https://github.com/jingzhao-l/iterate-skill) / [无头引擎](https://github.com/jingzhao-l/iterate-harness))完全一致,迁移零成本。
64
64
 
65
65
  ---
66
66
 
@@ -103,10 +103,10 @@ dsh plugin --profile web add iterate-plugin
103
103
  - 每轮修复后验证
104
104
  - 修复失败自动回滚
105
105
 
106
- ### UI 层(客户端免构建槽位,v3.7:11 个页签,双语)
106
+ ### UI 层(客户端免构建槽位,v3.12:12 个页签,双语)
107
107
 
108
108
  - **收敛看板 `ConvergenceDashboard`**(`conversation.input.dock`)— 输入框上方实时显示轮次进度条、严重度统计、维度徽章、趋势迷你图;normal 模式另显示修复计数徽章;还有运行阶段芯片(当前工作流阶段 + 运行中/已结束)与 **task_mode 指示器(code / iterate)**。运行期间看板切换为"运行中"状态(显示轮次与阶段),并隐藏启动按钮。
109
- - **运行时观测台 `ObservatoryPanel`**(`conversation.input.dock`)— 输入框下方**十一个页签**运行时观测台:**实时活动流**(默认页,支持按活动类型筛选)+ **F1** 审查线程(全部展开/全部收起)、**F2** 收敛趋势(含跨轮对比表:每轮 findings / fixed / 严重度分布 / 验证结果)、**F3** 发现定位(按严重度/维度/关键词筛选)、**F4** 修复与回滚、**F5** 断点恢复、**F6** 运行控制台、**F7** 决策时间线(按类型/轮次筛选与关键词搜索)、**F8** 质量门禁、**F9** 经验银行、**F10** 防御事件。数字键快捷键:`1`–`9` 跳到 F1–F9,`0` 到 F10,`-` 回实时活动流。**F8/F9/F10 为实时数据**——扫描会话流中最近一次 `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` 结果并渲染真实数据(PASS/FAIL 徽章 + 分维度评分条、经验条目(含采纳按钮 + 客户端搜索)、防御类型计数芯片 + 筛选后的事件流);会话流中没有数据时区分"磁盘无数据"与"尚未拉取",并提供**拉取磁盘快照**指令(让模型调用只读工具、把 `.iterate/` 产物回显进会话流)。一键导出全部观测数据为 JSON——manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report`(优先下载,失败回退复制)。**v3.9 新增报告导出按钮**(导出报告 / 导出 SARIF,**v3.11 新增导出 JSON 报告**),与 `iterate_report` 工具共用同一份数据模型——分别产出可直接粘贴进 PR 的 Markdown 运行报告、供 CI / GitHub Code Scanning 消费的 SARIF 2.1.0 日志,以及(v3.11)完整的归一化运行模型 JSON(无损格式)。**v3.5.3 新增重置操作**:F5 复制 `iterate_checkpoint clear`、F8 复制 `iterate_quality_gate clear`、F10 复制 `iterate_defense_events clear` 指令。
109
+ - **运行时观测台 `ObservatoryPanel`**(`conversation.input.dock`)— 输入框下方**十二个页签**运行时观测台:**实时活动流**(默认页,支持按活动类型筛选)+ **F1** 审查线程(全部展开/全部收起)、**F2** 收敛趋势(含跨轮对比表:每轮 findings / fixed / 严重度分布 / 验证结果)、**F3** 发现定位(按严重度/维度/关键词筛选)、**F4** 修复与回滚、**F5** 断点恢复、**F6** 运行控制台、**F7** 决策时间线(按类型/轮次筛选与关键词搜索)、**F8** 质量门禁、**F9** 经验银行、**F10** 防御事件、**F11** 开跑预检。数字键快捷键:`1`–`9` 跳到 F1–F9,`0` 到 F10,`-` 回实时活动流(F11 仅点击进入)。**F8/F9/F10 为实时数据**——扫描会话流中最近一次 `iterate_quality_gate` / `iterate_experience` / `iterate_defense_events` 结果并渲染真实数据(PASS/FAIL 徽章 + 分维度评分条、经验条目(含采纳按钮 + 客户端搜索)、防御类型计数芯片 + 筛选后的事件流);会话流中没有数据时区分"磁盘无数据"与"尚未拉取",并提供**拉取磁盘快照**指令(让模型调用只读工具、把 `.iterate/` 产物回显进会话流)。一键导出全部观测数据为 JSON——manifest + live + `qualityGate` / `experienceBank` / `defenseEvents` / `report`(优先下载,失败回退复制)。**v3.9 新增报告导出按钮**(导出报告 / 导出 SARIF,**v3.11 新增导出 JSON 报告**),与 `iterate_report` 工具共用同一份数据模型——分别产出可直接粘贴进 PR 的 Markdown 运行报告、供 CI / GitHub Code Scanning 消费的 SARIF 2.1.0 日志,以及(v3.11)完整的归一化运行模型 JSON(无损格式)。**v3.5.3 新增重置操作**:F5 复制 `iterate_checkpoint clear`、F8 复制 `iterate_quality_gate clear`、F10 复制 `iterate_defense_events clear` 指令。**v3.12 新增开跑预检(Preflight)**——F11 渲染会话流中最近一次 `iterate_preflight` 清单(验证命令是否配置 / 是否 Git 仓库 / 脏工作区 / 陈旧 FAIL 门禁 / 中断断点),每个 warn/fail 行都给出精确的修复指令(配置 `validation.commands`、清除陈旧门禁、恢复断点),让环境在**花掉一轮之前**就被修好;空态提供**运行预检**指令(让模型调用 `iterate_preflight`)。
110
110
  - **Findings 分诊面板 `TriagePanel`**(`conversation.chat.turnTail`)— 逐条分诊,键盘快捷键 `y`(保留→修复)、`n`(跳过)、`a`(已知有意)与 `↑`/`↓` 移动(焦点守卫:仅面板内焦点时生效),支持筛选、批量(含一键全选所有 findings)、localStorage 持久化。**写回闭环**:主按钮复制已标记条目的 `iterate_triage apply` 指令(工具负责校验、去重、备份、失败回滚);次按钮复制裸 `known_intentional` YAML;**回读已写入条目**复制 `iterate_triage list` 指令核对实际落盘内容。复制载荷与目标工具 schema 对齐。**v3.2:原生命令按钮**(批准架构修复、触发新一轮、回滚到断点)。
111
111
  - **收敛统计卡片 `StatsCard`**(`conversation.chat.turnTail`)— 无 findings 时显示收敛统计、历史轮次、趋势图、完成摘要。
112
112
  - **iterate 主题皮肤**(`theme.overrideTokens`)— 暖琥珀配色的 13 个 `--dsw-*` token 覆盖,明暗双模式,可在设置页开关。
@@ -118,7 +118,7 @@ UI 层为**防御式设计**:`slots` / `theme` / `React` 任一不可用时自
118
118
 
119
119
  ### 工具之外的闭环行为
120
120
 
121
- 除 17 个注册工具(完整参考见下文)外,插件还端到端打通了几条关键闭环:
121
+ 除 19 个注册工具(完整参考见下文)外,插件还端到端打通了几条关键闭环:
122
122
 
123
123
  - **findings 分诊闭环** — 审查 → UI 分诊(y/n/a)→ `iterate_triage` 写回 `known_intentional` → 下一轮自动过滤
124
124
  - **结构化修复系统** — 每次修复先备份、写注册表、记录 diff,验证失败可 `iterate_rollback` 还原
@@ -195,9 +195,9 @@ pnpm add /path/to/iterate-skill/harness/iterate-plugin
195
195
 
196
196
  ### 观测台页签与快捷键
197
197
 
198
- 输入框下方的观测台共 **11 个页签**:实时活动流(默认页)+ **F1–F10**:
198
+ 输入框下方的观测台共 **12 个页签**:实时活动流(默认页)+ **F1–F11**:
199
199
 
200
- `F1` 审查线程 · `F2` 收敛趋势(含跨轮对比)· `F3` 发现定位 · `F4` 修复与回滚 · `F5` 断点恢复 · `F6` 运行控制台 · `F7` 决策时间线 · `F8` 质量门禁 · `F9` 经验银行 · `F10` 防御事件
200
+ `F1` 审查线程 · `F2` 收敛趋势(含跨轮对比)· `F3` 发现定位 · `F4` 修复与回滚 · `F5` 断点恢复 · `F6` 运行控制台 · `F7` 决策时间线 · `F8` 质量门禁 · `F9` 经验银行 · `F10` 防御事件 · `F11` 开跑预检
201
201
 
202
202
  按 `1`–`9` 跳到 F1–F9,`0` 到 F10,`-` 回实时活动流。
203
203
 
@@ -287,7 +287,7 @@ validation:
287
287
 
288
288
  ---
289
289
 
290
- ## 🔧 注册工具(17 个)
290
+ ## 🔧 注册工具(19 个)
291
291
 
292
292
  - `iterate_config` — 读取 / 写入 `iterate.config.yaml`。`operation=read` 返回完整配置或指定 section;`operation=write` 做 schema 校验、备份后局部合并写入,失败自动回滚
293
293
  - `iterate_validate` — 运行白名单验证命令,返回结果
@@ -307,6 +307,7 @@ validation:
307
307
  - `iterate_quality_gate` — **v3.2** 读取质量凭证(`read`),或基于 findings、验证结果、`findingsByRound` 与 `fixedByDimension` 重新计算并持久化一份新凭证(`compute`)。真实的逐维度收敛率。**v3.5 新增 `clear`:删除持久化的质量凭证**,用于新一轮迭代前重置陈旧的门禁
308
308
  - `iterate_defense_events` — **v3.2** 查询防御事件(list / counts),`record` 记录一条新事件,或 **v3.5.3 `clear` 清除持久化事件流**,让新一轮迭代不带陈旧防御数据起跑。可读标签跟随项目语言(en / zh)
309
309
  - `iterate_report` — **v3.9** 把本次运行导出为可移植报告。`format:"markdown"`(默认)产出可直接粘贴进 PR 的报告(运行头部、质量门禁、发现、修复、验证、防御事件、经验银行);`format:"sarif"` 产出 SARIF 2.1.0 日志供 CI / GitHub Code Scanning 消费(每个审查维度一条规则,严重度 → error/warning/note,携带文件+行号位置与建议修复)。**v3.11:`format:"json"` 产出完整归一化运行模型为机器可读 JSON**(发现、修复、验证、收敛率、质量门禁、经验银行、防御事件——供 CI 仪表盘 / PR 机器人消费的无损格式)。`write:true` 持久化到 `.iterate/report.md` / `.iterate/report.sarif` / `.iterate/report.json`;`lang`(zh | en)选择报告语言。除可选的报告文件外均为只读
310
+ - `iterate_preflight` — **v3.12** 开跑前只读预检。返回结构化清单:`validation.commands` 是否已配置(为空意味着本轮无法证明修复——**阻断项**)、是否 Git 仓库、多少个未提交文件、磁盘上是否残留 FAIL 质量门禁、是否有中断断点在等待。每项都携带结构化事实(`{ id, status: pass|warn|fail, detail }`),观测台 F11 面板据此本地化为一句话并给出精确修复指令。绝不修改配置、文件或产物
310
311
 
311
312
  ---
312
313
 
@@ -423,7 +424,7 @@ validation:
423
424
 
424
425
  插件遵循 dsh "everything-is-a-plugin" 架构:
425
426
 
426
- - **只做两件事** — 注入系统 prompt 教模型写 iterate workflow + 注册 17 个纯函数工具
427
+ - **只做两件事** — 注入系统 prompt 教模型写 iterate workflow + 注册 19 个纯函数工具
427
428
  - **所有 orchestration 通过 dsh 原生 `workflow` + `agent` + `parallel` 完成**
428
429
  - **核心逻辑全部纯函数**(去重 / 过滤 / 排序 / 收敛 / meta-audit / diff 计算 / 历史过滤 / 清理报告),可单元测试,无 I/O
429
430
  - **安全模型** — 文件写入限定在解析后的项目根目录内(路径遍历防护);写文件前必备份,失败回滚;配置写入同样备份 + 回滚;`iterate_prune` 默认 dry-run、只清理 `.iterate/` 下产物、每次清理写日志;`iterate_fix` 对 content 设字符上限、`iterate_triage` 对 entries 设数量上限,防止异常超大负载
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ import { registerExperienceBankTool } from "./tools/experience-bank.js";
44
44
  import { registerQualityGateTool } from "./tools/quality-gate.js";
45
45
  import { registerDefenseEventsTool } from "./tools/defense-events.js";
46
46
  import { registerReportTool } from "./tools/report.js";
47
+ import { registerPreflightTool } from "./tools/preflight.js";
47
48
  import { registerSessionHooks } from "./session-hooks.js";
48
49
  import { registerLiveCapture } from "./live.js";
49
50
  import { installWorkingDirectoryContext } from "./working-directory.js";
@@ -66,11 +67,11 @@ export function apply(ctx) {
66
67
  installWorkingDirectoryContext(ctx);
67
68
  registerSessionHooks(ctx);
68
69
  registerLiveCapture(ctx);
69
- // 1. Register the 17 tools (14 original + 3 v3.0). Each registration is
70
- // isolated: one failing tool must not take down the other 16, the gate
71
- // above, or the prompt below (a partial plugin that still gates and
72
- // teaches the workflow beats an aborted `apply`). Failures are logged,
73
- // never swallowed silently.
70
+ // 1. Register the 19 tools (14 original + 3 v3.0 + report + preflight). Each
71
+ // registration is isolated: one failing tool must not take down the other
72
+ // 18, the gate above, or the prompt below (a partial plugin that still
73
+ // gates and teaches the workflow beats an aborted `apply`). Failures are
74
+ // logged, never swallowed silently.
74
75
  const toolRegistrations = [
75
76
  ['iterate_config', () => registerConfigTool(ctx)],
76
77
  ['iterate_validate', () => registerValidateTool(ctx)],
@@ -92,6 +93,8 @@ export function apply(ctx) {
92
93
  ['iterate_defense_events', () => registerDefenseEventsTool(ctx)],
93
94
  // v3.9: PR-ready / CI-ready report export
94
95
  ['iterate_report', () => registerReportTool(ctx)],
96
+ // v3.12: pre-run readiness check (validation/git/stale-gate/interrupted)
97
+ ['iterate_preflight', () => registerPreflightTool(ctx)],
95
98
  ];
96
99
  for (const [toolName, register] of toolRegistrations) {
97
100
  try {
@@ -0,0 +1,239 @@
1
+ /**
2
+ * src/tools/preflight.ts — `iterate_preflight` tool.
3
+ *
4
+ * Answers one question BEFORE a run starts: "is this project ready to iterate
5
+ * safely, and what must be fixed first?" It reads only local state (config,
6
+ * git, persisted artifacts, checkpoint) and returns a structured checklist the
7
+ * observatory renders as a preflight panel (F11).
8
+ *
9
+ * Why it exists: the most expensive failure mode is starting a run that cannot
10
+ * prove anything — an empty `validation.commands` means the loop has no way to
11
+ * tell a fix from a regression, and a stale FAIL certificate or a leftover
12
+ * interrupted checkpoint makes the first round lie about its starting state.
13
+ * Preflight surfaces those conditions before the model spends a round
14
+ * discovering them mid-flight.
15
+ *
16
+ * Read-only: never mutates config, files, or artifacts.
17
+ */
18
+ import { defineTool } from '@deepseek-ai/dsh-tools';
19
+ import { resolveProjectRootForExec, loadEffectiveConfig, flattenCommands } from "../config-loader.js";
20
+ import { runGit } from "../git-scope.js";
21
+ import { readQualityGate } from "./quality-store.js";
22
+ import { readCheckpoint, computeStatus } from "./checkpoint.js";
23
+ import { readDecisionEntries } from "./decision-log.js";
24
+ import { readRegistry } from "./fix.js";
25
+ import { resultHeadline } from "./present.js";
26
+ /**
27
+ * Build the readiness checklist from already-gathered facts (pure).
28
+ *
29
+ * The five checks and why each exists:
30
+ * 1. `validation` — empty command set ⇒ the loop cannot prove a
31
+ * fix; the product is fail-closed here, so this
32
+ * is a BLOCKING `fail`, not a warning.
33
+ * 2. `git` — changed-only scope + `iterate_diff` need a
34
+ * repository; full-scope review still works, so
35
+ * absent is a `warn`.
36
+ * 3. `dirty-tree` — a run over uncommitted work mixes the user's
37
+ * WIP with the plugin's fixes; advisory `warn`.
38
+ * Unknown (null) is treated as pass so a git
39
+ * hiccup never fabricates a warning.
40
+ * 4. `stale-gate` — a persisted FAIL certificate makes the first
41
+ * round read stale; `warn` + clear before a
42
+ * fresh run.
43
+ * 5. `interrupted-checkpoint`— a leftover checkpoint from a run that never
44
+ * progressed; `warn` + resume or clear.
45
+ */
46
+ export function buildPreflightReport(input) {
47
+ const checks = [
48
+ {
49
+ id: 'validation',
50
+ status: input.validationCommandCount > 0 ? 'pass' : 'fail',
51
+ detail: { commandCount: input.validationCommandCount },
52
+ },
53
+ {
54
+ id: 'git',
55
+ status: input.isGitRepo ? 'pass' : 'warn',
56
+ detail: { isRepo: input.isGitRepo },
57
+ },
58
+ {
59
+ id: 'dirty-tree',
60
+ status: input.changedFiles !== null && input.changedFiles > 0 ? 'warn' : 'pass',
61
+ detail: { changedFiles: input.changedFiles },
62
+ },
63
+ {
64
+ id: 'stale-gate',
65
+ status: input.gateOverallStatus === 'fail' ? 'warn' : 'pass',
66
+ detail: { overallStatus: input.gateOverallStatus },
67
+ },
68
+ {
69
+ id: 'interrupted-checkpoint',
70
+ status: input.interrupted ? 'warn' : 'pass',
71
+ detail: { interrupted: input.interrupted, resumeCount: input.resumeCount },
72
+ },
73
+ ];
74
+ const blocking = checks.filter((c) => c.status === 'fail').length;
75
+ const warnings = checks.filter((c) => c.status === 'warn').length;
76
+ return { ok: true, ready: blocking === 0, blocking, warnings, checks };
77
+ }
78
+ /** Human-readable (English, for the model-facing render) description per check. */
79
+ function describeCheck(id, detail) {
80
+ switch (id) {
81
+ case 'validation': {
82
+ const n = Number(detail.commandCount ?? 0);
83
+ return n > 0
84
+ ? `${n} validation command(s) configured`
85
+ : 'no validation.commands configured — the run cannot prove fixes (blocking)';
86
+ }
87
+ case 'git':
88
+ return detail.isRepo === true
89
+ ? 'git repository detected'
90
+ : 'not a git repository — changed-only scope and iterate_diff need one';
91
+ case 'dirty-tree': {
92
+ const n = detail.changedFiles;
93
+ if (n === null || n === undefined)
94
+ return 'uncommitted changes: unknown';
95
+ const num = Number(n);
96
+ return num > 0
97
+ ? `${num} uncommitted/untracked file(s) — commit or stash before a run`
98
+ : 'working tree clean';
99
+ }
100
+ case 'stale-gate': {
101
+ const s = detail.overallStatus;
102
+ if (s === 'fail')
103
+ return 'a stale FAIL quality gate is persisted — clear it before a fresh run';
104
+ return s ? `quality gate: ${String(s)}` : 'no persisted quality gate';
105
+ }
106
+ case 'interrupted-checkpoint': {
107
+ if (detail.interrupted === true) {
108
+ const rc = Number(detail.resumeCount ?? 0);
109
+ return `an interrupted checkpoint is waiting (resumed ${rc}x) — resume or clear it`;
110
+ }
111
+ return 'no interrupted checkpoint';
112
+ }
113
+ default:
114
+ return '';
115
+ }
116
+ }
117
+ /**
118
+ * Register the `iterate_preflight` tool.
119
+ */
120
+ export function registerPreflightTool(ctx) {
121
+ ctx.tools.register(defineTool({
122
+ name: 'iterate_preflight',
123
+ // Pure read of on-disk state + git → safe to join a parallel group.
124
+ isConcurrencySafe: () => true,
125
+ presentResult: (_args, result) => {
126
+ const title = resultHeadline(result);
127
+ return title ? { card: 'generic', title } : undefined;
128
+ },
129
+ description: 'Read-only readiness check to run BEFORE starting an iterate run. Returns a checklist: whether ' +
130
+ 'validation.commands is configured (an empty set means the loop cannot prove fixes — blocking), ' +
131
+ 'whether the project is a git repository, how many files are uncommitted, whether a stale FAIL ' +
132
+ 'quality gate is persisted, and whether an interrupted checkpoint is waiting. Use it before a fresh ' +
133
+ 'run so you fix the environment first instead of discovering it mid-round.',
134
+ parameters: {
135
+ path: { type: 'string', description: 'Project root directory (default: current working directory).' },
136
+ },
137
+ output: {
138
+ schema: {
139
+ type: 'object',
140
+ additionalProperties: false,
141
+ properties: {
142
+ ok: { type: 'boolean', required: true },
143
+ ready: { type: 'boolean' },
144
+ blocking: { type: 'integer' },
145
+ warnings: { type: 'integer' },
146
+ checks: { type: 'json', description: 'Per-check outcomes: { id, status: pass|warn|fail, detail }.' },
147
+ error: { type: 'string' },
148
+ },
149
+ },
150
+ render: (_args, value) => {
151
+ if (!value.ok)
152
+ return [{ type: 'text', text: `preflight failed: ${value.error}` }];
153
+ const checks = Array.isArray(value.checks) ? value.checks : [];
154
+ const warnings = Number(value.warnings ?? 0);
155
+ const noun = warnings === 1 ? 'warning' : 'warnings';
156
+ const head = value.ready
157
+ ? `Preflight: READY (${Number(value.blocking ?? 0)} blocking, ${warnings} ${noun})`
158
+ : `Preflight: NOT READY (${Number(value.blocking ?? 0)} blocking, ${warnings} ${noun})`;
159
+ const lines = checks.map((c) => {
160
+ const check = c;
161
+ const id = String(check.id ?? '?');
162
+ const status = String(check.status ?? '?').toUpperCase();
163
+ return ` ${status} ${id} — ${describeCheck(id, check.detail ?? {})}`;
164
+ });
165
+ return [{ type: 'text', text: [head, ...lines].join('\n') }];
166
+ },
167
+ },
168
+ async execute(args, exec) {
169
+ const resolved = resolveProjectRootForExec(exec, args.path);
170
+ if (!resolved.ok) {
171
+ return { ok: false, ready: false, blocking: 0, warnings: 0, checks: [], error: resolved.reason };
172
+ }
173
+ const projectRoot = resolved.root;
174
+ // 1. validation.commands — the load-bearing check.
175
+ let validationCommandCount = 0;
176
+ try {
177
+ const effective = loadEffectiveConfig(projectRoot);
178
+ validationCommandCount = flattenCommands(effective.config.validation?.commands).length;
179
+ }
180
+ catch {
181
+ validationCommandCount = 0;
182
+ }
183
+ // 2/3. git repo + dirty tree (best-effort; any failure degrades to unknown).
184
+ let isGitRepo = false;
185
+ let changedFiles = null;
186
+ try {
187
+ const inside = await runGit(['rev-parse', '--is-inside-work-tree'], projectRoot, { signal: exec.signal });
188
+ isGitRepo = inside.ok && inside.stdout.trim() === 'true';
189
+ if (isGitRepo) {
190
+ const status = await runGit(['status', '--porcelain', '-z'], projectRoot, { signal: exec.signal });
191
+ if (status.ok) {
192
+ // `-z` NUL-terminates each entry; a rename emits two NUL-separated
193
+ // paths for one entry, but counting non-empty tokens is still a
194
+ // faithful "there is uncommitted work" signal (>=1 when dirty).
195
+ changedFiles = status.stdout.split('\u0000').filter((s) => s.length > 0).length;
196
+ }
197
+ }
198
+ }
199
+ catch {
200
+ isGitRepo = false;
201
+ changedFiles = null;
202
+ }
203
+ // 4. stale quality gate.
204
+ let gateOverallStatus = null;
205
+ try {
206
+ gateOverallStatus = readQualityGate(projectRoot).overallStatus ?? null;
207
+ }
208
+ catch {
209
+ gateOverallStatus = null;
210
+ }
211
+ // 5. interrupted checkpoint (reuse the exact status logic so preflight
212
+ // and iterate_status can never disagree about "interrupted").
213
+ let interrupted = false;
214
+ let resumeCount = 0;
215
+ try {
216
+ const status = computeStatus({
217
+ checkpoint: readCheckpoint(projectRoot),
218
+ decisionEntries: readDecisionEntries(projectRoot),
219
+ fixRegistry: readRegistry(projectRoot),
220
+ });
221
+ interrupted = status.interrupted;
222
+ resumeCount = status.resumeCount;
223
+ }
224
+ catch {
225
+ interrupted = false;
226
+ resumeCount = 0;
227
+ }
228
+ const report = buildPreflightReport({
229
+ validationCommandCount,
230
+ isGitRepo,
231
+ changedFiles,
232
+ gateOverallStatus,
233
+ interrupted,
234
+ resumeCount,
235
+ });
236
+ return { ...report, checks: report.checks };
237
+ },
238
+ }));
239
+ }