dsh-vibe-math 2.3.15 → 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.
- package/README.en.md +45 -42
- package/README.md +11 -9
- package/docs/AUDIT-CHECKLIST.md +62 -43
- package/docs/generate_framework_diagram_v2_en.mjs +595 -0
- package/docs/generate_framework_diagram_v3_en.mjs +624 -0
- package/docs/generate_framework_diagram_v4.mjs +380 -125
- package/docs/generate_framework_diagram_v5.mjs +365 -107
- package/docs/release-notes/RELEASE-NOTES-2.3.16.md +165 -0
- package/docs/test-timing.md +2 -2
- package/docs//346/236/266/346/236/204/345/233/276.md +3 -1
- package/package.json +9 -2
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v2-en.svg +291 -0
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v3-en.svg +316 -0
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v4-en.svg +187 -0
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5-en.svg +174 -0
|
@@ -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 持久子代理,独立上下文,自主方向;
|
|
@@ -29,7 +38,333 @@ import { dirname, join } from 'node:path'
|
|
|
29
38
|
import { fileURLToPath } from 'node:url'
|
|
30
39
|
|
|
31
40
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
32
|
-
|
|
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)
|
|
33
368
|
|
|
34
369
|
const W = 1720, H = 1260
|
|
35
370
|
const FONT = "'Microsoft YaHei','PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Source Han Sans SC',sans-serif"
|
|
@@ -55,7 +390,7 @@ const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replac
|
|
|
55
390
|
const textWidth = (s, fs) => [...String(s)].reduce((n, ch) => n + (/[\u2e80-\u9fff\uff00-\uffef]/.test(ch) ? 1 : 0.56), 0) * fs
|
|
56
391
|
const fits = (s, fs, limit, where) => {
|
|
57
392
|
const w = textWidth(s, fs)
|
|
58
|
-
if (w > limit) warnings.push(
|
|
393
|
+
if (w > limit) warnings.push(T.msg.widthOverflow(where, w.toFixed(0), limit.toFixed(0), String(s).slice(0, 42)))
|
|
59
394
|
return w
|
|
60
395
|
}
|
|
61
396
|
|
|
@@ -77,7 +412,7 @@ function card(x, y, w, h, lines, { fill = '#ffffff', stroke = C.frame, fs = 13,
|
|
|
77
412
|
const cx = x + w / 2
|
|
78
413
|
const lead = fs + 4
|
|
79
414
|
const total = (lines.length - 1) * lead
|
|
80
|
-
if (total + fs > h) warnings.push(
|
|
415
|
+
if (total + fs > h) warnings.push(T.msg.cardOverflow(x, y, lines.length, (total + fs).toFixed(0), h))
|
|
81
416
|
let ty = y + h / 2 - total / 2 + fs * 0.36
|
|
82
417
|
lines.forEach((ln, i) => {
|
|
83
418
|
const size = i === 0 ? fs : sfs
|
|
@@ -109,7 +444,7 @@ function polyline(points, { color = C.human, width = 2, dashed = false, marker =
|
|
|
109
444
|
|
|
110
445
|
// ---------- document ----------
|
|
111
446
|
push('<?xml version="1.0" encoding="UTF-8"?>')
|
|
112
|
-
push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="
|
|
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)}">`)
|
|
113
448
|
push(`<defs>
|
|
114
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>
|
|
115
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>
|
|
@@ -122,170 +457,90 @@ push(`<rect width="${W}" height="${H}" fill="#ffffff"/>`)
|
|
|
122
457
|
push(`<g font-family="${FONT}">`)
|
|
123
458
|
|
|
124
459
|
// ---- 标题 ----
|
|
125
|
-
plain(W / 2, 40,
|
|
126
|
-
plain(W / 2, 67,
|
|
127
|
-
{ 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' })
|
|
128
462
|
|
|
129
463
|
// ---- 定论门槛 ----
|
|
130
|
-
band(FULL_L, 84, FULL_R - FULL_L, 92,
|
|
131
|
-
'全体常驻一致为真(全 1)或一致为假(全 0) · 没有任何 forced / flat / 近共识自动收口 —— 强分歧不得被强行判真',
|
|
464
|
+
band(FULL_L, 84, FULL_R - FULL_L, 92, T.gate.title, T.gate.sub,
|
|
132
465
|
{ fill: C.gateBg, stroke: C.gate, titleFill: C.gate })
|
|
133
|
-
card(62, 140, 506, 28, [
|
|
134
|
-
card(588, 140, 506, 28, [
|
|
135
|
-
card(1114, 140, 524, 28, [
|
|
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 })
|
|
136
469
|
|
|
137
470
|
// ---- 常驻层 ----
|
|
138
|
-
band(MAIN_L, 188, MAIN_R - MAIN_L, 150,
|
|
139
|
-
'起始 brainstorm 产出各自方向 · 此后持续工作直到"全体认为已解决"(会议全票同意) · 可增开 / 关闭常驻',
|
|
471
|
+
band(MAIN_L, 188, MAIN_R - MAIN_L, 150, T.residents.title, T.residents.sub,
|
|
140
472
|
{ fill: C.houseBg, stroke: C.house, titleFill: '#8a5200' })
|
|
141
473
|
{
|
|
142
474
|
const cw = (IN_R - IN_L - 3 * 18) / 4
|
|
143
|
-
|
|
144
|
-
['常驻 r-1', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
|
|
145
|
-
['常驻 r-2', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
|
|
146
|
-
['常驻 r-3', 'continuable 持久会话', '独立上下文 · 自主方向', '只写自己的库', '跨读他人的库(只读)'],
|
|
147
|
-
['… r-N', 'add_member / remove_member', 'residentCount 默认 4', '唤醒 = sendMessage', '一轮 = 一次完整思考'],
|
|
148
|
-
]
|
|
149
|
-
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 }))
|
|
150
476
|
}
|
|
151
477
|
|
|
152
478
|
// ---- 人 / 助手(左侧控制通道)----
|
|
153
|
-
card(FULL_L + 2, 188, 176, 150,
|
|
154
|
-
'人 / 助手',
|
|
155
|
-
'会话根代理 = 接口',
|
|
156
|
-
'configure → start',
|
|
157
|
-
'message / meeting',
|
|
158
|
-
'add_member / remove_member',
|
|
159
|
-
'pause / resume / abort',
|
|
160
|
-
'set 调参(立即生效)',
|
|
161
|
-
], { 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 })
|
|
162
480
|
|
|
163
481
|
// ---- 框架 ----
|
|
164
|
-
band(MAIN_L, 400, MAIN_R - MAIN_L, 384,
|
|
165
|
-
'每轮发「状态块 + 本轮问句」;常驻回一个 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,
|
|
166
483
|
{ fill: C.frameBg, stroke: C.frame, titleFill: '#084d80' })
|
|
167
|
-
const chips = [
|
|
168
|
-
['消息总线 · 邮件箱', 'vibe_v4_message(to|all) → 入目标邮箱', '空闲则 sendMessage 唤醒;常驻之间不直接互调', '全靠框架 relay(模拟收件箱)· 支持广播'],
|
|
169
|
-
['会议 / 辩论', 'vibe_v4_meeting(agenda) → 全体发言', '写 Shared/meetings/<id>.md 并广播结论', '看门狗防死锁 · 与验证互斥(排队,不抢占)'],
|
|
170
|
-
['任务板(只搬运)', '提议 / 认领写在回执里 → Shared/taskboard.md', '认领后被唤醒;框架不决定谁做什么', 'open 任务在空闲时常驻的唤醒顺序里优先'],
|
|
171
|
-
['共识验证(全票)', '回执字段 propose_verify → FIFO 排队', '独立初评(互不可见)→ 公开辩论重评', '最多 verdictMaxRounds 轮(默认 3);全票同向才入 Verified/'],
|
|
172
|
-
['上下文 / compact', 'contextPct ≥ compactThreshold(默认 66)', '触发 DSH /compact;压缩后重申核心规则', 'compactAfterRounds 默认 8;人设与哲学不丢'],
|
|
173
|
-
['活性 / 并发 / 恢复', 'activityTimeoutMs 心跳 · maxParallel 并发闸', '停滞 stallAutoMeetingMs(默认 6 min)自动开会', 'resume 用 residents.json + progress.md 重种化'],
|
|
174
|
-
]
|
|
175
484
|
const ccw = (IN_R - IN_L - 2 * 22) / 3
|
|
176
|
-
chips.forEach((c, i) => {
|
|
485
|
+
T.framework.chips.forEach((c, i) => {
|
|
177
486
|
card(IN_L + (i % 3) * (ccw + 22), i < 3 ? 462 : 548, ccw, 76, c, { stroke: C.frame, fs: 12.5, sfs: 10.5 })
|
|
178
487
|
})
|
|
179
|
-
card(IN_L, 636, IN_R - IN_L, 84,
|
|
180
|
-
|
|
181
|
-
'encourage:按实现难度自行决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录,否则记为未定论',
|
|
182
|
-
'★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
|
|
183
|
-
'★ 忠实性缺陷(decision=defect)≠ 命题为假:撤回「已通过」+ 撤回归档证明 + 进「形式化待办」,绝不记成 0/假',
|
|
184
|
-
], { stroke: C.gate, fs: 12, sfs: 10.5 })
|
|
185
|
-
card(IN_L, 732, IN_R - IN_L, 40, [
|
|
186
|
-
'subagent/end 之后框架按序推进:① 邮箱非空 → 唤醒非忙收件人 ② open 任务 → 唤醒提议人/认领人 ③ 待验证提议 → 开会 ④ 否则心跳:唤醒最久未活跃者并给它开会',
|
|
187
|
-
], { 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 })
|
|
188
490
|
|
|
189
491
|
// ---- 数据面 ----
|
|
190
492
|
const dw = (MAIN_R - MAIN_L - 2 * 20) / 3
|
|
191
|
-
band(MAIN_L, 816, dw, 250,
|
|
192
|
-
'本人可写 · 他人只读(跨读被鼓励)—— 直接经 fs 写入,框架只登记',
|
|
493
|
+
band(MAIN_L, 816, dw, 250, T.libraries.title, T.libraries.sub,
|
|
193
494
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
194
|
-
card(IN_L, 872, dw - 44, 176,
|
|
195
|
-
'Progress/<r-id>/progress.md',
|
|
196
|
-
'本人持续进展(追加式,断点续跑靠它重种化)',
|
|
197
|
-
'',
|
|
198
|
-
'Propos/<r-id>/<p-id>.md 命题',
|
|
199
|
-
'Methods/<r-id>/<m-id>.md 理论 / 方法 / 工具',
|
|
200
|
-
'Subproblems/<r-id>/<s-id>.md 子问题',
|
|
201
|
-
'',
|
|
202
|
-
'每条记录必填:价值程度 / 动机用途计划 / 自身概率估计',
|
|
203
|
-
], { 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 })
|
|
204
496
|
|
|
205
497
|
const B2 = MAIN_L + dw + 20
|
|
206
|
-
band(B2, 816, dw, 250,
|
|
207
|
-
'Shared/ 是协作产物;Verified/ 是唯一定论,写入后只读',
|
|
498
|
+
band(B2, 816, dw, 250, T.shared.title, T.shared.sub,
|
|
208
499
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
209
|
-
card(B2 + 22, 872, dw - 44, 176,
|
|
210
|
-
'Shared/meetings/<id>.md 会议录 + 结论',
|
|
211
|
-
'Shared/debates/<target>.md 辩论录(初评 + 重评)',
|
|
212
|
-
'Shared/taskboard.md 任务板(提议 / 认领)',
|
|
213
|
-
'Shared/meetings/brainstorm.md 起始头脑风暴',
|
|
214
|
-
'',
|
|
215
|
-
'Verified/命题/<id>.md (或 问题/、方法→可信断言)',
|
|
216
|
-
'Problems/<id>.md 原问题陈述',
|
|
217
|
-
'未全票 → 留在来源库 + 平均概率 + 辩论录',
|
|
218
|
-
], { 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 })
|
|
219
501
|
|
|
220
502
|
const B3 = B2 + dw + 20
|
|
221
|
-
band(B3, 816, MAIN_R - B3, 250,
|
|
222
|
-
'框架自己的状态:注册表、邮件箱、锁、进程纪元',
|
|
503
|
+
band(B3, 816, MAIN_R - B3, 250, T.state.title, T.state.sub,
|
|
223
504
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
224
|
-
card(B3 + 22, 872, MAIN_R - B3 - 44, 176,
|
|
225
|
-
'State/residents.json 常驻注册表',
|
|
226
|
-
'State/mailboxes.json 邮件箱(谁还没读)',
|
|
227
|
-
'State/taskboard.json 任务板机器态',
|
|
228
|
-
'State/decisions.json 关键决策留痕',
|
|
229
|
-
'State/session.json 活动日志(人可读)',
|
|
230
|
-
'State/settings.json 参数(set 立即生效)',
|
|
231
|
-
'State/formal.json Lean 形式化记录(非 off 档)',
|
|
232
|
-
'fileOwner 写锁 · projectLock · processEpoch · abort→resume',
|
|
233
|
-
], { 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 })
|
|
234
506
|
|
|
235
507
|
// ---- 哲学红线 ----
|
|
236
|
-
band(MAIN_L, 1102, MAIN_R - MAIN_L, 118,
|
|
237
|
-
'这五条是设计本体,不是实现细节;改它们等于换一个架构',
|
|
508
|
+
band(MAIN_L, 1102, MAIN_R - MAIN_L, 118, T.redlines.title, T.redlines.sub,
|
|
238
509
|
{ fill: C.gateBg, stroke: C.gate, titleFill: C.gate })
|
|
239
510
|
{
|
|
240
|
-
const red = [
|
|
241
|
-
['框架绝不指派任务', '方向 / 分工 / 谁做什么', '只能由常驻留言与开会决定'],
|
|
242
|
-
['定论必须全体一致', '没有 forced / flat / 近共识收口', '强分歧只能留在库里'],
|
|
243
|
-
['只有 Verified/ 绝对可信', '其余(方法库未验证断言等)', '只作经验参考'],
|
|
244
|
-
['常驻不直接互调', '留言一律经框架 relay', '(模拟收件箱,先落盘再投递)'],
|
|
245
|
-
['人工干预不改变自组织', '留言 / 开会 / 增删成员都可用', '但"之后怎么做"仍由常驻决定'],
|
|
246
|
-
]
|
|
247
511
|
const rw = (IN_R - IN_L - 4 * 16) / 5
|
|
248
|
-
|
|
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 }))
|
|
249
513
|
}
|
|
250
514
|
|
|
251
515
|
// ---- 连线:人 / 助手 ↔ 框架(左侧通道,不穿过常驻)----
|
|
252
516
|
const CHX = 129
|
|
253
517
|
polyline([[CHX, 338], [CHX, 470], [MAIN_L, 470]], { color: C.human, width: 2 })
|
|
254
|
-
plain(CHX, 384,
|
|
255
|
-
plain(CHX, 404,
|
|
256
|
-
plain(CHX, 422,
|
|
257
|
-
plain(CHX, 446,
|
|
258
|
-
plain(CHX, 506,
|
|
259
|
-
plain(CHX, 526,
|
|
260
|
-
plain(CHX, 544,
|
|
261
|
-
plain(CHX, 572,
|
|
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 })
|
|
262
526
|
|
|
263
527
|
// ---- 连线:常驻 ↔ 框架(提示词 / 回执)----
|
|
264
528
|
arrow(600, 338, 600, 400, { color: C.frame, marker: 'bothFrame', width: 2 })
|
|
265
529
|
arrow(1360, 338, 1360, 400, { color: C.frame, marker: 'bothFrame', width: 2 })
|
|
266
|
-
plain(980, 366, '
|
|
267
|
-
|
|
268
|
-
plain(980, 386, 'say / progress / record_* / propose_verify / verdict / vote_solved / input / reject_assign / task_done',
|
|
269
|
-
{ 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 })
|
|
270
532
|
|
|
271
533
|
// ---- 连线:框架 ↔ 数据面 ----
|
|
272
534
|
arrow(560, 784, 560, 816, { color: C.data, marker: 'bothData', width: 2 })
|
|
273
|
-
plain(552, 806,
|
|
535
|
+
plain(552, 806, T.links.persist, { fill: C.data, anchor: 'end', size: 10.5, halo: true })
|
|
274
536
|
arrow(1180, 784, 1180, 816, { color: C.gate, marker: 'bothGate', width: 2, dashed: true })
|
|
275
|
-
plain(1188, 806,
|
|
537
|
+
plain(1188, 806, T.links.notUnanimous, { fill: C.gate, anchor: 'start', size: 10.5, halo: true })
|
|
276
538
|
|
|
277
539
|
// ---- 图例 ----
|
|
278
|
-
plain(52, 1246,
|
|
279
|
-
|
|
280
|
-
[C.human, '人 / 助手(外部接口,不研究不投票)'],
|
|
281
|
-
[C.house, '常驻子代理(有表决权 · 自主方向)'],
|
|
282
|
-
[C.frame, '框架(中继 / 沉淀 / 计数 / 调度)'],
|
|
283
|
-
[C.data, '产物与状态(常驻专属库 / Shared / State)'],
|
|
284
|
-
[C.gate, '定论门槛 / Lean 形式化(可调)'],
|
|
285
|
-
]
|
|
286
|
-
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) => {
|
|
287
542
|
const x = 108 + i * 330
|
|
288
|
-
push(`<rect x="${x}" y="1235" width="14" height="14" rx="3" fill="#ffffff" stroke="${
|
|
543
|
+
push(`<rect x="${x}" y="1235" width="14" height="14" rx="3" fill="#ffffff" stroke="${C[key]}" stroke-width="2"/>`)
|
|
289
544
|
plain(x + 21, 1247, txt, { size: 11, fill: C.ink2, limit: 305, where: 'legend' })
|
|
290
545
|
})
|
|
291
546
|
|
|
@@ -295,10 +550,10 @@ push('</svg>')
|
|
|
295
550
|
mkdirSync(dirname(OUT), { recursive: true })
|
|
296
551
|
const svg = out.join('\n')
|
|
297
552
|
writeFileSync(OUT, svg, 'utf8')
|
|
298
|
-
console.log(
|
|
553
|
+
console.log(T.msg.saved(OUT, svg.length))
|
|
299
554
|
if (warnings.length) {
|
|
300
|
-
console.error(
|
|
301
|
-
for (const w of warnings) console.error(
|
|
555
|
+
console.error(T.msg.warnHeader)
|
|
556
|
+
for (const w of warnings) console.error(T.msg.warnPrefix + w)
|
|
302
557
|
process.exit(1)
|
|
303
558
|
}
|
|
304
|
-
console.log(
|
|
559
|
+
console.log(T.msg.ok)
|