dsh-vibe-math 2.3.14 → 2.3.16

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.
@@ -2,7 +2,16 @@
2
2
  // Vibe Math V4 架构图生成器(零依赖,纯 Node)
3
3
  //
4
4
  // node docs/generate_framework_diagram_v4.mjs
5
- // → 写出 示例图/框架图-v4.svg
5
+ // → 写出 示例图/框架图-v4.svg(中文 —— 与本次加入语言开关之前逐字节一致)
6
+ //
7
+ // node docs/generate_framework_diagram_v4.mjs --lang=en
8
+ // node docs/generate_framework_diagram_v4.mjs --lang en
9
+ // → 写出 示例图/框架图-v4-en.svg(英文 —— 同一份版式,只换文字)
10
+ //
11
+ // 所有面向人的文字都收在 zh / en 两张表里,绘制代码只从选中的那张表(T)取值;
12
+ // 语言开关**不改变**几何、颜色、坐标、元素顺序与绘制原语 —— 只改文字来源。
13
+ // 溢出告警(warnings + process.exit(1))对两种语言同样生效:英文往往更长,
14
+ // 所以它同时也是"译文没有撑破容器"的守卫。
6
15
  //
7
16
  // 与 v5 同一套版式语言(band / card / 图例 / 溢出告警),只是内容换成 v4 的真实机制:
8
17
  // · 常驻层:continuable 持久子代理,独立上下文,自主方向;
@@ -11,8 +20,13 @@
11
20
  // · 门槛:**全体常驻一致**(全 1 或全 0)才进 Verified/,没有 forced / flat / 近共识收口;
12
21
  // · Lean 形式化:formalVerify = off / encourage / require(passed 后审查对象变成"忠实性")。
13
22
  //
14
- // 需要 PNG 时用浏览器打开 SVG 另存,或无头浏览器截图:
15
- // msedge --headless=new --window-size=1760,1300 --screenshot=框架图-v4.png 框架图-v4.svg
23
+ // 需要 PNG(市场轮播 / 预览图)时**不要**直接照着"窗口尺寸 = 画布尺寸"截图:
24
+ // msedge --headless=new --window-size=1720,1260 --screenshot=示例图/框架图-v4.png 示例图/框架图-v4.svg ← 会静默裁掉底部
25
+ // 原因:`--screenshot` 截的是**窗口**,而页面**视口**比窗口矮约 96px(浏览器 UI 占位),
26
+ // 画布底部那一截根本没被绘制——而 PNG 的尺寸又恰好等于画布尺寸,所以从尺寸上完全看不出来。
27
+ // (2.3.15 修掉的真实缺陷:v4/v5 的 PNG 因此丢了最下面的图例行,且连续两版没人发现。)
28
+ // 正确做法(按 SVG 自身尺寸渲染、裁掉多余高度、并校验墨迹到达内容底边):
29
+ // node docs/render_framework_diagram_png.mjs 示例图/框架图-v4.svg 示例图/框架图-v4.png 2
16
30
  //
17
31
  // 版式约定(改布局时请遵守):
18
32
  // · 每个 band 的标题在 y+27、副标题在 y+47,**内容从 y+56 开始**;
@@ -24,7 +38,333 @@ import { dirname, join } from 'node:path'
24
38
  import { fileURLToPath } from 'node:url'
25
39
 
26
40
  const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
27
- const OUT = join(ROOT, '示例图', '框架图-v4.svg')
41
+
42
+ // ---------- 语言开关 ----------
43
+ // 无参数 = zh(默认,输出与历史版本逐字节一致);--lang=en 或 --lang en = 英文。
44
+ const LANGS = { zh: 'zh', 'zh-cn': 'zh', en: 'en' }
45
+ function parseLang(argv) {
46
+ let lang = 'zh'
47
+ for (let i = 0; i < argv.length; i++) {
48
+ const a = argv[i]
49
+ let raw = null
50
+ if (a === '--lang') raw = argv[++i]
51
+ else if (a.startsWith('--lang=')) raw = a.slice('--lang='.length)
52
+ else continue
53
+ const key = String(raw === null || raw === undefined ? '' : raw).trim().toLowerCase().replace(/_/g, '-')
54
+ if (!Object.prototype.hasOwnProperty.call(LANGS, key)) {
55
+ console.error('unknown --lang value ' + (raw === undefined || raw === null ? '(missing)' : JSON.stringify(raw)) + ' (supported: zh | en)')
56
+ process.exit(2)
57
+ }
58
+ lang = LANGS[key]
59
+ }
60
+ return lang
61
+ }
62
+ const LANG = parseLang(process.argv.slice(2))
63
+
64
+ // ============================================================
65
+ // 文字表 —— zh 就是历史版本的原文(不得改动),en 是同一句话的英文。
66
+ // 工具名 / 参数名 / 路径 / JSON 键 / 标识符不翻译,两表保持一致。
67
+ // ============================================================
68
+ const zh = {
69
+ outFile: '框架图-v4.svg',
70
+ ariaLabel: 'Vibe Math V4 常驻自组织架构图',
71
+
72
+ title: 'Vibe Math V4 —— 常驻自组织合作研究',
73
+ subtitle: '框架只是媒介:中继留言 · 沉淀产物 · 计数表决 · 管理上下文。它绝不分配任务 —— 分工由常驻之间留言与开会决定',
74
+
75
+ gate: {
76
+ title: '定论门槛 —— 唯一让对象进入 Verified/ 的规则',
77
+ sub: '全体常驻一致为真(全 1)或一致为假(全 0) · 没有任何 forced / flat / 近共识自动收口 —— 强分歧不得被强行判真',
78
+ cards: [
79
+ ['全体一致为真 → 写入 Verified/命题|问题/<id>.md,回写来源库状态 = 已验证·真'],
80
+ ['全体一致为假 → 同样写入 Verified/(结论 = 假),来源库状态 = 已验证·假'],
81
+ ['出现分歧 → 公开辩论重评;仍未全票 → 留库 + 全组平均概率 + 完整辩论录'],
82
+ ],
83
+ },
84
+
85
+ residents: {
86
+ title: '常驻层(所内自治)—— 编制、方向与分工都在常驻之间完成,框架永不指派',
87
+ sub: '起始 brainstorm 产出各自方向 · 此后持续工作直到"全体认为已解决"(会议全票同意) · 可增开 / 关闭常驻',
88
+ cards: [
89
+ ['常驻 r-1', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
90
+ ['常驻 r-2', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
91
+ ['常驻 r-3', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
92
+ ['… r-N', 'add_member / remove_member', 'residentCount 默认 4', '唤醒 = sendMessage', '一轮 = 一次完整思考'],
93
+ ],
94
+ },
95
+
96
+ human: {
97
+ lines: [
98
+ '人 / 助手',
99
+ '会话根代理 = 接口',
100
+ 'configure → start',
101
+ 'message / meeting',
102
+ 'add_member / remove_member',
103
+ 'pause / resume / abort',
104
+ 'set 调参(立即生效)',
105
+ ],
106
+ },
107
+
108
+ framework: {
109
+ title: '框架 vibe-v4 —— 只是媒介(middleware):中继 · 沉淀 · 计数 · 调度',
110
+ sub: '每轮发「状态块 + 本轮问句」;常驻回一个 JSON:summary / solved / input / vote / propose_verify / propose_task / claim_task / task_done / voteSolved / formal / contextPct',
111
+ chips: [
112
+ ['消息总线 · 邮件箱', 'vibe_v4_message(to|all) → 入目标邮箱', '空闲则 sendMessage 唤醒;常驻之间不直接互调', '全靠框架 relay(模拟收件箱)· 支持广播'],
113
+ ['会议 / 辩论', 'vibe_v4_meeting(agenda) → 全体发言', '写 Shared/meetings/<id>.md 并广播结论', '看门狗防死锁 · 与验证互斥(排队,不抢占)'],
114
+ ['任务板(只搬运)', '提议 / 认领写在回执里 → Shared/taskboard.md', '认领后被唤醒;框架不决定谁做什么', 'open 任务在空闲时常驻的唤醒顺序里优先'],
115
+ ['共识验证(全票)', '回执字段 propose_verify → FIFO 排队', '独立初评(互不可见)→ 公开辩论重评', '最多 verdictMaxRounds 轮(默认 3);全票同向才入 Verified/'],
116
+ ['上下文 / compact', 'contextPct ≥ compactThreshold(默认 66)', '触发 DSH /compact;压缩后重申核心规则', 'compactAfterRounds 默认 8;人设与哲学不丢'],
117
+ ['活性 / 并发 / 恢复', 'activityTimeoutMs 心跳 · maxParallel 并发闸', '停滞 stallAutoMeetingMs(默认 6 min)自动开会', 'resume 用 residents.json + progress.md 重种化'],
118
+ ],
119
+ lean: [
120
+ 'Lean 形式化验证(可调参数 formalVerify = off / encourage / require)· 工具 vibe_v4_lean_run / _archive / _lib',
121
+ 'encourage:按实现难度自行决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录,否则记为未定论',
122
+ '★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
123
+ '★ 忠实性缺陷(decision=defect)≠ 命题为假:撤回「已通过」+ 撤回归档证明 + 进「形式化待办」,绝不记成 0/假',
124
+ ],
125
+ advance: [
126
+ 'subagent/end 之后框架按序推进:① 邮箱非空 → 唤醒非忙收件人 ② open 任务 → 唤醒提议人/认领人 ③ 待验证提议 → 开会 ④ 否则心跳:唤醒最久未活跃者并给它开会',
127
+ ],
128
+ },
129
+
130
+ libraries: {
131
+ title: '常驻专属库(每人一个目录)',
132
+ sub: '本人可写 · 他人只读(跨读被鼓励)—— 直接经 fs 写入,框架只登记',
133
+ lines: [
134
+ 'Progress/<r-id>/progress.md',
135
+ '本人持续进展(追加式,断点续跑靠它重种化)',
136
+ '',
137
+ 'Propos/<r-id>/<p-id>.md 命题',
138
+ 'Methods/<r-id>/<m-id>.md 理论 / 方法 / 工具',
139
+ 'Subproblems/<r-id>/<s-id>.md 子问题',
140
+ '',
141
+ '每条记录必填:价值程度 / 动机用途计划 / 自身概率估计',
142
+ ],
143
+ },
144
+
145
+ shared: {
146
+ title: '共享与定论(VibeMath/Projects/<project>/)',
147
+ sub: 'Shared/ 是协作产物;Verified/ 是唯一定论,写入后只读',
148
+ lines: [
149
+ 'Shared/meetings/<id>.md 会议录 + 结论',
150
+ 'Shared/debates/<target>.md 辩论录(初评 + 重评)',
151
+ 'Shared/taskboard.md 任务板(提议 / 认领)',
152
+ 'Shared/meetings/brainstorm.md 起始头脑风暴',
153
+ '',
154
+ 'Verified/命题/<id>.md (或 问题/、方法→可信断言)',
155
+ 'Problems/<id>.md 原问题陈述',
156
+ '未全票 → 留在来源库 + 平均概率 + 辩论录',
157
+ ],
158
+ },
159
+
160
+ state: {
161
+ title: '状态 / 隔离 / 恢复',
162
+ sub: '框架自己的状态:注册表、邮件箱、锁、进程纪元',
163
+ lines: [
164
+ 'State/residents.json 常驻注册表',
165
+ 'State/mailboxes.json 邮件箱(谁还没读)',
166
+ 'State/taskboard.json 任务板机器态',
167
+ 'State/decisions.json 关键决策留痕',
168
+ 'State/session.json 活动日志(人可读)',
169
+ 'State/settings.json 参数(set 立即生效)',
170
+ 'State/formal.json Lean 形式化记录(非 off 档)',
171
+ 'fileOwner 写锁 · projectLock · processEpoch · abort→resume',
172
+ ],
173
+ },
174
+
175
+ redlines: {
176
+ title: '哲学红线(不可协商)',
177
+ sub: '这五条是设计本体,不是实现细节;改它们等于换一个架构',
178
+ cards: [
179
+ ['框架绝不指派任务', '方向 / 分工 / 谁做什么', '只能由常驻留言与开会决定'],
180
+ ['定论必须全体一致', '没有 forced / flat / 近共识收口', '强分歧只能留在库里'],
181
+ ['只有 Verified/ 绝对可信', '其余(方法库未验证断言等)', '只作经验参考'],
182
+ ['常驻不直接互调', '留言一律经框架 relay', '(模拟收件箱,先落盘再投递)'],
183
+ ['人工干预不改变自组织', '留言 / 开会 / 增删成员都可用', '但"之后怎么做"仍由常驻决定'],
184
+ ],
185
+ },
186
+
187
+ links: {
188
+ toolSurface: ['工具面', 'vibe_v4_*', '/v4 命令', '↓'],
189
+ report: ['汇报', 'status', 'report', '↑'],
190
+ prompt: '↑ 每轮提示词(状态块 + 本轮问句)  ↓ 单个 JSON 回执',
191
+ promptFields: 'say / progress / record_* / propose_verify / verdict / vote_solved / input / reject_assign / task_done',
192
+ persist: '沉淀 / 记录 / 唤醒',
193
+ notUnanimous: '未全票 → 留库附概率(虚线)',
194
+ },
195
+
196
+ legend: {
197
+ title: '图例',
198
+ items: [
199
+ ['human', '人 / 助手(外部接口,不研究不投票)'],
200
+ ['house', '常驻子代理(有表决权 · 自主方向)'],
201
+ ['frame', '框架(中继 / 沉淀 / 计数 / 调度)'],
202
+ ['data', '产物与状态(常驻专属库 / Shared / State)'],
203
+ ['gate', '定论门槛 / Lean 形式化(可调)'],
204
+ ],
205
+ },
206
+
207
+ msg: {
208
+ saved: (p, n) => 'saved ' + p + ' (' + n + ' bytes)',
209
+ warnHeader: '\n布局告警(文字可能溢出容器):',
210
+ warnPrefix: ' WARN ',
211
+ ok: 'layout: no overflow warnings',
212
+ widthOverflow: (where, w, limit, s) => `${where}: 文字宽 ${w} > 可用 ${limit} — ${s}…`,
213
+ cardOverflow: (x, y, n, need, h) => `card@${x},${y}: ${n} 行需要高 ${need} > 容器 ${h}`,
214
+ },
215
+ }
216
+
217
+ const en = {
218
+ outFile: '框架图-v4-en.svg',
219
+ ariaLabel: 'Vibe Math V4 resident self-organizing architecture diagram',
220
+
221
+ title: 'Vibe Math V4 —— Resident Self-Organizing Collaborative Research',
222
+ subtitle: 'The framework is only a medium: relay messages · persist artifacts · count votes · manage context. It never assigns tasks —— division of labor is decided by residents messaging and meeting',
223
+
224
+ gate: {
225
+ title: 'Truth gate —— the only rule that lets an object enter Verified/',
226
+ sub: 'All residents agree true (all 1) or agree false (all 0) · no forced / flat / near-consensus auto-closing —— strong disagreement must not be forced true',
227
+ cards: [
228
+ ['All agree true → write Verified/命题|问题/<id>.md; source: verified·true'],
229
+ ['All agree false → also write Verified/ (conclusion = false), source = false'],
230
+ ['Disagreement → debate re-vote; still split → library + average + debate log'],
231
+ ],
232
+ },
233
+
234
+ residents: {
235
+ title: 'Resident layer (self-organization inside) —— roster, direction and division of labor are all settled among residents; the framework never assigns',
236
+ sub: 'Initial brainstorm produces each one\'s own direction · after that they keep working until "all consider it solved" (unanimous meeting consent) · residents can be added / closed',
237
+ cards: [
238
+ ['Resident r-1', 'continuable persistent session', 'Independent context · autonomous direction', 'Writes only its own library', "Cross-reads others' libraries (read-only)"],
239
+ ['Resident r-2', 'continuable persistent session', 'Independent context · autonomous direction', 'Writes only its own library', "Cross-reads others' libraries (read-only)"],
240
+ ['Resident r-3', 'continuable persistent session', 'Independent context · autonomous direction', 'Writes only its own library', "Cross-reads others' libraries (read-only)"],
241
+ ['… r-N', 'add_member / remove_member', 'residentCount defaults to 4', 'Wake-up = sendMessage', 'One round = one full thought'],
242
+ ],
243
+ },
244
+
245
+ human: {
246
+ lines: [
247
+ 'Human / assistant',
248
+ 'Root agent = the interface',
249
+ 'configure → start',
250
+ 'message / meeting',
251
+ 'add_member / remove_member',
252
+ 'pause / resume / abort',
253
+ 'set parameters (immediate)',
254
+ ],
255
+ },
256
+
257
+ framework: {
258
+ title: 'Framework vibe-v4 —— only a medium (middleware): relay · persist · count · schedule',
259
+ sub: 'Each round: "state block + this round\'s question"; each resident returns one JSON: summary / solved / input / vote / propose_verify / propose_task / claim_task / task_done / voteSolved / formal / contextPct',
260
+ chips: [
261
+ ['Message bus · mailbox', 'vibe_v4_message(to|all) → into the target mailbox', 'Idle → sendMessage wake-up; residents never call each other directly', 'Everything via the framework relay (simulated inbox) · broadcast'],
262
+ ['Meeting / debate', 'vibe_v4_meeting(agenda) → everyone speaks', 'Writes Shared/meetings/<id>.md and broadcasts the conclusion', 'Watchdog vs deadlock · queued, never preempts verification'],
263
+ ['Task board (relay only)', 'Propose / claim written in the receipt → Shared/taskboard.md', 'Claiming wakes the claimer; the framework never decides who does what', 'Open tasks come first in the idle-resident wake-up order'],
264
+ ['Consensus verification (unanimous)', 'Receipt field propose_verify → FIFO queue', 'Independent first vote (blind to each other) → public debate re-vote', 'Max verdictMaxRounds rounds (default 3); unanimous same-way → Verified/'],
265
+ ['Context / compact', 'contextPct ≥ compactThreshold (default 66)', 'Triggers DSH /compact; core rules restated after compaction', 'compactAfterRounds default 8; persona and philosophy kept'],
266
+ ['Liveness / concurrency / recovery', 'activityTimeoutMs heartbeat · maxParallel concurrency gate', 'Stall stallAutoMeetingMs (default 6 min) automatic meeting', 'resume re-seeds from residents.json + progress.md'],
267
+ ],
268
+ lean: [
269
+ 'Lean formal verification (adjustable parameter formalVerify = off / encourage / require) · tools vibe_v4_lean_run / _archive / _lib',
270
+ 'encourage: decide by implementation difficulty whether to formalize; require: a true/false conclusion needs "Lean passed" or an explicit blocking record, otherwise it is recorded as undecided',
271
+ '★ Once Lean passes, the object of review changes: no longer "is the derivation right" but "are Lean\'s definitions/objects/conditions/assumptions/conclusions faithful to the original text of the proposition"',
272
+ '★ A fidelity defect (decision=defect) ≠ the proposition is false: revoke "passed" + withdraw the archived proof + add to the "formalization TODO"; never record it as 0/false',
273
+ ],
274
+ advance: [
275
+ 'After subagent/end the framework advances: ① non-empty mailbox → wake non-busy recipients ② open task → wake proposer/claimer ③ pending verification → meeting ④ else heartbeat: wake longest-idle resident into a meeting',
276
+ ],
277
+ },
278
+
279
+ libraries: {
280
+ title: 'Resident-owned libraries (one directory each)',
281
+ sub: 'Owner writes · others read-only · via fs · framework registers',
282
+ lines: [
283
+ 'Progress/<r-id>/progress.md',
284
+ 'Ongoing progress (append-only; checkpoint resume re-seeds from it)',
285
+ '',
286
+ 'Propos/<r-id>/<p-id>.md proposition',
287
+ 'Methods/<r-id>/<m-id>.md theory / method / tool',
288
+ 'Subproblems/<r-id>/<s-id>.md subproblem',
289
+ '',
290
+ 'Required per record: value · motivation-use plan · own probability',
291
+ ],
292
+ },
293
+
294
+ shared: {
295
+ title: 'Shared and concluded (VibeMath/Projects/<project>/)',
296
+ sub: 'Shared/ = artifacts; Verified/ = only conclusion, read-only',
297
+ lines: [
298
+ 'Shared/meetings/<id>.md meeting minutes + conclusion',
299
+ 'Shared/debates/<target>.md debate record (first vote + re-vote)',
300
+ 'Shared/taskboard.md task board (propose / claim)',
301
+ 'Shared/meetings/brainstorm.md initial brainstorm',
302
+ '',
303
+ 'Verified/命题/<id>.md (or 问题/, 方法 → trusted assertion)',
304
+ 'Problems/<id>.md original problem statement',
305
+ 'Not unanimous → source library + average probability + debate record',
306
+ ],
307
+ },
308
+
309
+ state: {
310
+ title: 'State / isolation / recovery',
311
+ sub: 'Framework state: registry, mailboxes, locks, process epoch',
312
+ lines: [
313
+ 'State/residents.json resident registry',
314
+ 'State/mailboxes.json mailboxes (who has not read)',
315
+ 'State/taskboard.json task board machine state',
316
+ 'State/decisions.json key decisions on record',
317
+ 'State/session.json activity log (human-readable)',
318
+ 'State/settings.json parameters (set takes effect immediately)',
319
+ 'State/formal.json Lean formalization record (non-off tiers)',
320
+ 'fileOwner write lock · projectLock · processEpoch · abort→resume',
321
+ ],
322
+ },
323
+
324
+ redlines: {
325
+ title: 'Philosophical red lines (non-negotiable)',
326
+ sub: 'These five are the design itself, not implementation details; changing them means a different architecture',
327
+ cards: [
328
+ ['The framework never assigns tasks', 'Direction / division / who does what', 'only residents, by messages and meetings'],
329
+ ['Conclusions must be unanimous', 'No forced / flat / near-consensus closing', 'strong disagreement stays in the library'],
330
+ ['Only Verified/ is absolutely trusted', 'the rest (unverified method-library claims)', 'is empirical reference only'],
331
+ ['Residents never call each other', 'all messages go through framework relay', '(simulated inbox: persisted before delivery)'],
332
+ ['Self-organization survives intervention', 'messages / meetings / add-remove members', 'but "what happens next" is up to residents'],
333
+ ],
334
+ },
335
+
336
+ links: {
337
+ toolSurface: ['Tool surface', 'vibe_v4_*', '/v4 commands', '↓'],
338
+ report: ['Report', 'status', 'report', '↑'],
339
+ prompt: '↑ prompt each round (state block + this round\'s question)  ↓ a single JSON receipt',
340
+ promptFields: 'say / progress / record_* / propose_verify / verdict / vote_solved / input / reject_assign / task_done',
341
+ persist: 'Persist / record / wake',
342
+ notUnanimous: 'Not unanimous → kept in library with probability (dashed)',
343
+ },
344
+
345
+ legend: {
346
+ title: 'Legend',
347
+ items: [
348
+ ['human', 'Human / assistant (no research, no vote)'],
349
+ ['house', 'Resident subagent (votes · autonomous direction)'],
350
+ ['frame', 'Framework (relay / persist / count / schedule)'],
351
+ ['data', 'Artifacts, state (resident lib / Shared / State)'],
352
+ ['gate', 'Truth gate / Lean formalization (adjustable)'],
353
+ ],
354
+ },
355
+
356
+ msg: {
357
+ saved: (p, n) => 'saved ' + p + ' (' + n + ' bytes)',
358
+ warnHeader: '\nLayout warnings (text may overflow its container):',
359
+ warnPrefix: ' WARN ',
360
+ ok: 'layout: no overflow warnings',
361
+ widthOverflow: (where, w, limit, s) => `${where}: text width ${w} > available ${limit} — ${s}…`,
362
+ cardOverflow: (x, y, n, need, h) => `card@${x},${y}: ${n} line(s) need height ${need} > container ${h}`,
363
+ },
364
+ }
365
+
366
+ const T = LANG === 'en' ? en : zh
367
+ const OUT = join(ROOT, '示例图', T.outFile)
28
368
 
29
369
  const W = 1720, H = 1260
30
370
  const FONT = "'Microsoft YaHei','PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Source Han Sans SC',sans-serif"
@@ -50,7 +390,7 @@ const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replac
50
390
  const textWidth = (s, fs) => [...String(s)].reduce((n, ch) => n + (/[\u2e80-\u9fff\uff00-\uffef]/.test(ch) ? 1 : 0.56), 0) * fs
51
391
  const fits = (s, fs, limit, where) => {
52
392
  const w = textWidth(s, fs)
53
- if (w > limit) warnings.push(`${where}: 文字宽 ${w.toFixed(0)} > 可用 ${limit.toFixed(0)} — ${String(s).slice(0, 42)}…`)
393
+ if (w > limit) warnings.push(T.msg.widthOverflow(where, w.toFixed(0), limit.toFixed(0), String(s).slice(0, 42)))
54
394
  return w
55
395
  }
56
396
 
@@ -72,7 +412,7 @@ function card(x, y, w, h, lines, { fill = '#ffffff', stroke = C.frame, fs = 13,
72
412
  const cx = x + w / 2
73
413
  const lead = fs + 4
74
414
  const total = (lines.length - 1) * lead
75
- if (total + fs > h) warnings.push(`card@${x},${y}: ${lines.length} 行需要高 ${(total + fs).toFixed(0)} > 容器 ${h}`)
415
+ if (total + fs > h) warnings.push(T.msg.cardOverflow(x, y, lines.length, (total + fs).toFixed(0), h))
76
416
  let ty = y + h / 2 - total / 2 + fs * 0.36
77
417
  lines.forEach((ln, i) => {
78
418
  const size = i === 0 ? fs : sfs
@@ -104,7 +444,7 @@ function polyline(points, { color = C.human, width = 2, dashed = false, marker =
104
444
 
105
445
  // ---------- document ----------
106
446
  push('<?xml version="1.0" encoding="UTF-8"?>')
107
- push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="Vibe Math V4 常驻自组织架构图">`)
447
+ push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="${esc(T.ariaLabel)}">`)
108
448
  push(`<defs>
109
449
  <marker id="both" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="${C.line}"/></marker>
110
450
  <marker id="bothHuman" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="${C.human}"/></marker>
@@ -117,170 +457,90 @@ push(`<rect width="${W}" height="${H}" fill="#ffffff"/>`)
117
457
  push(`<g font-family="${FONT}">`)
118
458
 
119
459
  // ---- 标题 ----
120
- plain(W / 2, 40, 'Vibe Math V4 —— 常驻自组织合作研究', { size: 27, fill: C.ink, anchor: 'middle', weight: 700 })
121
- plain(W / 2, 67, '框架只是媒介:中继留言 · 沉淀产物 · 计数表决 · 管理上下文。它绝不分配任务 —— 分工由常驻之间留言与开会决定',
122
- { size: 13, fill: C.mute, anchor: 'middle', limit: W - 80, where: 'subtitle' })
460
+ plain(W / 2, 40, T.title, { size: 27, fill: C.ink, anchor: 'middle', weight: 700 })
461
+ plain(W / 2, 67, T.subtitle, { size: 13, fill: C.mute, anchor: 'middle', limit: W - 80, where: 'subtitle' })
123
462
 
124
463
  // ---- 定论门槛 ----
125
- band(FULL_L, 84, FULL_R - FULL_L, 92, '定论门槛 —— 唯一让对象进入 Verified/ 的规则',
126
- '全体常驻一致为真(全 1)或一致为假(全 0) · 没有任何 forced / flat / 近共识自动收口 —— 强分歧不得被强行判真',
464
+ band(FULL_L, 84, FULL_R - FULL_L, 92, T.gate.title, T.gate.sub,
127
465
  { fill: C.gateBg, stroke: C.gate, titleFill: C.gate })
128
- card(62, 140, 506, 28, ['全体一致为真 → 写入 Verified/命题|问题/<id>.md,回写来源库状态 = 已验证·真'], { stroke: C.gate, fs: 11.5 })
129
- card(588, 140, 506, 28, ['全体一致为假 → 同样写入 Verified/(结论 = 假),来源库状态 = 已验证·假'], { stroke: C.gate, fs: 11.5 })
130
- card(1114, 140, 524, 28, ['出现分歧 → 公开辩论重评;仍未全票 → 留库 + 全组平均概率 + 完整辩论录'], { stroke: C.gate, fs: 11.5 })
466
+ card(62, 140, 506, 28, T.gate.cards[0], { stroke: C.gate, fs: 11.5 })
467
+ card(588, 140, 506, 28, T.gate.cards[1], { stroke: C.gate, fs: 11.5 })
468
+ card(1114, 140, 524, 28, T.gate.cards[2], { stroke: C.gate, fs: 11.5 })
131
469
 
132
470
  // ---- 常驻层 ----
133
- band(MAIN_L, 188, MAIN_R - MAIN_L, 150, '常驻层(所内自治)—— 编制、方向与分工都在常驻之间完成,框架永不指派',
134
- '起始 brainstorm 产出各自方向 · 此后持续工作直到"全体认为已解决"(会议全票同意) · 可增开 / 关闭常驻',
471
+ band(MAIN_L, 188, MAIN_R - MAIN_L, 150, T.residents.title, T.residents.sub,
135
472
  { fill: C.houseBg, stroke: C.house, titleFill: '#8a5200' })
136
473
  {
137
474
  const cw = (IN_R - IN_L - 3 * 18) / 4
138
- const residents = [
139
- ['常驻 r-1', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
140
- ['常驻 r-2', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
141
- ['常驻 r-3', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
142
- ['… r-N', 'add_member / remove_member', 'residentCount 默认 4', '唤醒 = sendMessage', '一轮 = 一次完整思考'],
143
- ]
144
- residents.forEach((lines, i) => card(IN_L + i * (cw + 18), 246, cw, 88, lines, { stroke: C.house, fs: 13, sfs: 10.5 }))
475
+ T.residents.cards.forEach((lines, i) => card(IN_L + i * (cw + 18), 246, cw, 88, lines, { stroke: C.house, fs: 13, sfs: 10.5 }))
145
476
  }
146
477
 
147
478
  // ---- 人 / 助手(左侧控制通道)----
148
- card(FULL_L + 2, 188, 176, 150, [
149
- '人 / 助手',
150
- '会话根代理 = 接口',
151
- 'configure → start',
152
- 'message / meeting',
153
- 'add_member / remove_member',
154
- 'pause / resume / abort',
155
- 'set 调参(立即生效)',
156
- ], { stroke: C.human, fs: 12, sfs: 10 })
479
+ card(FULL_L + 2, 188, 176, 150, T.human.lines, { stroke: C.human, fs: 12, sfs: 10 })
157
480
 
158
481
  // ---- 框架 ----
159
- band(MAIN_L, 400, MAIN_R - MAIN_L, 384, '框架 vibe-v4 —— 只是媒介(middleware):中继 · 沉淀 · 计数 · 调度',
160
- '每轮发「状态块 + 本轮问句」;常驻回一个 JSON:summary / solved / input / vote / propose_verify / propose_task / claim_task / task_done / voteSolved / formal / contextPct',
482
+ band(MAIN_L, 400, MAIN_R - MAIN_L, 384, T.framework.title, T.framework.sub,
161
483
  { fill: C.frameBg, stroke: C.frame, titleFill: '#084d80' })
162
- const chips = [
163
- ['消息总线 · 邮件箱', 'vibe_v4_message(to|all) → 入目标邮箱', '空闲则 sendMessage 唤醒;常驻之间不直接互调', '全靠框架 relay(模拟收件箱)· 支持广播'],
164
- ['会议 / 辩论', 'vibe_v4_meeting(agenda) → 全体发言', '写 Shared/meetings/<id>.md 并广播结论', '看门狗防死锁 · 与验证互斥(排队,不抢占)'],
165
- ['任务板(只搬运)', '提议 / 认领写在回执里 → Shared/taskboard.md', '认领后被唤醒;框架不决定谁做什么', 'open 任务在空闲时常驻的唤醒顺序里优先'],
166
- ['共识验证(全票)', '回执字段 propose_verify → FIFO 排队', '独立初评(互不可见)→ 公开辩论重评', '最多 verdictMaxRounds 轮(默认 3);全票同向才入 Verified/'],
167
- ['上下文 / compact', 'contextPct ≥ compactThreshold(默认 66)', '触发 DSH /compact;压缩后重申核心规则', 'compactAfterRounds 默认 8;人设与哲学不丢'],
168
- ['活性 / 并发 / 恢复', 'activityTimeoutMs 心跳 · maxParallel 并发闸', '停滞 stallAutoMeetingMs(默认 6 min)自动开会', 'resume 用 residents.json + progress.md 重种化'],
169
- ]
170
484
  const ccw = (IN_R - IN_L - 2 * 22) / 3
171
- chips.forEach((c, i) => {
485
+ T.framework.chips.forEach((c, i) => {
172
486
  card(IN_L + (i % 3) * (ccw + 22), i < 3 ? 462 : 548, ccw, 76, c, { stroke: C.frame, fs: 12.5, sfs: 10.5 })
173
487
  })
174
- card(IN_L, 636, IN_R - IN_L, 84, [
175
- 'Lean 形式化验证(可调参数 formalVerify = off / encourage / require)· 工具 vibe_v4_lean_run / _archive / _lib',
176
- 'encourage:按实现难度自行决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录,否则记为未定论',
177
- '★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
178
- '★ 忠实性缺陷(decision=defect)≠ 命题为假:撤回「已通过」+ 撤回归档证明 + 进「形式化待办」,绝不记成 0/假',
179
- ], { stroke: C.gate, fs: 12, sfs: 10.5 })
180
- card(IN_L, 732, IN_R - IN_L, 40, [
181
- 'subagent/end 之后框架按序推进:① 邮箱非空 → 唤醒非忙收件人 ② open 任务 → 唤醒提议人/认领人 ③ 待验证提议 → 开会 ④ 否则心跳:唤醒最久未活跃者并给它开会',
182
- ], { stroke: C.frame, fs: 11 })
488
+ card(IN_L, 636, IN_R - IN_L, 84, T.framework.lean, { stroke: C.gate, fs: 12, sfs: 10.5 })
489
+ card(IN_L, 732, IN_R - IN_L, 40, T.framework.advance, { stroke: C.frame, fs: 11 })
183
490
 
184
491
  // ---- 数据面 ----
185
492
  const dw = (MAIN_R - MAIN_L - 2 * 20) / 3
186
- band(MAIN_L, 816, dw, 250, '常驻专属库(每人一个目录)',
187
- '本人可写 · 他人只读(跨读被鼓励)—— 直接经 fs 写入,框架只登记',
493
+ band(MAIN_L, 816, dw, 250, T.libraries.title, T.libraries.sub,
188
494
  { fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
189
- card(IN_L, 872, dw - 44, 176, [
190
- 'Progress/<r-id>/progress.md',
191
- '本人持续进展(追加式,断点续跑靠它重种化)',
192
- '',
193
- 'Propos/<r-id>/<p-id>.md 命题',
194
- 'Methods/<r-id>/<m-id>.md 理论 / 方法 / 工具',
195
- 'Subproblems/<r-id>/<s-id>.md 子问题',
196
- '',
197
- '每条记录必填:价值程度 / 动机用途计划 / 自身概率估计',
198
- ], { stroke: C.data, fs: 11.5, sfs: 10.5 })
495
+ card(IN_L, 872, dw - 44, 176, T.libraries.lines, { stroke: C.data, fs: 11.5, sfs: 10.5 })
199
496
 
200
497
  const B2 = MAIN_L + dw + 20
201
- band(B2, 816, dw, 250, '共享与定论(VibeMath/Projects/<project>/)',
202
- 'Shared/ 是协作产物;Verified/ 是唯一定论,写入后只读',
498
+ band(B2, 816, dw, 250, T.shared.title, T.shared.sub,
203
499
  { fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
204
- card(B2 + 22, 872, dw - 44, 176, [
205
- 'Shared/meetings/<id>.md 会议录 + 结论',
206
- 'Shared/debates/<target>.md 辩论录(初评 + 重评)',
207
- 'Shared/taskboard.md 任务板(提议 / 认领)',
208
- 'Shared/meetings/brainstorm.md 起始头脑风暴',
209
- '',
210
- 'Verified/命题/<id>.md (或 问题/、方法→可信断言)',
211
- 'Problems/<id>.md 原问题陈述',
212
- '未全票 → 留在来源库 + 平均概率 + 辩论录',
213
- ], { stroke: C.data, fs: 11.5, sfs: 10.5 })
500
+ card(B2 + 22, 872, dw - 44, 176, T.shared.lines, { stroke: C.data, fs: 11.5, sfs: 10.5 })
214
501
 
215
502
  const B3 = B2 + dw + 20
216
- band(B3, 816, MAIN_R - B3, 250, '状态 / 隔离 / 恢复',
217
- '框架自己的状态:注册表、邮件箱、锁、进程纪元',
503
+ band(B3, 816, MAIN_R - B3, 250, T.state.title, T.state.sub,
218
504
  { fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
219
- card(B3 + 22, 872, MAIN_R - B3 - 44, 176, [
220
- 'State/residents.json 常驻注册表',
221
- 'State/mailboxes.json 邮件箱(谁还没读)',
222
- 'State/taskboard.json 任务板机器态',
223
- 'State/decisions.json 关键决策留痕',
224
- 'State/session.json 活动日志(人可读)',
225
- 'State/settings.json 参数(set 立即生效)',
226
- 'State/formal.json Lean 形式化记录(非 off 档)',
227
- 'fileOwner 写锁 · projectLock · processEpoch · abort→resume',
228
- ], { stroke: C.data, fs: 11.5, sfs: 10.5 })
505
+ card(B3 + 22, 872, MAIN_R - B3 - 44, 176, T.state.lines, { stroke: C.data, fs: 11.5, sfs: 10.5 })
229
506
 
230
507
  // ---- 哲学红线 ----
231
- band(MAIN_L, 1102, MAIN_R - MAIN_L, 118, '哲学红线(不可协商)',
232
- '这五条是设计本体,不是实现细节;改它们等于换一个架构',
508
+ band(MAIN_L, 1102, MAIN_R - MAIN_L, 118, T.redlines.title, T.redlines.sub,
233
509
  { fill: C.gateBg, stroke: C.gate, titleFill: C.gate })
234
510
  {
235
- const red = [
236
- ['框架绝不指派任务', '方向 / 分工 / 谁做什么', '只能由常驻留言与开会决定'],
237
- ['定论必须全体一致', '没有 forced / flat / 近共识收口', '强分歧只能留在库里'],
238
- ['只有 Verified/ 绝对可信', '其余(方法库未验证断言等)', '只作经验参考'],
239
- ['常驻不直接互调', '留言一律经框架 relay', '(模拟收件箱,先落盘再投递)'],
240
- ['人工干预不改变自组织', '留言 / 开会 / 增删成员都可用', '但"之后怎么做"仍由常驻决定'],
241
- ]
242
511
  const rw = (IN_R - IN_L - 4 * 16) / 5
243
- red.forEach((lines, i) => card(IN_L + i * (rw + 16), 1158, rw, 56, lines, { stroke: C.gate, fs: 11.5, sfs: 10 }))
512
+ T.redlines.cards.forEach((lines, i) => card(IN_L + i * (rw + 16), 1158, rw, 56, lines, { stroke: C.gate, fs: 11.5, sfs: 10 }))
244
513
  }
245
514
 
246
515
  // ---- 连线:人 / 助手 ↔ 框架(左侧通道,不穿过常驻)----
247
516
  const CHX = 129
248
517
  polyline([[CHX, 338], [CHX, 470], [MAIN_L, 470]], { color: C.human, width: 2 })
249
- plain(CHX, 384, '工具面', { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
250
- plain(CHX, 404, 'vibe_v4_*', { fill: C.human, anchor: 'middle', size: 11, halo: true })
251
- plain(CHX, 422, '/v4 命令', { fill: C.human, anchor: 'middle', size: 11, halo: true })
252
- plain(CHX, 446, '↓', { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
253
- plain(CHX, 506, '汇报', { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
254
- plain(CHX, 526, 'status', { fill: C.human, anchor: 'middle', size: 11, halo: true })
255
- plain(CHX, 544, 'report', { fill: C.human, anchor: 'middle', size: 11, halo: true })
256
- plain(CHX, 572, '↑', { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
518
+ plain(CHX, 384, T.links.toolSurface[0], { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
519
+ plain(CHX, 404, T.links.toolSurface[1], { fill: C.human, anchor: 'middle', size: 11, halo: true })
520
+ plain(CHX, 422, T.links.toolSurface[2], { fill: C.human, anchor: 'middle', size: 11, halo: true })
521
+ plain(CHX, 446, T.links.toolSurface[3], { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
522
+ plain(CHX, 506, T.links.report[0], { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
523
+ plain(CHX, 526, T.links.report[1], { fill: C.human, anchor: 'middle', size: 11, halo: true })
524
+ plain(CHX, 544, T.links.report[2], { fill: C.human, anchor: 'middle', size: 11, halo: true })
525
+ plain(CHX, 572, T.links.report[3], { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
257
526
 
258
527
  // ---- 连线:常驻 ↔ 框架(提示词 / 回执)----
259
528
  arrow(600, 338, 600, 400, { color: C.frame, marker: 'bothFrame', width: 2 })
260
529
  arrow(1360, 338, 1360, 400, { color: C.frame, marker: 'bothFrame', width: 2 })
261
- plain(980, 366, '↑ 每轮提示词(状态块 + 本轮问句)  ↓ 单个 JSON 回执',
262
- { fill: C.frame, anchor: 'middle', size: 10.5, limit: 700, where: 'prompt label', halo: true })
263
- plain(980, 386, 'say / progress / record_* / propose_verify / verdict / vote_solved / input / reject_assign / task_done',
264
- { fill: C.mute, anchor: 'middle', size: 9.5, limit: 760, where: 'prompt fields', halo: true })
530
+ plain(980, 366, T.links.prompt, { fill: C.frame, anchor: 'middle', size: 10.5, limit: 700, where: 'prompt label', halo: true })
531
+ plain(980, 386, T.links.promptFields, { fill: C.mute, anchor: 'middle', size: 9.5, limit: 760, where: 'prompt fields', halo: true })
265
532
 
266
533
  // ---- 连线:框架 ↔ 数据面 ----
267
534
  arrow(560, 784, 560, 816, { color: C.data, marker: 'bothData', width: 2 })
268
- plain(552, 806, '沉淀 / 记录 / 唤醒', { fill: C.data, anchor: 'end', size: 10.5, halo: true })
535
+ plain(552, 806, T.links.persist, { fill: C.data, anchor: 'end', size: 10.5, halo: true })
269
536
  arrow(1180, 784, 1180, 816, { color: C.gate, marker: 'bothGate', width: 2, dashed: true })
270
- plain(1188, 806, '未全票 → 留库附概率(虚线)', { fill: C.gate, anchor: 'start', size: 10.5, halo: true })
537
+ plain(1188, 806, T.links.notUnanimous, { fill: C.gate, anchor: 'start', size: 10.5, halo: true })
271
538
 
272
539
  // ---- 图例 ----
273
- plain(52, 1246, '图例', { size: 12, fill: C.ink, weight: 700 })
274
- const legend = [
275
- [C.human, '人 / 助手(外部接口,不研究不投票)'],
276
- [C.house, '常驻子代理(有表决权 · 自主方向)'],
277
- [C.frame, '框架(中继 / 沉淀 / 计数 / 调度)'],
278
- [C.data, '产物与状态(常驻专属库 / Shared / State)'],
279
- [C.gate, '定论门槛 / Lean 形式化(可调)'],
280
- ]
281
- legend.forEach(([col, txt], i) => {
540
+ plain(52, 1246, T.legend.title, { size: 12, fill: C.ink, weight: 700 })
541
+ T.legend.items.forEach(([key, txt], i) => {
282
542
  const x = 108 + i * 330
283
- push(`<rect x="${x}" y="1235" width="14" height="14" rx="3" fill="#ffffff" stroke="${col}" stroke-width="2"/>`)
543
+ push(`<rect x="${x}" y="1235" width="14" height="14" rx="3" fill="#ffffff" stroke="${C[key]}" stroke-width="2"/>`)
284
544
  plain(x + 21, 1247, txt, { size: 11, fill: C.ink2, limit: 305, where: 'legend' })
285
545
  })
286
546
 
@@ -290,10 +550,10 @@ push('</svg>')
290
550
  mkdirSync(dirname(OUT), { recursive: true })
291
551
  const svg = out.join('\n')
292
552
  writeFileSync(OUT, svg, 'utf8')
293
- console.log('saved ' + OUT + ' (' + svg.length + ' bytes)')
553
+ console.log(T.msg.saved(OUT, svg.length))
294
554
  if (warnings.length) {
295
- console.error('\n布局告警(文字可能溢出容器):')
296
- for (const w of warnings) console.error(' WARN ' + w)
555
+ console.error(T.msg.warnHeader)
556
+ for (const w of warnings) console.error(T.msg.warnPrefix + w)
297
557
  process.exit(1)
298
558
  }
299
- console.log('layout: no overflow warnings')
559
+ console.log(T.msg.ok)