dsh-vibe-math 2.2.2 → 2.3.1
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/AUDIT-CHECKLIST.md +61 -3
- package/README.md +119 -1
- package/RELEASE-NOTES-2.3.0.md +207 -0
- package/RELEASE-NOTES-2.3.1.md +134 -0
- package/audit-formal-sensitivity.mjs +333 -0
- package/audit-persona-sensitivity.mjs +249 -0
- package/audit-persona-surface.test.mjs +349 -0
- package/audit-v5-integrity.mjs +43 -2
- package/audit-v5-sensitivity.mjs +77 -6
- package/docs/formal-verification.md +401 -0
- package/docs/generate_framework_diagram_v5.mjs +22 -16
- package/docs/test-timing.md +79 -0
- package/formal-verify-v2.test.mjs +951 -0
- package/formal-verify-v3.test.mjs +1031 -0
- package/formal-verify-v4.test.mjs +882 -0
- package/formal-verify-v5.test.mjs +598 -0
- package/package.json +22 -2
- package/prompt-corpus-persona/persona-corpus.json +32 -0
- package/prompt-corpus-persona/persona-corpus.md +674 -0
- package/prompt-corpus-v2/formal-verify-v2.json +394 -0
- package/prompt-corpus-v2/formal-verify-v2.md +4250 -0
- package/prompt-corpus-v3/formal-verify-v3.json +382 -0
- package/prompt-corpus-v3/formal-verify-v3.md +3843 -0
- package/prompt-corpus-v4/formal-verify-v4.json +84 -0
- package/prompt-corpus-v4/formal-verify-v4.md +255 -0
- package/prompt-corpus-v5/prompt-corpus-v5.json +109 -5
- package/prompt-corpus-v5/prompt-corpus-v5.md +653 -109
- package/prompt-v5-integrity.test.mjs +1158 -984
- package/run-tests.mjs +99 -0
- package/vibe-math-v2/agent.cordis.yml +40 -2
- package/vibe-math-v2/vibe-math-v2.js +811 -21
- package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +218 -1
- package/vibe-math-v3/agent.cordis.yml +46 -2
- package/vibe-math-v3/vibe-math-v3.js +810 -21
- package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +104 -2
- package/vibe-math-v4/agent.cordis.yml +46 -4
- package/vibe-math-v4/vibe-math-v4.js +744 -15
- package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +255 -0
- package/vibe-math-v5/agent.cordis.yml +41 -5
- package/vibe-math-v5/vibe-math-v5.js +621 -9
- package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +131 -4
- package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +57 -0
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5.svg +51 -46
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Lean 形式化验证(v2 / v3 / v4 / v5 共用设计)
|
|
2
|
+
|
|
3
|
+
> 本文件是四个架构**共同遵守的契约**。每个预设都在自己的单文件插件里独立实现同一套语义
|
|
4
|
+
> (四个预设之间零依赖、零共享模块,这是本项目的既有约定)。
|
|
5
|
+
> 参数、路径、工具名、提示词语义、门禁规则、索引格式都必须与本文件一致。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. 为什么要有它
|
|
10
|
+
|
|
11
|
+
多代理交叉验证的本质是**共识**,不是**证明**:m 个代理一致认为"这是对的",
|
|
12
|
+
既不能排除共同误解,也不能排除共同漏掉的情形。Lean 形式化把"我认为"换成"机器已核对":
|
|
13
|
+
一旦形式化代码通过,剩下的**唯一**不确定项就缩小为
|
|
14
|
+
|
|
15
|
+
> **Lean 代码里的定义、对象、条件、假设、结论,是否与命题原文完全一致?**
|
|
16
|
+
|
|
17
|
+
这个问题人(和代理)是能有效审查的,而"这个证明对不对"交给内核。于是验证工作的性质发生变化:
|
|
18
|
+
|
|
19
|
+
| | 原验证工作 | 启用形式化后的验证工作 |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| 审查对象 | 命题本身(推导是否正确) | **忠实性**:Lean 代码 ↔ 命题原文是否一致 |
|
|
22
|
+
| 结论强度 | 共识(可能共同出错) | 严格(内核已检查),前提是忠实性成立 |
|
|
23
|
+
| 副产品 | 无 | 可复用的 Lean 定义/引理库 |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. 参数(四个架构同名同语义)
|
|
28
|
+
|
|
29
|
+
| 参数 | 取值 | 默认 | 含义 |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `formalVerify` | `'off'` \| `'encourage'` \| `'require'` | `'off'` | 三档开关,见 §2 |
|
|
32
|
+
| `leanCommand` | 字符串 | `'lean'` | 要执行的 Lean 可执行文件(例:`'lake'`) |
|
|
33
|
+
| `leanArgs` | 字符串数组 | `[]` | 插在文件名之前的附加参数(例:`['env','lean']` 配合 `leanCommand='lake'`) |
|
|
34
|
+
| `leanTimeoutMs` | 正整数 | `120000` | 单次 Lean 运行的超时上限 |
|
|
35
|
+
|
|
36
|
+
- 非法值一律**回退到默认**(`formalVerify` 非三档之一 → `'off'`;`leanTimeoutMs` 非正 → 默认)。
|
|
37
|
+
- 参数必须出现在该架构既有的参数体系里:`set_params` / `vibe_v4_set` / `vibe_v5_set`、
|
|
38
|
+
参数 schema(`*_setup` / `*_template`)、`status`/`report` 的可读参数表。
|
|
39
|
+
- **模式是动态的**,可以在运行中切换:所有与模式相关的提示词文本都必须在**构造提示词的那一刻**
|
|
40
|
+
由 `params.formalVerify` 现算,**不得**写进"入职时冻结"的人格/章程快照
|
|
41
|
+
(否则切换模式后成员读到的仍是旧指令)。
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. 三档语义
|
|
46
|
+
|
|
47
|
+
### `off`(默认)—— 不额外进行任何要求
|
|
48
|
+
|
|
49
|
+
- 提示词里**不出现**任何 Lean 相关内容;验证流程、门禁、归档全部不变。
|
|
50
|
+
- 三个 Lean 工具**仍然注册**(注册是静态的,与既有 `ctx.effect` 纪律一致),
|
|
51
|
+
但框架不会告诉代理它们存在;代理/人主动调用时它们照常工作。
|
|
52
|
+
|
|
53
|
+
### `encourage` —— 鼓励但不强制
|
|
54
|
+
|
|
55
|
+
注入到提示词里的要求:
|
|
56
|
+
|
|
57
|
+
1. **验证时**:先判断该对象的**实现难度**;若能在可接受的工作量内形式化,优先写 Lean 代码并执行。
|
|
58
|
+
一旦 Lean 通过,**你唯一需要确认的就是忠实性**(定义/对象/条件/假设/结论是否与命题原文一致),
|
|
59
|
+
请把注意力放在这种逐条核对上,而不是重新做一遍推导。
|
|
60
|
+
2. **平时工作时**:把常用或可能复用的对象/假设/新定义随手用 Lean 形式化定义,
|
|
61
|
+
归档到全局可复用库,方便后续证明直接复用。
|
|
62
|
+
3. 若判断不值得/无法形式化,可以不做——但**鼓励**在回执里写明难度判断(会记入索引)。
|
|
63
|
+
|
|
64
|
+
**不设门禁**:即使没有任何 Lean 产物,验证照常收口(与 `off` 相同的结论)。
|
|
65
|
+
|
|
66
|
+
### `require` —— 强制上述要求
|
|
67
|
+
|
|
68
|
+
与 `encourage` 相同的注入文本,但语气为"必须",并且**加门禁**:
|
|
69
|
+
|
|
70
|
+
> 一个对象要被判定为 **真**(严格证明)或 **假**(严格反驳),必须满足
|
|
71
|
+
> `formal.status ∈ {'passed', 'blocked'}`;
|
|
72
|
+
> 否则本次裁定**不生效**——框架把它记为 `undecided`(原因 `formal-required`),
|
|
73
|
+
> 写入「形式化待办」,并在群聊公告;对象留在原库,可形式化后再次提议。
|
|
74
|
+
|
|
75
|
+
- `passed`:有 Lean 产物且最近一次运行 `exitCode === 0`;
|
|
76
|
+
- `blocked`:代理给出了**显式的难度判断/阻塞原因**(`note` 非空)。
|
|
77
|
+
**这就是"根据实现难度决定是否通过 Lean"的落点**:决定权在代理,但决定必须显式、可审计,
|
|
78
|
+
不允许静默跳过。
|
|
79
|
+
- 门禁是**兜底**而非唯一手段:`require` 模式下的验证提示词会先告知表决者
|
|
80
|
+
"定论前需要 `passed` 或 `blocked`,请先做形式化或记录阻塞原因",所以正常情况下不会触发门禁。
|
|
81
|
+
|
|
82
|
+
**为什么用 `undecided` 而不是"卡住不动"**:卡住会让研究所永久停在一个对象上;
|
|
83
|
+
记为 `undecided` + 待办既保住了"未经形式化不得称为严格结论",又保证了系统可继续推进
|
|
84
|
+
(与既有"未达门槛留库附平均概率"的设计一致)。
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 3. 路径布局
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
<VibeMath 根>/
|
|
92
|
+
├─ Formal/ # 全局可复用 Lean 库(跨项目)
|
|
93
|
+
│ ├─ Lib/<name>.lean # 可复用定义/对象/假设(def / structure / notation)
|
|
94
|
+
│ ├─ Lib/Index.md # 名称 → 文件 → 类别 → 摘要
|
|
95
|
+
│ ├─ Proved/<name>.lean # 已成立的 Lean 命题/引理(机器已核对)
|
|
96
|
+
│ └─ Proved/Index.md # 名称 → 文件 → 陈述 → 依赖
|
|
97
|
+
└─ Projects/<项目>/ # (v5 为 Projects/<项目>/Institutes/<所>/)
|
|
98
|
+
├─ Formal/
|
|
99
|
+
│ ├─ <对象id>.lean # 该对象的形式化工作文件
|
|
100
|
+
│ ├─ Index.md # 对象 → 状态 → 文件 → 归档证明 → 运行结果 → 难度判断
|
|
101
|
+
│ └─ TODO.md # require 模式下的「形式化待办」
|
|
102
|
+
└─ Verified/
|
|
103
|
+
├─ <原有定论卡片>
|
|
104
|
+
└─ Lean/<对象id>.lean # ★ 归档证明:该定论对象对应的形式化代码
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- **归档证明放在 `Verified/Lean/<id>.lean`**:它和定论卡片同处 `Verified/`,
|
|
108
|
+
一眼可见"这条结论的形式化证明在哪"。
|
|
109
|
+
- **可复用的东西放全局 `Formal/Lib` 与 `Formal/Proved`**:跨项目复用是这套设计的核心收益。
|
|
110
|
+
- 文件 id 一律过**该架构既有的 id 安全化函数**(去分隔符、去 `..`),防止路径穿越。
|
|
111
|
+
- `lean_run` 只接受位于 `<VibeMath 根>` 之内的路径;越界一律拒绝(`V5_INVALID_ARGUMENT` 或该架构对应错误)。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 4. 对象的形式化状态(`formal`)
|
|
116
|
+
|
|
117
|
+
每个可验证对象(命题/问题/子问题/方法卡)都带一个形式化记录:
|
|
118
|
+
|
|
119
|
+
```jsonc
|
|
120
|
+
{
|
|
121
|
+
"status": "none" | "attempted" | "passed" | "blocked",
|
|
122
|
+
"file": "Formal/p-1.lean", // 工作文件(可为空)
|
|
123
|
+
"proof": "Verified/Lean/p-1.lean",// 归档证明(仅 passed)
|
|
124
|
+
"decision": "used" | "blocked" | "defect", // 代理的显式判断(defect 见 §4.1)
|
|
125
|
+
"note": "…", // blocked / defect 时必填:难度判断 / 阻塞原因 / 具体偏差
|
|
126
|
+
"run": { "at": 0, "ok": true, "exitCode": 0, "ms": 0, "stdoutTail": "", "stderrTail": "" },
|
|
127
|
+
"updatedAt": 0
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
状态迁移:
|
|
132
|
+
|
|
133
|
+
| 事件 | 迁移 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `lean_run` 成功 | `none`/`attempted` → `attempted`(记录运行结果) |
|
|
136
|
+
| `lean_run` 失败 | `none` → `attempted`(记录失败输出,供代理修复) |
|
|
137
|
+
| `lean_archive{kinds:'proof', target, from|content}` + 该文件最近一次运行 `ok` | → `passed`,写 `Verified/Lean/<id>.lean` |
|
|
138
|
+
| `lean_archive{kinds:'blocked', target, note}` | → `blocked`(`note` 必填) |
|
|
139
|
+
| 回执里 `formal:{target, decision:'blocked', note}` | → `blocked`(`note` 必填) |
|
|
140
|
+
| **回执里 `formal:{target, decision:'defect', note}`** | **撤回 `passed`:→ `attempted`,清空 `proof`、删除 `Verified/Lean/<id>.lean`、把 `note` 写入记录与 `Formal/TODO.md`、公告**(`note` 必填) |
|
|
141
|
+
| 回执里 `formal:{target, decision:'used', file}` | → `attempted`(记录文件) |
|
|
142
|
+
|
|
143
|
+
### 4.1 `defect`:忠实性缺陷**不是**"命题为假"
|
|
144
|
+
|
|
145
|
+
`passed` 只保证"这段 Lean 代码通过了内核检查",**不保证它说的就是命题想说的**。当表决者逐条核对后
|
|
146
|
+
发现 Lean 代码与命题原文不一致(写窄了 / 写宽了 / 换了对象 / 漏了条件…),那是**形式化不合格**,
|
|
147
|
+
不是命题被证伪。两种混淆的后果都很严重:
|
|
148
|
+
|
|
149
|
+
- 若让表决者"发现偏差 → 投 0",框架记下的是"**该命题为假**";在 v5 的全 0 一致规则下,
|
|
150
|
+
一个写错的形式化会直接把命题写进 `Verified/` 并标注**假**——用来求真更严格的机制,
|
|
151
|
+
反而**伪造出一个错误的否定结论**。
|
|
152
|
+
- 若只把偏差记成 `blocked`,门禁会**放行**(`blocked` 本就允许定论),等于带着一个坏形式化去定论。
|
|
153
|
+
|
|
154
|
+
因此 `defect` 是独立的一档,语义固定为:
|
|
155
|
+
|
|
156
|
+
1. **表决者**:不得投 `1` 或 `0`;给一个严格介于 0 与 1 之间的值(记为弃权)并在 `Reason` 里写清偏差;
|
|
157
|
+
同时用回执 `formal:{decision:'defect', note:'<具体偏差>'}` 记录(`note` 必填)。
|
|
158
|
+
2. **框架**:把该对象的形式化记录**降级为 `attempted`**(无论此前是 `passed` 还是 `blocked`——都让位于
|
|
159
|
+
"形式化不合格,需重做")、清空 `proof`、删除 `Verified/Lean/<id>.lean`
|
|
160
|
+
(工作文件 `Formal/<id>.lean` 保留,代码不丢)、`note` 记入记录与 `Formal/TODO.md`、公告。
|
|
161
|
+
3. **`require` 档**:降级后 `formalGateOk` 为假,本次裁定**不定论**,对象进入「形式化待办」——
|
|
162
|
+
修正形式化并重新跑通后再投票。这正是"形式化不合格 ⇒ 重做",而不是"命题为假"。
|
|
163
|
+
`encourage` 档没有门禁,框架**仍然**撤回证明并记入待办,但**不得在提示词里承诺一个它无法强制的
|
|
164
|
+
"不定论"**;那里靠表决者自己的弃权(§6.1 第 ① 条)使表决无法得出布尔一致结论。
|
|
165
|
+
4. **唯一可以投 0 的情形**:表决者**独立于这份 Lean 代码**也能确定命题为假(并能给出独立理由)。
|
|
166
|
+
此时 `Reason` 必须写清独立理由,不得以"Lean 与命题不一致"作为投 0 的依据。
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 5. 工具(每个架构三个,前缀各自不同)
|
|
171
|
+
|
|
172
|
+
前缀:v2/v3 → `vibe_math_`;v4 → `vibe_v4_`;v5 → `vibe_v5_`。
|
|
173
|
+
|
|
174
|
+
### 5.1 `<prefix>lean_run`
|
|
175
|
+
|
|
176
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
177
|
+
|---|---|---|---|
|
|
178
|
+
| `file` | string | ✅ | 相对 `<VibeMath 根>` 或项目根的 `.lean` 路径 |
|
|
179
|
+
| `target` | string | | 关联对象 id(给了就更新该对象的运行记录) |
|
|
180
|
+
| `timeout_ms` | integer | | 覆盖 `leanTimeoutMs` |
|
|
181
|
+
|
|
182
|
+
返回:`{ok, exitCode, signal, ms, command, stdout, stderr, file}`;
|
|
183
|
+
找不到工具链返回 `{ok:false, code:'LEAN_NOT_FOUND', error}`;超时返回 `{ok:false, code:'LEAN_TIMEOUT'}`。
|
|
184
|
+
**绝不抛异常到调度循环**——任何失败都要变成可读结果并记录。
|
|
185
|
+
|
|
186
|
+
### 5.2 `<prefix>lean_archive`
|
|
187
|
+
|
|
188
|
+
一个工具覆盖三种归档(`kind` 区分):
|
|
189
|
+
|
|
190
|
+
| `kind` | 必填 | 行为 |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| `'def'` / `'lemma'` | `name`, `content` 或 `from` | 写入全局 `Formal/Lib/<name>.lean`(def)或 `Formal/Proved/<name>.lean`(lemma),重建对应 `Index.md`;可选 `run:true` 先跑一次再归档 |
|
|
193
|
+
| `'proof'` | `target`, `content` 或 `from` | 写入 `Formal/<target>.lean`;若该文件最近一次运行 `ok`,同时写 `Verified/Lean/<target>.lean` 并把对象标为 `passed` |
|
|
194
|
+
| `'blocked'` | `target`, `note` | 记录显式难度判断/阻塞原因(`note` 空 → 拒绝),对象标为 `blocked` |
|
|
195
|
+
|
|
196
|
+
### 5.3 `<prefix>lean_lib`
|
|
197
|
+
|
|
198
|
+
无必填参数。**扫描并重建**三处索引(项目 `Formal/Index.md`、全局 `Lib/Index.md`、`Proved/Index.md`),
|
|
199
|
+
返回可复用库清单(供代理写新定义前先查重、直接复用)。`refresh:false` 时只读不重建。
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 6. 提示词注入(在构造提示词时现算)
|
|
204
|
+
|
|
205
|
+
> **硬要求(四套一致,逐字级别的约束)**
|
|
206
|
+
> 1. **工具名一律写全称**(`<prefix>lean_run` / `<prefix>lean_archive` / `<prefix>lean_lib`)。
|
|
207
|
+
> 注入文本里**不得**出现 `lean_run` / `lean_archive` / `lean_lib` 这类缩写——那不是注册名,
|
|
208
|
+
> 代理照抄会调用一个不存在的工具(工具自己返回的 `hint` 字段同样算注入文本)。
|
|
209
|
+
> 2. **回执字段名必须与该架构真实契约一致**:v2/v3 的评审值字段是 `Result`,v4/v5 是 `verdict`。
|
|
210
|
+
> 写错字段名 = 那一票被静默丢弃。
|
|
211
|
+
> 3. **归档可复用定义/引理前必须先跑通**:`<prefix>lean_archive` 支持 `run:true`,
|
|
212
|
+
> 或先 `<prefix>lean_run`。跑不通的代码不得进入 `Formal/Lib` / `Formal/Proved`——
|
|
213
|
+
> 否则"可复用库"会被不编译的定义污染。
|
|
214
|
+
> 4. **工具链缺失时的出路必须写出来**:`LEAN_NOT_FOUND` 时把代码写下来并归档,
|
|
215
|
+
> 在 `note` 里写明"宿主无 Lean 工具链";这算显式阻塞原因,`require` 档可以据此放行,
|
|
216
|
+
> 代理不会因为装不了 Lean 而卡死。
|
|
217
|
+
> 5. **忠实性缺陷不得用 0 表达**(§4.1 第 4 条):只有独立于 Lean 代码也能确定命题为假时才投 0。
|
|
218
|
+
|
|
219
|
+
### 6.1 验证提示词
|
|
220
|
+
|
|
221
|
+
`encourage`:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
【Lean 形式化验证(鼓励模式)】
|
|
225
|
+
· 请先判断该对象的**实现难度**:若能在可接受的工作量内形式化,优先用 Lean 写形式化代码并执行。
|
|
226
|
+
· 工具:<prefix>lean_run(执行)· <prefix>lean_archive(归档)· <prefix>lean_lib(查已有可复用库)
|
|
227
|
+
· 工作目录:<项目根>/Formal/(可复用定义放 <VibeMath 根>/Formal/Lib/,已证引理放 <VibeMath 根>/Formal/Proved/)
|
|
228
|
+
· **一旦 Lean 通过,你唯一需要确认的就是忠实性**:Lean 代码里的定义/对象/条件/假设/结论
|
|
229
|
+
是否与命题原文逐条一致。请把注意力放在这种核对上,而不是重新做一遍推导。
|
|
230
|
+
· 若判断不值得或无法形式化,可以不做,但请在回执的 formal 字段写明难度判断。
|
|
231
|
+
· 归档可复用定义/引理前先跑通(<prefix>lean_archive run=true 或先 <prefix>lean_run);跑不通不要入库。
|
|
232
|
+
· 宿主没有 Lean 工具链(LEAN_NOT_FOUND)时:把代码写下来归档,并在会诊/回执的 note 里写明
|
|
233
|
+
"宿主无 Lean 工具链"——这算显式阻塞原因,定论门禁可以据此放行。
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`require`:同样内容,但"可以不做"改为"**必须**产出 Lean 形式化,或**必须**给出显式的阻塞原因",
|
|
237
|
+
并附加:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
· 本模式下定论门禁:对象必须先达到 形式化已通过 或 已记录阻塞原因,否则本次裁定记为未定论
|
|
241
|
+
(原因 formal-required)并进入「形式化待办」。
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**忠实性分支(两种模式都加,只要模式非 off)**:如果该对象**已经** `formal.status === 'passed'`,
|
|
245
|
+
验证提示词把审查对象换成忠实性,并**明确禁止**把偏差写成"假":
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
· 该对象已有**通过的 Lean 形式化证明**(<proof 路径>,最近运行 exit 0)。
|
|
249
|
+
**你不需要重新检查推导**。你的任务是**忠实性审查**:逐条核对 Lean 代码里的
|
|
250
|
+
定义 / 对象 / 条件 / 假设 / 结论是否与命题原文**完全一致**。
|
|
251
|
+
▸ 一致 → 投 <真值 1>。
|
|
252
|
+
▸ **发现任何偏差,不要投 <0>**:偏差只说明**形式化不合格**,不代表命题为假。此时请:
|
|
253
|
+
① 投票给一个严格介于 0 与 1 之间的值(记为弃权),并在理由里写清偏差;
|
|
254
|
+
② 用回执 `formal:{decision:'defect', note:'<具体偏差>'}` 记录它。框架会撤回这条证明的
|
|
255
|
+
「已通过」状态(降级为 attempted、删除归档证明、写入形式化待办);`require` 档下
|
|
256
|
+
本次裁定**不定论**,`encourage` 档**不得**声称框架会强制搁置(那里靠你的弃权阻止定论)。
|
|
257
|
+
修正形式化并重新跑通后再投票。
|
|
258
|
+
▸ 只有当你**独立于这份 Lean 代码**也能确定命题为假时,才投 <0>,并在理由里写清独立理由。
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 6.2 平时工作提示词(solver / explorer / 常驻 / method-keeper 等)
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
【顺手形式化(<模式>)】把你工作中常用或可能复用的对象、假设、新定义,
|
|
265
|
+
用 Lean 形式化定义并归档到全局可复用库(<prefix>lean_archive kind='def'),
|
|
266
|
+
已成立的引理归到 <VibeMath 根>/Formal/Proved/(kind='lemma');写之前先 <prefix>lean_lib 查重,避免重复定义。
|
|
267
|
+
归档前先跑通(<prefix>lean_run 或 lean_archive run=true):跑不通的定义不要进可复用库。
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 6.3 回执契约
|
|
271
|
+
|
|
272
|
+
非 `off` 模式时,在每轮回执契约里加入(**必须真的被框架解析并落库**——只把字段写进提示词而不实现
|
|
273
|
+
解析,等于让代理的难度判断静默消失;每个架构都必须有"回执 → 记录"的行为断言,不能只断言措辞):
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
"formal": {"target":"p-x","decision":"used|blocked|defect","file":"Formal/p-x.lean","note":"难度判断/阻塞原因/具体偏差"},
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
- `decision='blocked'` 与 `decision='defect'` 时 `note` 必填,否则整条记录被拒绝(返回该架构的
|
|
280
|
+
`*_INVALID_ARGUMENT`)。
|
|
281
|
+
- `decision='defect'` 的落库见 §4.1:**降级 + 删除归档证明 + 写入待办**。
|
|
282
|
+
- 这些字段必须出现在**该架构每一类会被表决者/研究者读到的回执契约**里(v2 的初评与辩论两条路径、
|
|
283
|
+
v3 的初评/辩论/工作轮、v4 的验证与常规/心跳轮、v5 的回执契约与心跳轮)。
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## 7. Run 语义(实现要点)
|
|
288
|
+
|
|
289
|
+
- 用 `subprocess` 服务:`resolveExecutable(leanCommand)` → `spawn({argv:[exe,...leanArgs,file], cwd, stdio:{stdin:'ignore',stdout:{maxBytes},stderr:{maxBytes}}, graceMs})`
|
|
290
|
+
→ `await handle.done` → `handle.collected.stdout?.readFrom(0).text`。
|
|
291
|
+
- **必须**给 `cwd`(项目根或 `<VibeMath 根>`),并对超时调用 `handle.terminate()`。
|
|
292
|
+
- 输出截断到 ~4KB 再入库(避免把巨大的编译器输出写进状态)。
|
|
293
|
+
- 宿主没有 `subprocess` 服务 → 返回 `{ok:false, code:'NO_SUBPROCESS'}`,并只记录 `attempted`。
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 8. 门禁(`require`)的实现位置
|
|
298
|
+
|
|
299
|
+
**必须在"写 Verified 卡片/判定定论"这唯一的收口点加门禁**,而不是散落在多处:
|
|
300
|
+
|
|
301
|
+
| 架构 | 收口点 |
|
|
302
|
+
|---|---|
|
|
303
|
+
| v2 | `writeVerifiedCardIfNeeded` 及其问题收口分支(`writeVerifiedProblemCardIfNeeded`);另有前置门禁 `formalVerdictDeferred`(在 `settleVerdict` / `processStatusUpdates` 中裁定前调用) |
|
|
304
|
+
| v3 | `writeVerifiedPropositionCardIfNeeded` / `writeVerifiedProblemCardIfNeeded`,以及最后一道闸门 `writeVerifiedCardIfChanged`;另有前置门禁 `formalBlocksConclusion`(在 `settleVerdict` / `processStatusUpdates` 中裁定前调用) |
|
|
305
|
+
| v4 | `finalizeVerify`(紧随其后的 `closeVerify` 之前) |
|
|
306
|
+
| v5 | `continueVerifyRound`(紧随其后的 `closeVerify` 之前;`finalizeUndecided` 不受影响) |
|
|
307
|
+
|
|
308
|
+
门禁不通过时:把结果记为 `undecided`(原因 `formal-required: …`)、写 `Formal/TODO.md`、
|
|
309
|
+
群聊公告、**不写** `Verified/` 卡片、不改变对象的既有权重/概率字段。
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 9. 索引格式(三份,框架维护)
|
|
314
|
+
|
|
315
|
+
### 9.1 `<项目>/Formal/Index.md`
|
|
316
|
+
|
|
317
|
+
```markdown
|
|
318
|
+
# Lean 形式化索引|<项目>
|
|
319
|
+
> 本文件由框架维护(工具调用时增量更新;`<prefix>lean_lib` 会重建)。权威状态在对象记录里。
|
|
320
|
+
|
|
321
|
+
| 对象 | 状态 | 形式化文件 | 归档证明 | 最近运行 | 难度判断 / 阻塞原因 |
|
|
322
|
+
|---|---|---|---|---|---|
|
|
323
|
+
| p-1 | passed | Formal/p-1.lean | Verified/Lean/p-1.lean | ok(exit 0,1.2s) | — |
|
|
324
|
+
| p-2 | blocked | — | — | — | 需要外层解析数论框架,本轮工作量不可接受 |
|
|
325
|
+
| p-3 | attempted | Formal/p-3.lean | — | fail(exit 1,0.8s) | — |
|
|
326
|
+
|
|
327
|
+
## 形式化待办(require 模式)
|
|
328
|
+
- p-4 —— 尚未形式化(formal-required),定论被搁置
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 9.2 `<VibeMath 根>/Formal/Lib/Index.md`
|
|
332
|
+
|
|
333
|
+
```markdown
|
|
334
|
+
# 可复用 Lean 定义库(跨项目)
|
|
335
|
+
| 名称 | 文件 | 类别 | 摘要 | 最近运行 |
|
|
336
|
+
|---|---|---|---|---|
|
|
337
|
+
| ZMod5 | Lib/ZMod5.lean | def | 模 5 剩余类与基本引理 | ok |
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 9.3 `<VibeMath 根>/Formal/Proved/Index.md`
|
|
341
|
+
|
|
342
|
+
```markdown
|
|
343
|
+
# 已成立的 Lean 命题 / 引理(机器已核对,可跨项目复用)
|
|
344
|
+
| 名称 | 文件 | 陈述 | 依赖 | 最近运行 |
|
|
345
|
+
|---|---|---|---|---|
|
|
346
|
+
| pell_sq_odd | Proved/pell_sq_odd.lean | … | ZMod5 | ok |
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 10. 测试要求(每个架构都要有)
|
|
352
|
+
|
|
353
|
+
1. **`off` 是无操作**:提示词里不出现 Lean 字样;验证流程与门禁行为与改动前一致。
|
|
354
|
+
2. **`encourage` 注入**:验证提示词含鼓励段落;平时工作提示词含"顺手形式化"段落;
|
|
355
|
+
对象 `passed` 后,验证提示词切换为**忠实性审查**措辞。
|
|
356
|
+
3. **`require` 门禁**:无形式化记录时"真"结论**不写入 Verified/**,而是 `undecided` +
|
|
357
|
+
`Formal/TODO.md` 记录;补上 `passed` 后重跑可正常写入,且卡片上记录形式化状态;
|
|
358
|
+
`blocked`(`note` 非空)也可放行;`note` 为空则拒绝。
|
|
359
|
+
4. **工具**:`lean_run` 走注入的 mock subprocess 时能拿到 exitCode/输出并记录;
|
|
360
|
+
`lean_run` 拒绝越界路径;工具链缺失返回 `LEAN_NOT_FOUND` 且不崩;
|
|
361
|
+
`lean_archive` 三种 kind 分别落到正确路径并更新索引;`lean_lib` 能重建索引。
|
|
362
|
+
5. **参数**:非法值回退;可在运行中切换(切换后新提示词立刻反映新模式)。
|
|
363
|
+
6. **灵敏度探针**(新增,放进 `audit-formal-sensitivity.mjs`):每条不变式都要有能让对应套件**变红**的变异,
|
|
364
|
+
且探针必须真的启动套件、真的被套件读取、变异真的改变行为(见 `AUDIT-CHECKLIST.md` §2)。
|
|
365
|
+
7. **静态提示词面**(放进 `audit-persona-surface.test.mjs`,四个预设一起):三个 Lean 工具名与四个参数名
|
|
366
|
+
必须出现在**该预设 persona 的 `prefix` 与 `text` 两个块**里,档位名(`'off'`/`'encourage'`/`'require'`)
|
|
367
|
+
逐字出现,忠实性语义与 `Formal/Lib` / `Formal/Proved` / `Verified/Lean` 路径写清;
|
|
368
|
+
反向:persona 里的每个 `vibe_*` 名字必须真的注册。`audit-persona-sensitivity.mjs` 用变异副本
|
|
369
|
+
证明这套断言会变红。**教训**:本特性首版在 v2/v3/v4 上"工具已注册、persona 从未列出",
|
|
370
|
+
而当时所有既有套件全绿——因为 e2e 套件直接 `apply(ctx)`,从不加载 YAML。
|
|
371
|
+
8. **回执通道必须是行为断言,不是措辞断言**(放进各架构的 `formal-verify-vN.test.mjs`):
|
|
372
|
+
构造一条带 `formal:{decision:'blocked'|'defect', note}` 的**代理回执**喂给框架,断言记录真的落库
|
|
373
|
+
(`blocked` → `blocked`;`defect` → `attempted` + `proof` 清空 + 归档文件被删 + 待办条目出现)。
|
|
374
|
+
**教训**:v2 首版只在提示词里写了"请在回执的 formal 字段写明难度判断",框架从不解析它;
|
|
375
|
+
而套件只断言"那句话存在",于是 177 条断言全绿却守着一个**死通道**——这正是
|
|
376
|
+
`AUDIT-CHECKLIST.md` §2.2 说的"只断言包含某些关键词"。
|
|
377
|
+
9. **忠实性语义必须有断言**(四套都要):断言注入文本**不含**"偏离 → 0"这类把形式化缺陷等同命题为假
|
|
378
|
+
的指令,且含"不要投 0 / 记为形式化不合格 / 走待办"的要求;并断言 `defect` 路径真的不得定论
|
|
379
|
+
(`require` 档下对象留在未定论 + `Formal/TODO.md`)。
|
|
380
|
+
10. **每个架构都要有人可读的 Lean 提示词语料**(`prompt-corpus-vN/`,随包发布):至少覆盖
|
|
381
|
+
`off` 不出文本、`encourage`、**`require`**、`passed` 忠实性分支、以及平时工作轮的"顺手形式化";
|
|
382
|
+
语料必须把工作区与 VibeMath 根**归一化为 `<WS>` / `<VIBEMATH>`**(大小写与分隔符无关——
|
|
383
|
+
Windows 下 `os.tmpdir()` 的大小写可能与插件渲染的不同),并把**时间戳归一化为 `<TIME>`**,
|
|
384
|
+
保证逐字节确定性、可 diff、不泄露本机路径。**教训**:首版只有 v3/v5 有语料,v2/v4 的 Lean
|
|
385
|
+
提示词只能翻源码;`require` 档文本不在任何语料里;v5 语料既有绝对临时路径又有时间戳,
|
|
386
|
+
每跑一次都变。
|
|
387
|
+
11. **提示词硬要求的探针**(放进 `audit-formal-sensitivity.mjs`):把注入文本里的工具名改成缩写
|
|
388
|
+
(`lean_archive`)、删掉 `require` 档的要求段落、把忠实性分支改回"偏离 → 0"——三者都必须让
|
|
389
|
+
对应套件**变红**。
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## 11. 不做什么(边界)
|
|
394
|
+
|
|
395
|
+
- **不内置 Lean**:本框架不安装工具链、不下载依赖。工具链不存在时优雅降级(记录 `LEAN_NOT_FOUND`)。
|
|
396
|
+
- **不判"忠实性"**:忠实性由代理/人审查并投票决定;框架只负责把审查焦点**换成**忠实性
|
|
397
|
+
(因为证明正确性已由内核保证)。框架不会假装自己能判断 Lean 代码是否对应命题。
|
|
398
|
+
- **`defect` 也不是框架的判断**:框架不判断 Lean 代码是否忠实,只提供"表决者认定不忠实时"的
|
|
399
|
+
一档落库语义(§4.1)——**降级 + 待办 + 不定论**,而不是把它记成"命题为假"。
|
|
400
|
+
- **不把 Lean 通过等同于"命题为真"**:`passed` 只表示"形式化代码通过内核检查",
|
|
401
|
+
该代码是否忠实于命题仍需 m 票审查。这正是 §0 表格里"审查对象变化"的含义。
|
|
@@ -23,7 +23,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
23
23
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
24
24
|
const OUT = join(ROOT, '示例图', '框架图-v5.svg')
|
|
25
25
|
|
|
26
|
-
const W = 1720, H =
|
|
26
|
+
const W = 1720, H = 1204
|
|
27
27
|
const FONT = "'Microsoft YaHei','PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Source Han Sans SC',sans-serif"
|
|
28
28
|
const MONO = "'Cascadia Mono','Consolas','SFMono-Regular',monospace"
|
|
29
29
|
|
|
@@ -171,7 +171,7 @@ plain(RX, 536, '所内组织动作(分派 · 优先级 · 督办 · 会议 ·
|
|
|
171
171
|
{ size: 10.5, fill: C.mute, limit: RW, where: 'house note' })
|
|
172
172
|
|
|
173
173
|
// ---- 框架 ----
|
|
174
|
-
band(MAIN_L, 566, MAIN_R - MAIN_L,
|
|
174
|
+
band(MAIN_L, 566, MAIN_R - MAIN_L, 382, '框架 vibe-v5 —— 只是媒介(middleware):中继 · 沉淀 · 计数 · 调度',
|
|
175
175
|
'每轮发「状态块 + 本轮问句」(规章在 persona 里,不进对话);成员回一个 JSON:say / progress / record / verdict / task_* / hire / fire / reject_assign / input / vote_solved',
|
|
176
176
|
{ fill: C.frameBg, stroke: C.frame, titleFill: '#084d80' })
|
|
177
177
|
const chips = [
|
|
@@ -186,15 +186,21 @@ const ccw = (IN_R - IN_L - 2 * 22) / 3
|
|
|
186
186
|
chips.forEach((c, i) => {
|
|
187
187
|
card(IN_L + (i % 3) * (ccw + 22), i < 3 ? 620 : 708, ccw, 78, c, { stroke: C.frame, fs: 12.5, sfs: 10.5 })
|
|
188
188
|
})
|
|
189
|
-
card(IN_L, 796, IN_R - IN_L,
|
|
189
|
+
card(IN_L, 796, IN_R - IN_L, 76, [
|
|
190
|
+
'Lean 形式化验证(可调参数 formalVerify = off / encourage / require)',
|
|
191
|
+
'encourage:验证时按实现难度决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录',
|
|
192
|
+
'★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
|
|
193
|
+
'归档:证明 → Verified/Lean/<对象>.lean|可复用定义 → VibeMath/Formal/Lib/|已证引理 → VibeMath/Formal/Proved/',
|
|
194
|
+
], { stroke: C.gate, fs: 12.5, sfs: 10.5 })
|
|
195
|
+
card(IN_L, 880, IN_R - IN_L, 38, [
|
|
190
196
|
'调度器优先级:进行中的会议或验证(二者互斥,永不同时)→ 队列中的验证 → 暂存会议 → 已认领/被分派的在办任务(按 activityTimeoutMs 节流)→ 加急邮件 → 群聊摘要 → 停滞自动开会 → 兜底心跳',
|
|
191
197
|
], { stroke: C.frame, fs: 11.5 })
|
|
192
198
|
|
|
193
199
|
// ---- 数据面 ----
|
|
194
|
-
band(FULL_L,
|
|
200
|
+
band(FULL_L, 964, 790, 196, '状态权威源:会话日志的 host-only 投影单元(键 vibeMathV5)',
|
|
195
201
|
'副作用只是往会话日志追加事件 —— 不进模型上下文(零 token 成本),checkpoint / restore 交给 DSH',
|
|
196
202
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
197
|
-
card(62,
|
|
203
|
+
card(62, 1020, 360, 124, [
|
|
198
204
|
'applyV5Event(纯折叠,只此一份)',
|
|
199
205
|
'11 类事件:institute / member / task /',
|
|
200
206
|
'message / delivered / meeting / debate /',
|
|
@@ -202,7 +208,7 @@ card(62, 932, 360, 124, [
|
|
|
202
208
|
'未知或损坏事件 → 跳过并记入 diagnostics:',
|
|
203
209
|
'可用性优先,绝不因一条坏事件卡死全场',
|
|
204
210
|
], { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
205
|
-
card(434,
|
|
211
|
+
card(434, 1020, 374, 124, [
|
|
206
212
|
'checkpoint / restore / resume',
|
|
207
213
|
'投影随会话日志一起 checkpoint;restore',
|
|
208
214
|
'时从快照 + 日志尾部重新折叠 → 跨进程与',
|
|
@@ -211,10 +217,10 @@ card(434, 932, 374, 124, [
|
|
|
211
217
|
'加固 JSON:State/<institute>.v5state.json',
|
|
212
218
|
], { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
213
219
|
|
|
214
|
-
band(852,
|
|
220
|
+
band(852, 964, FULL_R - 852, 196, '文件面:人可读产物(投影之外的一切都只是镜像)',
|
|
215
221
|
'共识的权威在投影;文件是工作区与可读产物,手工改坏不会破坏研究所',
|
|
216
222
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
217
|
-
card(874,
|
|
223
|
+
card(874, 1020, FULL_R - 874 - 22, 124, [
|
|
218
224
|
'Members/<id>/Progress|Propos|Methods|Subproblems/',
|
|
219
225
|
'只有本人可写,人人可读(跨读被鼓励)',
|
|
220
226
|
'Shared/Chat/*.md · Shared/Meetings/<mt-id>.md · Shared/Debates/<obj>.md',
|
|
@@ -249,24 +255,24 @@ arrow(556, 400, 578, 400, { color: C.house, dashed: true, width: 1.8, marker: 'b
|
|
|
249
255
|
arrow(556, 430, 578, 430, { color: C.house, dashed: true, width: 1.8, marker: 'bothHouse' })
|
|
250
256
|
|
|
251
257
|
// ---- 连线:框架 ↔ 数据面 ----
|
|
252
|
-
arrow(400,
|
|
253
|
-
plain(392,
|
|
254
|
-
arrow(1280,
|
|
255
|
-
plain(1288,
|
|
258
|
+
arrow(400, 940, 400, 964, { color: C.data, marker: 'bothData', width: 2 })
|
|
259
|
+
plain(392, 956, 'append / fold / stateOf', { fill: C.data, anchor: 'end', size: 10.5, halo: true })
|
|
260
|
+
arrow(1280, 940, 1280, 964, { color: C.data, marker: 'bothData', width: 2 })
|
|
261
|
+
plain(1288, 956, '读写产物', { fill: C.data, anchor: 'start', size: 10.5, halo: true })
|
|
256
262
|
|
|
257
263
|
// ---- 图例 ----
|
|
258
|
-
plain(52,
|
|
264
|
+
plain(52, 1186, '图例', { size: 12, fill: C.ink, weight: 700 })
|
|
259
265
|
const legend = [
|
|
260
266
|
[C.human, '所办 / 人(外部接口,不研究不投票)'],
|
|
261
267
|
[C.house, '所内成员与所内组织(虚线 = 不由框架执行)'],
|
|
262
268
|
[C.frame, '框架(中继 / 沉淀 / 计数 / 调度)'],
|
|
263
269
|
[C.data, '状态与产物'],
|
|
264
|
-
[C.gate, '
|
|
270
|
+
[C.gate, '求真门槛 / Lean 形式化(可调)'],
|
|
265
271
|
]
|
|
266
272
|
legend.forEach(([col, txt], i) => {
|
|
267
273
|
const x = 108 + i * 330
|
|
268
|
-
push(`<rect x="${x}" y="
|
|
269
|
-
plain(x + 21,
|
|
274
|
+
push(`<rect x="${x}" y="1175" width="14" height="14" rx="3" fill="#ffffff" stroke="${col}" stroke-width="2"/>`)
|
|
275
|
+
plain(x + 21, 1187, txt, { size: 11, fill: C.ink2, limit: 305, where: 'legend' })
|
|
270
276
|
})
|
|
271
277
|
|
|
272
278
|
push('</g>')
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# 测试与脚本耗时基线(决定每次跑什么、怎么跑)
|
|
2
|
+
|
|
3
|
+
> **为什么要写下来**:这套仓库的测试耗时极不均匀(一个套件 100 s,大多数不到 2 s;探针脚本要
|
|
4
|
+
> 把同一套件跑十几遍)。不看数据就会犯两种错:要么每次都全量顺序跑(浪费 30 分钟),要么为了
|
|
5
|
+
> 省时间去砍测例(降低覆盖)。**先看基线,再选策略**;每次跑完把实测时间跟本表对一下,偏差大
|
|
6
|
+
> 就更新本表。
|
|
7
|
+
|
|
8
|
+
## 1. 怎么跑(并行是默认)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
node run-tests.mjs # 全部 *.test.mjs,并行(并发 = min(4, CPU 核数))
|
|
12
|
+
node run-tests.mjs --only formal # 只跑名字含 formal 的套件
|
|
13
|
+
node run-tests.mjs --concurrency=6 # 手动指定并发
|
|
14
|
+
node audit-formal-sensitivity.mjs # 49 条不变式探针,并行(--concurrency=N / --only=<preset> / --list)
|
|
15
|
+
node audit-persona-sensitivity.mjs # 11 条提示词面探针(串行,本身只要几秒)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
两个并行 runner 都会打印**每项耗时 + 汇总(wall / sum / speed-up / 最慢几项)**。跑完请读这几行。
|
|
19
|
+
|
|
20
|
+
## 2. 基线(本机:4 核 / 8 GB,Windows,2026-09 实测)
|
|
21
|
+
|
|
22
|
+
| 脚本 | 串行(sum) | 并行(wall) | 实测输出 |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `run-tests.mjs`(23 个套件) | 219.4 s | **114.4 s**(并发 4,speed-up x1.92) | 关键路径 = `e2e-v4-fixes` 101.1 s |
|
|
25
|
+
| `audit-formal-sensitivity.mjs`(49 探针) | 612.0 s | **154.6 s**(并发 4,speed-up x3.96) | 关键路径 = 12 个 v2 探针(每个 ≈32 s) |
|
|
26
|
+
| `audit-persona-sensitivity.mjs`(11 探针) | ≈ 5 s | — | 本身很快,不需要并行 |
|
|
27
|
+
| `audit-v5-integrity.mjs` | ≈ 3 s | — | 静态审计 |
|
|
28
|
+
| `prompt-v5-integrity.test.mjs` | ≈ 7 s | — | 生成 v5 语料 |
|
|
29
|
+
|
|
30
|
+
> 优化前:全量回归 ≈ 5.5 min(串行,`formal-verify-v2` 单独 186 s);
|
|
31
|
+
> 探针脚本 ≈ **38 min**(49 条串行,其中 12 条 × `formal-verify-v2` 162 s)。
|
|
32
|
+
> 现在:**1.9 min / 2.6 min**。
|
|
33
|
+
|
|
34
|
+
单套件耗时(并行时的关键路径按此排序):
|
|
35
|
+
|
|
36
|
+
| 套件 | 耗时 | 备注 |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `e2e-v4-fixes.test.mjs` | **≈ 101 s** | 9 个用例是**轮次采样**型(如 T13 采样 400 轮、T19/T25 多轮);时间 ≈ 轮数 × 框架自身的 40 ms 计时粒度 |
|
|
39
|
+
| `formal-verify-v2.test.mjs` | **≈ 32 s** | 曾为 186 s:见 §3 |
|
|
40
|
+
| `e2e-regression.test.mjs` | ≈ 14 s | |
|
|
41
|
+
| `e2e-business.test.mjs` | ≈ 13 s | |
|
|
42
|
+
| `e2e-d9-d13.test.mjs` | ≈ 13 s | |
|
|
43
|
+
| `e2e-v3.test.mjs` | ≈ 11 s | |
|
|
44
|
+
| 其余 17 个 | ≤ 10 s | 其中 8 个 < 1 s |
|
|
45
|
+
|
|
46
|
+
## 3. 已经做过的优化(别再重复踩)
|
|
47
|
+
|
|
48
|
+
1. **v2 套件 186 s → 32 s**(5.8×,断言数不变 261):
|
|
49
|
+
- 插件用 `setInterval(..., 1000)` 轮询调度器,套件的每次 `tick()` 都得等满 1 秒;
|
|
50
|
+
套件现在**只把 `setInterval` 快进到 25 ms**(自己的 `sleep` 用 `setTimeout`,不受影响),
|
|
51
|
+
插件内部"该不该 tick"仍按真实 200 ms 下限判断,**生产代码零改动**;
|
|
52
|
+
- `verifyWithDebate` 的 16 次循环**从不提前退出**(判据是 `autoDone` 之类永远不会发生的条件),
|
|
53
|
+
于是每次调用都烧满 16×1.3 s ≈ 21 s。现在按"连续 3 轮没有新 followup"提前退出。
|
|
54
|
+
2. **v4-fixes 的 12 个轮询循环加了"安静即停"**(105 s → 98 s):原判据
|
|
55
|
+
`autoDone || running===false` 对活着的 run 永远不成立,循环只是空转;
|
|
56
|
+
现在连续 100 次无待答 followup 就停(`V4_IDLE_POLLS` 可调)。
|
|
57
|
+
**再往下压就要砍采样深度了**——那 9 个慢用例(T13/T19/T22/T23/T25/T27/T2/T9/T20)是在
|
|
58
|
+
观察"多轮之后某个指令**没有**泄漏/重复",轮数是它们的不变式本体,不要再动。
|
|
59
|
+
3. **两个 runner 并行**(本轮新增):探针 38 min → 2.6 min(sum 612 s,wall 154.6 s,x3.96);
|
|
60
|
+
全量回归 5.5 min → 1.9 min(sum 219.4 s,wall 114.4 s,x1.92)。
|
|
61
|
+
|
|
62
|
+
## 4. 并行安全(为什么可以并发)
|
|
63
|
+
|
|
64
|
+
- 每个套件/探针都自建 `mkdtempSync` 工作区,互不共享状态;
|
|
65
|
+
- **会写语料的套件必须给不同的语料目录**:`V2_CORPUS_DIR` / `V3_CORPUS_DIR` / `V4_CORPUS_DIR` /
|
|
66
|
+
`V5_CORPUS_DIR`。探针 runner 为**每个探针**分配独立目录,否则同一套件的并发实例会互相覆盖语料;
|
|
67
|
+
- `audit-persona-sensitivity.mjs` 用 `PERSONA_ROOT` 指向变异副本,且在覆盖模式下**不写**语料。
|
|
68
|
+
|
|
69
|
+
## 5. 策略建议(按目的选最小代价的组合)
|
|
70
|
+
|
|
71
|
+
| 目的 | 跑什么 | 预期 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| 改了某个架构的插件 | `node run-tests.mjs --only <vN>` + `node audit-formal-sensitivity.mjs --only=vN` | 30 s – 2 min |
|
|
74
|
+
| 改了提示词/人设 | `node run-tests.mjs --only persona --only prompt` + `node audit-persona-sensitivity.mjs` | ≈ 15 s |
|
|
75
|
+
| 改了共享契约 / 发版前 | `node run-tests.mjs` + `node audit-formal-sensitivity.mjs` + `node audit-persona-sensitivity.mjs` + `node audit-v5-integrity.mjs` | ≈ 4.5 min |
|
|
76
|
+
| 只想知道"快不快" | `node run-tests.mjs --json` | 读 `wallSeconds` / `slowest` |
|
|
77
|
+
|
|
78
|
+
**每次跑完都要看那几行 timing**:如果某个套件突然比基线慢很多,先怀疑新增的固定等待,
|
|
79
|
+
再怀疑它是否在等一个永远不会发生的条件(这正是 v2 套件 186 s 的成因)。
|