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.
- package/README.en.md +876 -0
- package/README.md +11 -7
- package/docs/AUDIT-CHECKLIST.md +61 -0
- 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 +387 -127
- package/docs/generate_framework_diagram_v5.mjs +372 -109
- package/docs/release-notes/RELEASE-NOTES-2.3.15.md +137 -0
- package/docs/release-notes/RELEASE-NOTES-2.3.16.md +165 -0
- package/docs/render_framework_diagram_png.mjs +273 -0
- package/docs/test-timing.md +4 -2
- package/docs//346/236/266/346/236/204/345/233/276.md +3 -1
- package/package.json +13 -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
|
@@ -3,25 +3,338 @@
|
|
|
3
3
|
//
|
|
4
4
|
// node docs/generate_framework_diagram_v5.mjs
|
|
5
5
|
// → 写出 示例图/框架图-v5.svg
|
|
6
|
+
// node docs/generate_framework_diagram_v5.mjs --lang=en (也接受 --lang en)
|
|
7
|
+
// → 写出 示例图/框架图-v5-en.svg(同一张图,全部文字换成英文)
|
|
8
|
+
//
|
|
9
|
+
// 图上的**每一句人话**都放在本文件顶部的 zh / en 两张表里,绘图代码只从当前语言的表里取字:
|
|
10
|
+
// 因此两种语言共用同一套几何、配色、坐标、元素顺序与图元,不存在"两套版式各自漂移"的问题。
|
|
11
|
+
// 无参数时取到的就是中文原文,输出与加语言开关之前**逐字节相同**。
|
|
6
12
|
//
|
|
7
13
|
// v2/v3 用 matplotlib 脚本生成 PNG;v4/v5 用 Node 直接生成 **SVG**:
|
|
8
14
|
// · 本仓库的运行时就包含 Node,不需要额外装 Python / matplotlib;
|
|
9
15
|
// · SVG 是纯文本,diff 友好、可评审、任意缩放不糊。
|
|
10
|
-
// 需要 PNG
|
|
11
|
-
// chrome --headless=new --window-size=1720,
|
|
16
|
+
// 需要 PNG(市场轮播 / 预览图)时**不要**直接照着"窗口尺寸 = 画布尺寸"截图:
|
|
17
|
+
// chrome --headless=new --window-size=1720,1204 --screenshot=示例图/框架图-v5.png 示例图/框架图-v5.svg ← 会静默裁掉底部
|
|
18
|
+
// 原因:`--screenshot` 截的是**窗口**,而页面**视口**比窗口矮约 96px(浏览器 UI 占位),
|
|
19
|
+
// 画布底部那一截根本没被绘制——而 PNG 的尺寸又恰好等于画布尺寸,所以从尺寸上完全看不出来。
|
|
20
|
+
// (2.3.15 修掉的真实缺陷:v4/v5 的 PNG 因此丢了最下面的图例行,且连续两版没人发现。)
|
|
21
|
+
// 正确做法(按 SVG 自身尺寸渲染、裁掉多余高度、并校验墨迹到达内容底边):
|
|
22
|
+
// node docs/render_framework_diagram_png.mjs 示例图/框架图-v5.svg 示例图/框架图-v5.png 2
|
|
12
23
|
//
|
|
13
24
|
// 版式约定(改布局时请遵守,否则会重叠):
|
|
14
25
|
// · 每个 band 的标题在 y+27、副标题在 y+47,**内容从 y+56 开始**;
|
|
15
26
|
// · 左侧 x=40..214 是一条「控制面通道」,只有所办↔框架的连线走这里,
|
|
16
27
|
// 研究所/框架两个 band 从 x=226 开始 —— 这样控制面的箭头不会穿过所内成员;
|
|
17
|
-
// · 生成时会估算文字宽度,超出容器的行会打印 WARN
|
|
28
|
+
// · 生成时会估算文字宽度,超出容器的行会打印 WARN,便于立刻发现溢出(两种语言同样生效)。
|
|
18
29
|
// ============================================================
|
|
19
30
|
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
20
31
|
import { dirname, join } from 'node:path'
|
|
21
32
|
import { fileURLToPath } from 'node:url'
|
|
22
33
|
|
|
23
34
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
24
|
-
|
|
35
|
+
|
|
36
|
+
// ---------- 语言 ----------
|
|
37
|
+
// `--lang=en` / `--lang en` → 英文;缺省或 `--lang=zh` / `--lang zh` → 中文(保持原样)。
|
|
38
|
+
function readLang(argv) {
|
|
39
|
+
for (let i = 0; i < argv.length; i++) {
|
|
40
|
+
const a = argv[i]
|
|
41
|
+
if (a === '--lang') { if (argv[i + 1] !== undefined) return argv[i + 1] }
|
|
42
|
+
else if (a.startsWith('--lang=')) return a.slice('--lang='.length)
|
|
43
|
+
}
|
|
44
|
+
return 'zh'
|
|
45
|
+
}
|
|
46
|
+
const LANG = readLang(process.argv.slice(2)).toLowerCase()
|
|
47
|
+
if (LANG !== 'zh' && LANG !== 'en') {
|
|
48
|
+
console.error(`未知语言 "${LANG}"(只支持 zh / en)—— 用法: node docs/generate_framework_diagram_v5.mjs [--lang=zh|en]`)
|
|
49
|
+
process.exit(2)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============================================================
|
|
53
|
+
// 文案表:zh 是图上原有的全部中文(一字不改),en 是同一句话的英文。
|
|
54
|
+
// 排版相关的容器假设(每张卡几行、每行多长)在这里必须与 zh 对齐或更短。
|
|
55
|
+
// 工具名(vibe_v5_*)、参数名、路径(Verified/、Shared/…)、JSON 键、标识符、Lean / DSH / Cordis 一律原样保留。
|
|
56
|
+
// ============================================================
|
|
57
|
+
const STRINGS = {
|
|
58
|
+
zh: {
|
|
59
|
+
doc: {
|
|
60
|
+
ariaLabel: 'Vibe Math V5 研究所架构图',
|
|
61
|
+
warnHeading: '\n布局告警(文字可能溢出容器):',
|
|
62
|
+
warnPrefix: ' WARN ',
|
|
63
|
+
saved: 'saved ',
|
|
64
|
+
noWarn: 'layout: no overflow warnings',
|
|
65
|
+
overflowHint: '文字可能溢出容器',
|
|
66
|
+
cardTooShort: (n, need, h) => `${n} 行需要高 ${need} > 容器 ${h}`,
|
|
67
|
+
},
|
|
68
|
+
title: 'Vibe Math V5 —— 研究所体系',
|
|
69
|
+
subtitle: '框架只是媒介:中继消息 · 沉淀产物 · 计数表决 · 管理上下文。它绝不指派任务 —— 组织与分派是所内院士的职责',
|
|
70
|
+
|
|
71
|
+
gate: {
|
|
72
|
+
bandTitle: '求真门槛 —— 唯一让对象进入 Verified/ 的规则',
|
|
73
|
+
bandSub: 'm = min(quorumCap, 在册有表决权人数) · 票是 [0,1] 概率:恰好 1 = 断言为真,恰好 0 = 断言为假,严格介于两者 = 弃权/存疑(不计入 m,计入全组平均)',
|
|
74
|
+
cards: [
|
|
75
|
+
['布尔票 ≥ m 且全部为 1 或全部为 0 → 定论,写入 Verified/'],
|
|
76
|
+
['同时出现 1 与 0 → 阻塞:少数派无法靠别人弃权把结论推过去'],
|
|
77
|
+
['未达门槛 → 留在原库 + 全组平均概率 + 完整辩论录(不强行裁决)'],
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
office: {
|
|
82
|
+
bandTitle: '所办(会话根代理 / 人)—— 研究所的对外接口',
|
|
83
|
+
bandSub: '不参与研究 · 不投票 · 只汇报与转达人的指令;代持平台要求的一次性创建权',
|
|
84
|
+
cards: [
|
|
85
|
+
['自然语言接口 + 汇报者', '「求解 XX」「进度如何」「暂停」'],
|
|
86
|
+
['vibe_v5_configure → start', 'message / meeting / hire / fire'],
|
|
87
|
+
['pause / resume / stop', 'set 调参(立即生效)'],
|
|
88
|
+
['/v5 斜杠命令(与工具等价)', 'configure|start|resume|pause|status|report|members|message|meeting|hire|fire|set'],
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
institute: {
|
|
93
|
+
bandTitle: '研究所(所内自治)—— 编制、组织与分派都在成员之间完成,框架永不指派',
|
|
94
|
+
bandSub: '院士 acad:领头人 / 组织与协调中心 · 常驻研究员 r-n:有表决权 · 临时工 t-n:无表决权(由雇主雇入与解雇)',
|
|
95
|
+
acadCard: [
|
|
96
|
+
'院士 acad',
|
|
97
|
+
'领头人 · 组织与协调中心',
|
|
98
|
+
'L1 建立全所视图(overview)',
|
|
99
|
+
'L2 拆解并分派任务(assign)',
|
|
100
|
+
'L3 设定优先级(prioritize)',
|
|
101
|
+
'L4 召集并主持会议(convene)',
|
|
102
|
+
'L5 督导进度(nudge)',
|
|
103
|
+
'L6 调配临时工 · L7 对外代表',
|
|
104
|
+
],
|
|
105
|
+
acadNote: ['四条边界:一票与他人等重 / 分派的是工作不是结论 /', '成员可据理反对 / 不能自我扩张编制'],
|
|
106
|
+
researcherLabel: '常驻研究员(有表决权 · 可自主雇佣/解雇自己的临时工)',
|
|
107
|
+
researcherCard: ['常驻研究员 r-', 'continuable 持久会话 · 自主方向 · 主动汇报'],
|
|
108
|
+
tempLabel: '临时工(无表决权 · 为特定任务临时雇入 · 雇主或院士可解雇)',
|
|
109
|
+
tempCard: ['临时工 t-', '由 r-', ' 雇入 · 可读/可想/可发言/可写自己的库'],
|
|
110
|
+
houseNote: '所内组织动作(分派 · 优先级 · 督办 · 会议 · 用人)由院士发起,不是框架行为 —— 框架只负责送达',
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
framework: {
|
|
114
|
+
bandTitle: '框架 vibe-v5 —— 只是媒介(middleware):中继 · 沉淀 · 计数 · 调度',
|
|
115
|
+
bandSub: '每轮发「状态块 + 本轮问句」(规章在 persona 里,不进对话);成员回一个 JSON:say / progress / record / verdict / task_* / hire / fire / reject_assign / input / vote_solved',
|
|
116
|
+
chips: [
|
|
117
|
+
['消息中继(持久邮箱)', '群聊 / 私信 / 致全体表决者', '所办通知 / 分派 / 督办 / 框架提示', '逐收件人落盘 · 先落盘再投递 · 群聊合批'],
|
|
118
|
+
['会议 / 辩论', '议程 · 实时名册对账 · 随机发言序', '与验证互斥:任一方进行中,另一方排队', '看门狗:卡死即放弃并回到自组织'],
|
|
119
|
+
['任务板(CAS + DAG)', '开 / 认领 / 释放 / 完成 / 重开 / 分派', '版本号比较交换 · 依赖环检测', '写范围重叠告警 · 解雇自动收回'],
|
|
120
|
+
['m 票共识验证', '发起 → 独立初评 → 公开辩论重投', '布尔一致 + ≥ m 才定论', 'Verified/ 卡 + 辩论录 + 全组平均概率'],
|
|
121
|
+
['上下文与活性', '阈值压缩(人设不丢)· 免轮询活动等待', '优先级调度 · 心跳永远重武装', '并发闸 maxParallel · 停滞自动开会'],
|
|
122
|
+
['编制与雇佣', '院士/研究员可雇自己的临时工', '雇主或院士可真实解雇(释放子会话)', '代号永不复用 · 按人/全所双配额'],
|
|
123
|
+
],
|
|
124
|
+
leanCard: [
|
|
125
|
+
'Lean 形式化验证(可调参数 formalVerify = off / encourage / require)',
|
|
126
|
+
'encourage:验证时按实现难度决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录',
|
|
127
|
+
'★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
|
|
128
|
+
'★ 忠实性缺陷(decision=defect)≠ 命题为假:撤回「已通过」+ 撤回归档证明 + 进「形式化待办」,绝不记成 0/假',
|
|
129
|
+
'归档:证明 → Verified/Lean/<对象>.lean|可复用定义 → VibeMath/Formal/Lib/|已证引理 → VibeMath/Formal/Proved/',
|
|
130
|
+
],
|
|
131
|
+
schedulerCard: [
|
|
132
|
+
'调度器优先级:进行中的会议或验证(二者互斥,永不同时)→ 队列中的验证 → 暂存会议 → 已认领/被分派的在办任务(按 activityTimeoutMs 节流)→ 加急邮件 → 群聊摘要 → 停滞自动开会 → 兜底心跳',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
projection: {
|
|
137
|
+
bandTitle: '状态权威源:会话日志的 host-only 投影单元(键 vibeMathV5)',
|
|
138
|
+
bandSub: '副作用只是往会话日志追加事件 —— 不进模型上下文(零 token 成本),checkpoint / restore 交给 DSH',
|
|
139
|
+
eventsCard: [
|
|
140
|
+
'applyV5Event(纯折叠,只此一份)',
|
|
141
|
+
'11 类事件:institute / member / task /',
|
|
142
|
+
'message / delivered / meeting / debate /',
|
|
143
|
+
'verdict / queue / counters / progress',
|
|
144
|
+
'未知或损坏事件 → 跳过并记入 diagnostics:',
|
|
145
|
+
'可用性优先,绝不因一条坏事件卡死全场',
|
|
146
|
+
],
|
|
147
|
+
checkpointCard: [
|
|
148
|
+
'checkpoint / restore / resume',
|
|
149
|
+
'投影随会话日志一起 checkpoint;restore',
|
|
150
|
+
'时从快照 + 日志尾部重新折叠 → 跨进程与',
|
|
151
|
+
'同进程恢复走同一条代码路径',
|
|
152
|
+
'回退:宿主无 sessionProjections 时改用',
|
|
153
|
+
'加固 JSON:State/<institute>.v5state.json',
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
files: {
|
|
158
|
+
bandTitle: '文件面:人可读产物(投影之外的一切都只是镜像)',
|
|
159
|
+
bandSub: '共识的权威在投影;文件是工作区与可读产物,手工改坏不会破坏研究所',
|
|
160
|
+
card: [
|
|
161
|
+
'Members/<id>/Progress|Propos|Methods|Subproblems/',
|
|
162
|
+
'只有本人可写,人人可读(跨读被鼓励)',
|
|
163
|
+
'Shared/Chat/*.md · Shared/Meetings/<mt-id>.md · Shared/Debates/<obj>.md',
|
|
164
|
+
'Shared/TaskBoard.md · Institutes.md(编制镜像)· State/README.md(说明此处非权威)',
|
|
165
|
+
'Problems/<id>.md(原问题)· Problems/conclusion.md(结题)· Verified/<类型>/<id>.md(定论,只读)',
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
control: {
|
|
170
|
+
note1: '所办不投票', note2: '不能让结论变真', note3: '也不替成员', note4: '思考或署名',
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
links: {
|
|
174
|
+
promptLabel: '↑ 每轮提示词(状态块 + 本轮问句) ↓ 单个 JSON 回执',
|
|
175
|
+
filesLabel: '读写产物',
|
|
176
|
+
chLabel: '工具面',
|
|
177
|
+
chCommand: 'vibe_v5_*',
|
|
178
|
+
chSlash: '/v5 命令',
|
|
179
|
+
chDown: '↓',
|
|
180
|
+
reportLabel: '汇报',
|
|
181
|
+
chUp: '↑',
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
legend: {
|
|
185
|
+
heading: '图例',
|
|
186
|
+
items: [
|
|
187
|
+
'所办 / 人(外部接口,不研究不投票)',
|
|
188
|
+
'所内成员与所内组织(虚线 = 不由框架执行)',
|
|
189
|
+
'框架(中继 / 沉淀 / 计数 / 调度)',
|
|
190
|
+
'状态与产物',
|
|
191
|
+
'求真门槛 / Lean 形式化(可调)',
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
en: {
|
|
197
|
+
doc: {
|
|
198
|
+
ariaLabel: 'Vibe Math V5 institute architecture diagram',
|
|
199
|
+
warnHeading: '\nLayout warnings (text may overflow its container):',
|
|
200
|
+
warnPrefix: ' WARN ',
|
|
201
|
+
saved: 'saved ',
|
|
202
|
+
noWarn: 'layout: no overflow warnings',
|
|
203
|
+
overflowHint: 'text may overflow its container',
|
|
204
|
+
cardTooShort: (n, need, h) => `${n} lines need height ${need} > container ${h}`,
|
|
205
|
+
},
|
|
206
|
+
title: 'Vibe Math V5 —— Institute System',
|
|
207
|
+
subtitle: 'The framework is only a medium: relaying messages · persisting artifacts · counting votes · managing context. It never assigns tasks —— organizing and assigning is the academician\'s job',
|
|
208
|
+
|
|
209
|
+
gate: {
|
|
210
|
+
bandTitle: 'Truth gate —— the only rule that lets an object enter Verified/',
|
|
211
|
+
bandSub: 'm = min(quorumCap, number of registered voting members) · a vote is a [0,1] probability: exactly 1 = assert true, exactly 0 = assert false, strictly between = abstention/doubt (not counted toward m, counted in the group average)',
|
|
212
|
+
cards: [
|
|
213
|
+
['Boolean votes ≥ m and all 1 or all 0 → conclusion, written to Verified/'],
|
|
214
|
+
['Both 1 and 0 → blocked: the minority cannot push a conclusion through'],
|
|
215
|
+
['Below quorum → stays in the library + average probability + debate record'],
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
office: {
|
|
220
|
+
bandTitle: 'Institute office (session root agent / human) —— the institute\'s external interface',
|
|
221
|
+
bandSub: 'Does not research · does not vote · only reports and relays the human\'s instructions; holds the one-off creation rights the platform requires',
|
|
222
|
+
cards: [
|
|
223
|
+
['Natural-language interface + reporter', '"solve XX" "how is it going" "pause"'],
|
|
224
|
+
['vibe_v5_configure → start', 'message / meeting / hire / fire'],
|
|
225
|
+
['pause / resume / stop', 'set parameters (effective immediately)'],
|
|
226
|
+
['/v5 slash commands (equivalent to the tools)', 'configure|start|resume|pause|status|report|members|message|meeting|hire|fire|set'],
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
institute: {
|
|
231
|
+
bandTitle: 'Institute (internal autonomy) —— roster, organization and assignment all happen among members; the framework never assigns',
|
|
232
|
+
bandSub: 'Academician acad: leader / center of organization and coordination · resident researcher r-n: voting rights · temp worker t-n: no voting rights (hired and fired by its employer)',
|
|
233
|
+
acadCard: [
|
|
234
|
+
'Academician acad',
|
|
235
|
+
'Leader · organization and coordination center',
|
|
236
|
+
'L1 institute-wide overview (overview)',
|
|
237
|
+
'L2 decompose and assign tasks (assign)',
|
|
238
|
+
'L3 set priorities (prioritize)',
|
|
239
|
+
'L4 convene and chair meetings (convene)',
|
|
240
|
+
'L5 supervise progress (nudge)',
|
|
241
|
+
'L6 move temp workers · L7 external',
|
|
242
|
+
],
|
|
243
|
+
acadNote: ['Four boundaries: one vote, equally weighted /', 'what is assigned is work, not conclusions /'],
|
|
244
|
+
researcherLabel: 'Resident researcher (voting rights · may autonomously hire/fire its own temp workers)',
|
|
245
|
+
researcherCard: ['Resident researcher r-', 'continuable persistent session · own direction'],
|
|
246
|
+
tempLabel: 'Temp worker (no voting rights · hired temporarily for a specific task · fired by its employer or the academician)',
|
|
247
|
+
tempCard: ['Temp worker t-', 'Hired by r-', ' · read/think/speak, write own library'],
|
|
248
|
+
houseNote: 'In-institute organization (assignment · priority · nudge · meetings · staffing) is initiated by the academician, not the framework —— it only delivers',
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
framework: {
|
|
252
|
+
bandTitle: 'Framework vibe-v5 —— only a medium (middleware): relay · persistence · counting · scheduling',
|
|
253
|
+
bandSub: 'Each round: "state block + this round\'s question" (the charter is in the persona); a member returns one JSON: say / progress / record / verdict / task_* / hire / fire / reject_assign / input / vote_solved',
|
|
254
|
+
chips: [
|
|
255
|
+
['Message relay (persistent mailbox)', 'group chat / direct message / to all voters', 'office notice / assignment / nudge / framework hint', 'persisted per recipient · written before delivery · group chat batched'],
|
|
256
|
+
['Meeting / debate', 'agenda · live roster check · random speaking order', 'mutually exclusive with verification: while one runs, the other queues', 'watchdog: if it hangs, abandon it and return to self-organization'],
|
|
257
|
+
['Task board (CAS + DAG)', 'open / claim / release / complete / reopen / assign', 'compare-and-set revision · dependency cycle detection', 'write-scope overlap warning · firing reclaims tasks'],
|
|
258
|
+
['m-vote consensus verification', 'propose → independent initial assessment → open debate and re-vote', 'Boolean unanimity + ≥ m decides it', 'Verified/ card + debate record + group average probability'],
|
|
259
|
+
['Context and liveness', 'threshold compaction (persona kept) · poll-free activity wait', 'priority scheduling · heartbeat always re-armed', 'concurrency gate maxParallel · automatic meeting on stall'],
|
|
260
|
+
['Roster and hiring', 'academician/researchers can hire their own temp workers', 'employer or academician can really fire (releases the child session)', 'codenames never reused · dual quota per member / institute-wide'],
|
|
261
|
+
],
|
|
262
|
+
leanCard: [
|
|
263
|
+
'Lean formal verification (adjustable parameter formalVerify = off / encourage / require)',
|
|
264
|
+
'encourage: at verification time, implementation difficulty decides whether to formalize; require: a true/false conclusion needs "Lean passed" or an explicit blocking record first',
|
|
265
|
+
'★ Once Lean passes, the object of review changes: no longer "is the derivation correct", but "are Lean\'s definitions/objects/conditions/assumptions/conclusions faithful to the proposition"',
|
|
266
|
+
'★ A fidelity defect (decision=defect) ≠ the proposition is false: revoke "passed" + revoke the archived proof + enter the "formalization TODO", never recorded as 0/false',
|
|
267
|
+
'Archive: proof → Verified/Lean/<object>.lean | reusable definition → VibeMath/Formal/Lib/ | proved lemma → VibeMath/Formal/Proved/',
|
|
268
|
+
],
|
|
269
|
+
schedulerCard: [
|
|
270
|
+
'Scheduler priority: meeting/verification (mutually exclusive) → queued verification → meetings → active tasks (throttled by activityTimeoutMs) → mail → chat digest → meeting on stall → heartbeat',
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
projection: {
|
|
275
|
+
bandTitle: 'Source of truth: host-only projection cell of the session log (key vibeMathV5)',
|
|
276
|
+
bandSub: 'The only side effect is appending events to the session log, outside the model context (zero token cost)',
|
|
277
|
+
eventsCard: [
|
|
278
|
+
'applyV5Event (pure fold, the only copy)',
|
|
279
|
+
'11 kinds of events: institute / member / task /',
|
|
280
|
+
'message / delivered / meeting / debate /',
|
|
281
|
+
'verdict / queue / counters / progress',
|
|
282
|
+
'unknown/bad event → skipped and recorded in diagnostics:',
|
|
283
|
+
'availability first, one bad event never stalls the field',
|
|
284
|
+
],
|
|
285
|
+
checkpointCard: [
|
|
286
|
+
'checkpoint / restore / resume',
|
|
287
|
+
'the projection is checkpointed with the session log; restore',
|
|
288
|
+
're-folds snapshot + log tail → across and within processes',
|
|
289
|
+
'one process recovery takes the same code path',
|
|
290
|
+
'fallback: host without sessionProjections → hardened',
|
|
291
|
+
'JSON: State/<institute>.v5state.json',
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
files: {
|
|
296
|
+
bandTitle: 'File plane: human-readable artifacts (everything outside the projection is only a mirror)',
|
|
297
|
+
bandSub: 'The authority for consensus is the projection; files are readable artifacts, breaking them by hand does not damage it',
|
|
298
|
+
card: [
|
|
299
|
+
'Members/<id>/Progress|Propos|Methods|Subproblems/',
|
|
300
|
+
'only the owner can write, everyone can read (cross-reading is encouraged)',
|
|
301
|
+
'Shared/Chat/*.md · Shared/Meetings/<mt-id>.md · Shared/Debates/<obj>.md',
|
|
302
|
+
'Shared/TaskBoard.md · Institutes.md (roster mirror) · State/README.md (says the authority is not here)',
|
|
303
|
+
'Problems/<id>.md (original problem) · Problems/conclusion.md (conclusion) · Verified/<type>/<id>.md (conclusion, read-only)',
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
control: {
|
|
308
|
+
note1: 'the office does not vote', note2: 'cannot make a conclusion true', note3: 'nor think or sign', note4: 'on a member\'s behalf',
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
links: {
|
|
312
|
+
promptLabel: '↑ per-round prompt (state block + this round\'s question) ↓ single JSON receipt',
|
|
313
|
+
filesLabel: 'read/write artifacts',
|
|
314
|
+
chLabel: 'Tool surface',
|
|
315
|
+
chCommand: 'vibe_v5_*',
|
|
316
|
+
chSlash: '/v5 commands',
|
|
317
|
+
chDown: '↓',
|
|
318
|
+
reportLabel: 'Report',
|
|
319
|
+
chUp: '↑',
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
legend: {
|
|
323
|
+
heading: 'Legend',
|
|
324
|
+
items: [
|
|
325
|
+
'Institute office / human (no research, no vote)',
|
|
326
|
+
'Members and org (dashed = not framework)',
|
|
327
|
+
'Framework (relay / persist / count / sched.)',
|
|
328
|
+
'State and artifacts',
|
|
329
|
+
'Truth gate / Lean formalization (adjustable)',
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const T = STRINGS[LANG] // 绘图代码只会读这张表
|
|
336
|
+
|
|
337
|
+
const OUT = join(ROOT, '示例图', LANG === 'en' ? '框架图-v5-en.svg' : '框架图-v5.svg')
|
|
25
338
|
|
|
26
339
|
const W = 1720, H = 1204
|
|
27
340
|
const FONT = "'Microsoft YaHei','PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Source Han Sans SC',sans-serif"
|
|
@@ -48,7 +361,7 @@ const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replac
|
|
|
48
361
|
const textWidth = (s, fs) => [...String(s)].reduce((n, ch) => n + (/[\u2e80-\u9fff\uff00-\uffef]/.test(ch) ? 1 : 0.56), 0) * fs
|
|
49
362
|
const fits = (s, fs, limit, where) => {
|
|
50
363
|
const w = textWidth(s, fs)
|
|
51
|
-
if (w > limit) warnings.push(`${where}:
|
|
364
|
+
if (w > limit) warnings.push(`${where}: ${T.doc.overflowHint} ${w.toFixed(0)} > ${limit.toFixed(0)} — ${String(s).slice(0, 42)}…`)
|
|
52
365
|
return w
|
|
53
366
|
}
|
|
54
367
|
|
|
@@ -71,7 +384,7 @@ function card(x, y, w, h, lines, { fill = '#ffffff', stroke = C.frame, fs = 13,
|
|
|
71
384
|
const cx = x + w / 2
|
|
72
385
|
const lead = fs + 4
|
|
73
386
|
const total = (lines.length - 1) * lead
|
|
74
|
-
if (total + fs > h) warnings.push(`card@${x},${y}: ${lines.length
|
|
387
|
+
if (total + fs > h) warnings.push(`card@${x},${y}: ${T.doc.cardTooShort(lines.length, (total + fs).toFixed(0), h)}`)
|
|
75
388
|
let ty = y + h / 2 - total / 2 + fs * 0.36
|
|
76
389
|
lines.forEach((ln, i) => {
|
|
77
390
|
const size = i === 0 ? fs : sfs
|
|
@@ -104,7 +417,7 @@ function polyline(points, { color = C.human, width = 2, dashed = false, marker =
|
|
|
104
417
|
|
|
105
418
|
// ---------- document ----------
|
|
106
419
|
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="
|
|
420
|
+
push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="${esc(T.doc.ariaLabel)}">`)
|
|
108
421
|
push(`<defs>
|
|
109
422
|
<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
423
|
<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>
|
|
@@ -116,139 +429,89 @@ push(`<rect width="${W}" height="${H}" fill="#ffffff"/>`)
|
|
|
116
429
|
push(`<g font-family="${FONT}">`)
|
|
117
430
|
|
|
118
431
|
// ---- 标题 ----
|
|
119
|
-
plain(W / 2, 40,
|
|
120
|
-
plain(W / 2, 67,
|
|
432
|
+
plain(W / 2, 40, T.title, { size: 27, fill: C.ink, anchor: 'middle', weight: 700 })
|
|
433
|
+
plain(W / 2, 67, T.subtitle,
|
|
121
434
|
{ size: 13, fill: C.mute, anchor: 'middle', limit: W - 80, where: 'subtitle' })
|
|
122
435
|
|
|
123
436
|
// ---- 求真门槛 ----
|
|
124
|
-
band(FULL_L, 84, FULL_R - FULL_L, 94,
|
|
125
|
-
'm = min(quorumCap, 在册有表决权人数) · 票是 [0,1] 概率:恰好 1 = 断言为真,恰好 0 = 断言为假,严格介于两者 = 弃权/存疑(不计入 m,计入全组平均)',
|
|
437
|
+
band(FULL_L, 84, FULL_R - FULL_L, 94, T.gate.bandTitle, T.gate.bandSub,
|
|
126
438
|
{ fill: C.gateBg, stroke: C.gate, titleFill: C.gate })
|
|
127
|
-
card(62, 140, 506, 28, [
|
|
128
|
-
card(588, 140, 506, 28, [
|
|
129
|
-
card(1114, 140, 524, 28, [
|
|
439
|
+
card(62, 140, 506, 28, T.gate.cards[0], { stroke: C.gate, fs: 12 })
|
|
440
|
+
card(588, 140, 506, 28, T.gate.cards[1], { stroke: C.gate, fs: 12 })
|
|
441
|
+
card(1114, 140, 524, 28, T.gate.cards[2], { stroke: C.gate, fs: 12 })
|
|
130
442
|
|
|
131
443
|
// ---- 所办 ----
|
|
132
|
-
band(FULL_L, 190, FULL_R - FULL_L, 100,
|
|
133
|
-
'不参与研究 · 不投票 · 只汇报与转达人的指令;代持平台要求的一次性创建权',
|
|
444
|
+
band(FULL_L, 190, FULL_R - FULL_L, 100, T.office.bandTitle, T.office.bandSub,
|
|
134
445
|
{ fill: C.humanBg, stroke: C.human, titleFill: C.human })
|
|
135
|
-
card(62, 246, 372, 38, [
|
|
136
|
-
card(452, 246, 372, 38, [
|
|
137
|
-
card(842, 246, 330, 38, [
|
|
138
|
-
card(1190, 246, 448, 38, [
|
|
446
|
+
card(62, 246, 372, 38, T.office.cards[0], { stroke: C.human, fs: 12, sfs: 10.5 })
|
|
447
|
+
card(452, 246, 372, 38, T.office.cards[1], { stroke: C.human, fs: 12, sfs: 10.5 })
|
|
448
|
+
card(842, 246, 330, 38, T.office.cards[2], { stroke: C.human, fs: 12, sfs: 10.5 })
|
|
449
|
+
card(1190, 246, 448, 38, T.office.cards[3],
|
|
139
450
|
{ stroke: C.human, fs: 12, sfs: 9 })
|
|
140
451
|
|
|
141
452
|
// ---- 研究所 ----
|
|
142
|
-
band(MAIN_L, 302, MAIN_R - MAIN_L, 240,
|
|
143
|
-
'院士 acad:领头人 / 组织与协调中心 · 常驻研究员 r-n:有表决权 · 临时工 t-n:无表决权(由雇主雇入与解雇)',
|
|
453
|
+
band(MAIN_L, 302, MAIN_R - MAIN_L, 240, T.institute.bandTitle, T.institute.bandSub,
|
|
144
454
|
{ fill: C.houseBg, stroke: C.house, titleFill: '#8a5200' })
|
|
145
|
-
card(IN_L, 362, 300, 142,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
'L1 建立全所视图(overview)',
|
|
149
|
-
'L2 拆解并分派任务(assign)',
|
|
150
|
-
'L3 设定优先级(prioritize)',
|
|
151
|
-
'L4 召集并主持会议(convene)',
|
|
152
|
-
'L5 督导进度(nudge)',
|
|
153
|
-
'L6 调配临时工 · L7 对外代表',
|
|
154
|
-
], { stroke: C.house, fs: 13.5, sfs: 10.5 })
|
|
155
|
-
plain(IN_L + 150, 520, '四条边界:一票与他人等重 / 分派的是工作不是结论 /', { size: 10, fill: C.gate, anchor: 'middle', limit: 300, where: 'acad note' })
|
|
156
|
-
plain(IN_L + 150, 536, '成员可据理反对 / 不能自我扩张编制', { size: 10, fill: C.gate, anchor: 'middle', limit: 300, where: 'acad note' })
|
|
455
|
+
card(IN_L, 362, 300, 142, T.institute.acadCard, { stroke: C.house, fs: 13.5, sfs: 10.5 })
|
|
456
|
+
plain(IN_L + 150, 520, T.institute.acadNote[0], { size: 10, fill: C.gate, anchor: 'middle', limit: 300, where: 'acad note' })
|
|
457
|
+
plain(IN_L + 150, 536, T.institute.acadNote[1], { size: 10, fill: C.gate, anchor: 'middle', limit: 300, where: 'acad note' })
|
|
157
458
|
|
|
158
459
|
const RX = IN_L + 328, RW = 1390 - 328 - 44
|
|
159
460
|
const rcw = (RW - 2 * 20) / 3
|
|
160
|
-
plain(RX, 372,
|
|
461
|
+
plain(RX, 372, T.institute.researcherLabel, { size: 11.5, fill: '#8a5200', weight: 700, limit: RW, where: 'researcher label' })
|
|
161
462
|
for (let i = 0; i < 3; i++) {
|
|
162
|
-
card(RX + i * (rcw + 20), 380, rcw, 52, [
|
|
463
|
+
card(RX + i * (rcw + 20), 380, rcw, 52, [T.institute.researcherCard[0] + (i + 1), T.institute.researcherCard[1]],
|
|
163
464
|
{ stroke: C.house, fs: 13, sfs: 10.5 })
|
|
164
465
|
}
|
|
165
|
-
plain(RX, 452,
|
|
466
|
+
plain(RX, 452, T.institute.tempLabel, { size: 11.5, fill: '#8a5200', weight: 700, limit: RW, where: 'temp label' })
|
|
166
467
|
for (let i = 0; i < 3; i++) {
|
|
167
|
-
card(RX + i * (rcw + 20), 460, rcw, 52, [
|
|
468
|
+
card(RX + i * (rcw + 20), 460, rcw, 52, [T.institute.tempCard[0] + (i + 1), T.institute.tempCard[1] + (i + 1) + T.institute.tempCard[2]],
|
|
168
469
|
{ stroke: '#c99a4a', fs: 13, sfs: 10.5 })
|
|
169
470
|
}
|
|
170
|
-
plain(RX, 536,
|
|
471
|
+
plain(RX, 536, T.institute.houseNote,
|
|
171
472
|
{ size: 10.5, fill: C.mute, limit: RW, where: 'house note' })
|
|
172
473
|
|
|
173
474
|
// ---- 框架 ----
|
|
174
|
-
band(MAIN_L, 566, MAIN_R - MAIN_L, 382,
|
|
175
|
-
'每轮发「状态块 + 本轮问句」(规章在 persona 里,不进对话);成员回一个 JSON:say / progress / record / verdict / task_* / hire / fire / reject_assign / input / vote_solved',
|
|
475
|
+
band(MAIN_L, 566, MAIN_R - MAIN_L, 382, T.framework.bandTitle, T.framework.bandSub,
|
|
176
476
|
{ fill: C.frameBg, stroke: C.frame, titleFill: '#084d80' })
|
|
177
|
-
const chips =
|
|
178
|
-
['消息中继(持久邮箱)', '群聊 / 私信 / 致全体表决者', '所办通知 / 分派 / 督办 / 框架提示', '逐收件人落盘 · 先落盘再投递 · 群聊合批'],
|
|
179
|
-
['会议 / 辩论', '议程 · 实时名册对账 · 随机发言序', '与验证互斥:任一方进行中,另一方排队', '看门狗:卡死即放弃并回到自组织'],
|
|
180
|
-
['任务板(CAS + DAG)', '开 / 认领 / 释放 / 完成 / 重开 / 分派', '版本号比较交换 · 依赖环检测', '写范围重叠告警 · 解雇自动收回'],
|
|
181
|
-
['m 票共识验证', '发起 → 独立初评 → 公开辩论重投', '布尔一致 + ≥ m 才定论', 'Verified/ 卡 + 辩论录 + 全组平均概率'],
|
|
182
|
-
['上下文与活性', '阈值压缩(人设不丢)· 免轮询活动等待', '优先级调度 · 心跳永远重武装', '并发闸 maxParallel · 停滞自动开会'],
|
|
183
|
-
['编制与雇佣', '院士/研究员可雇自己的临时工', '雇主或院士可真实解雇(释放子会话)', '代号永不复用 · 按人/全所双配额'],
|
|
184
|
-
]
|
|
477
|
+
const chips = T.framework.chips
|
|
185
478
|
const ccw = (IN_R - IN_L - 2 * 22) / 3
|
|
186
479
|
chips.forEach((c, i) => {
|
|
187
480
|
card(IN_L + (i % 3) * (ccw + 22), i < 3 ? 620 : 708, ccw, 78, c, { stroke: C.frame, fs: 12.5, sfs: 10.5 })
|
|
188
481
|
})
|
|
189
|
-
card(IN_L, 796, IN_R - IN_L, 90,
|
|
190
|
-
|
|
191
|
-
'encourage:验证时按实现难度决定是否形式化;require:真/假结论必须先有「Lean 通过」或显式阻塞记录',
|
|
192
|
-
'★ 一旦 Lean 通过,审查对象就变了:不再是「推导对不对」,而是「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」',
|
|
193
|
-
'★ 忠实性缺陷(decision=defect)≠ 命题为假:撤回「已通过」+ 撤回归档证明 + 进「形式化待办」,绝不记成 0/假',
|
|
194
|
-
'归档:证明 → Verified/Lean/<对象>.lean|可复用定义 → VibeMath/Formal/Lib/|已证引理 → VibeMath/Formal/Proved/',
|
|
195
|
-
], { stroke: C.gate, fs: 12.5, sfs: 10.5 })
|
|
196
|
-
card(IN_L, 880, IN_R - IN_L, 38, [
|
|
197
|
-
'调度器优先级:进行中的会议或验证(二者互斥,永不同时)→ 队列中的验证 → 暂存会议 → 已认领/被分派的在办任务(按 activityTimeoutMs 节流)→ 加急邮件 → 群聊摘要 → 停滞自动开会 → 兜底心跳',
|
|
198
|
-
], { stroke: C.frame, fs: 11.5 })
|
|
482
|
+
card(IN_L, 796, IN_R - IN_L, 90, T.framework.leanCard, { stroke: C.gate, fs: 12.5, sfs: 10.5 })
|
|
483
|
+
card(IN_L, 880, IN_R - IN_L, 38, T.framework.schedulerCard, { stroke: C.frame, fs: 11.5 })
|
|
199
484
|
|
|
200
485
|
// ---- 数据面 ----
|
|
201
|
-
band(FULL_L, 964, 790, 196,
|
|
202
|
-
'副作用只是往会话日志追加事件 —— 不进模型上下文(零 token 成本),checkpoint / restore 交给 DSH',
|
|
486
|
+
band(FULL_L, 964, 790, 196, T.projection.bandTitle, T.projection.bandSub,
|
|
203
487
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
204
|
-
card(62, 1020, 360, 124,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
'verdict / queue / counters / progress',
|
|
209
|
-
'未知或损坏事件 → 跳过并记入 diagnostics:',
|
|
210
|
-
'可用性优先,绝不因一条坏事件卡死全场',
|
|
211
|
-
], { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
212
|
-
card(434, 1020, 374, 124, [
|
|
213
|
-
'checkpoint / restore / resume',
|
|
214
|
-
'投影随会话日志一起 checkpoint;restore',
|
|
215
|
-
'时从快照 + 日志尾部重新折叠 → 跨进程与',
|
|
216
|
-
'同进程恢复走同一条代码路径',
|
|
217
|
-
'回退:宿主无 sessionProjections 时改用',
|
|
218
|
-
'加固 JSON:State/<institute>.v5state.json',
|
|
219
|
-
], { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
220
|
-
|
|
221
|
-
band(852, 964, FULL_R - 852, 196, '文件面:人可读产物(投影之外的一切都只是镜像)',
|
|
222
|
-
'共识的权威在投影;文件是工作区与可读产物,手工改坏不会破坏研究所',
|
|
488
|
+
card(62, 1020, 360, 124, T.projection.eventsCard, { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
489
|
+
card(434, 1020, 374, 124, T.projection.checkpointCard, { stroke: C.data, fs: 12, sfs: 10.5 })
|
|
490
|
+
|
|
491
|
+
band(852, 964, FULL_R - 852, 196, T.files.bandTitle, T.files.bandSub,
|
|
223
492
|
{ fill: C.dataBg, stroke: C.data, titleFill: '#125a3c' })
|
|
224
|
-
card(874, 1020, FULL_R - 874 - 22, 124,
|
|
225
|
-
'Members/<id>/Progress|Propos|Methods|Subproblems/',
|
|
226
|
-
'只有本人可写,人人可读(跨读被鼓励)',
|
|
227
|
-
'Shared/Chat/*.md · Shared/Meetings/<mt-id>.md · Shared/Debates/<obj>.md',
|
|
228
|
-
'Shared/TaskBoard.md · Institutes.md(编制镜像)· State/README.md(说明此处非权威)',
|
|
229
|
-
'Problems/<id>.md(原问题)· Problems/conclusion.md(结题)· Verified/<类型>/<id>.md(定论,只读)',
|
|
230
|
-
], { stroke: C.data, fs: 11.5, sfs: 10.5 })
|
|
493
|
+
card(874, 1020, FULL_R - 874 - 22, 124, T.files.card, { stroke: C.data, fs: 11.5, sfs: 10.5 })
|
|
231
494
|
|
|
232
495
|
// ---- 连线:控制面(左侧通道,不穿过所内成员) ----
|
|
233
496
|
const CHX = 127
|
|
234
497
|
polyline([[CHX, 290], [CHX, 700], [MAIN_L, 700]], { color: C.human, width: 2 })
|
|
235
|
-
plain(CHX, 336,
|
|
236
|
-
plain(CHX, 356,
|
|
237
|
-
plain(CHX, 374,
|
|
238
|
-
plain(CHX, 398,
|
|
239
|
-
plain(CHX, 470,
|
|
498
|
+
plain(CHX, 336, T.links.chLabel, { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
|
|
499
|
+
plain(CHX, 356, T.links.chCommand, { fill: C.human, anchor: 'middle', size: 11, halo: true })
|
|
500
|
+
plain(CHX, 374, T.links.chSlash, { fill: C.human, anchor: 'middle', size: 11, halo: true })
|
|
501
|
+
plain(CHX, 398, T.links.chDown, { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
|
|
502
|
+
plain(CHX, 470, T.links.reportLabel, { fill: C.human, anchor: 'middle', size: 12, weight: 700, halo: true })
|
|
240
503
|
plain(CHX, 490, 'status', { fill: C.human, anchor: 'middle', size: 11, halo: true })
|
|
241
504
|
plain(CHX, 508, 'report', { fill: C.human, anchor: 'middle', size: 11, halo: true })
|
|
242
|
-
plain(CHX, 532,
|
|
243
|
-
plain(CHX, 600,
|
|
244
|
-
plain(CHX, 616,
|
|
245
|
-
plain(CHX, 644,
|
|
246
|
-
plain(CHX, 660,
|
|
505
|
+
plain(CHX, 532, T.links.chUp, { fill: C.human, anchor: 'middle', size: 13, weight: 700, halo: true })
|
|
506
|
+
plain(CHX, 600, T.control.note1, { fill: C.mute, anchor: 'middle', size: 10, limit: 170, where: 'ch note' })
|
|
507
|
+
plain(CHX, 616, T.control.note2, { fill: C.mute, anchor: 'middle', size: 10, limit: 170, where: 'ch note' })
|
|
508
|
+
plain(CHX, 644, T.control.note3, { fill: C.mute, anchor: 'middle', size: 10, limit: 170, where: 'ch note' })
|
|
509
|
+
plain(CHX, 660, T.control.note4, { fill: C.mute, anchor: 'middle', size: 10, limit: 170, where: 'ch note' })
|
|
247
510
|
|
|
248
511
|
// ---- 连线:研究所 ↔ 框架(提示词 / 回执) ----
|
|
249
512
|
arrow(560, 542, 560, 566, { color: C.frame, marker: 'bothFrame', width: 2 })
|
|
250
513
|
arrow(1400, 542, 1400, 566, { color: C.frame, marker: 'bothFrame', width: 2 })
|
|
251
|
-
plain(980, 559,
|
|
514
|
+
plain(980, 559, T.links.promptLabel,
|
|
252
515
|
{ fill: C.frame, anchor: 'middle', size: 10.5, limit: 780, where: 'prompt label', halo: true })
|
|
253
516
|
|
|
254
517
|
// ---- 连线:所内组织(虚线 = 不由框架执行) ----
|
|
@@ -259,16 +522,16 @@ arrow(556, 430, 578, 430, { color: C.house, dashed: true, width: 1.8, marker: 'b
|
|
|
259
522
|
arrow(400, 940, 400, 964, { color: C.data, marker: 'bothData', width: 2 })
|
|
260
523
|
plain(392, 956, 'append / fold / stateOf', { fill: C.data, anchor: 'end', size: 10.5, halo: true })
|
|
261
524
|
arrow(1280, 940, 1280, 964, { color: C.data, marker: 'bothData', width: 2 })
|
|
262
|
-
plain(1288, 956,
|
|
525
|
+
plain(1288, 956, T.links.filesLabel, { fill: C.data, anchor: 'start', size: 10.5, halo: true })
|
|
263
526
|
|
|
264
527
|
// ---- 图例 ----
|
|
265
|
-
plain(52, 1186,
|
|
528
|
+
plain(52, 1186, T.legend.heading, { size: 12, fill: C.ink, weight: 700 })
|
|
266
529
|
const legend = [
|
|
267
|
-
[C.human,
|
|
268
|
-
[C.house,
|
|
269
|
-
[C.frame,
|
|
270
|
-
[C.data,
|
|
271
|
-
[C.gate,
|
|
530
|
+
[C.human, T.legend.items[0]],
|
|
531
|
+
[C.house, T.legend.items[1]],
|
|
532
|
+
[C.frame, T.legend.items[2]],
|
|
533
|
+
[C.data, T.legend.items[3]],
|
|
534
|
+
[C.gate, T.legend.items[4]],
|
|
272
535
|
]
|
|
273
536
|
legend.forEach(([col, txt], i) => {
|
|
274
537
|
const x = 108 + i * 330
|
|
@@ -282,10 +545,10 @@ push('</svg>')
|
|
|
282
545
|
mkdirSync(dirname(OUT), { recursive: true })
|
|
283
546
|
const svg = out.join('\n')
|
|
284
547
|
writeFileSync(OUT, svg, 'utf8')
|
|
285
|
-
console.log(
|
|
548
|
+
console.log(T.doc.saved + OUT + ' (' + svg.length + ' bytes)')
|
|
286
549
|
if (warnings.length) {
|
|
287
|
-
console.error(
|
|
288
|
-
for (const w of warnings) console.error(
|
|
550
|
+
console.error(T.doc.warnHeading)
|
|
551
|
+
for (const w of warnings) console.error(T.doc.warnPrefix + w)
|
|
289
552
|
process.exit(1)
|
|
290
553
|
}
|
|
291
|
-
console.log(
|
|
554
|
+
console.log(T.doc.noWarn)
|