dsh-vibe-math 2.7.0 → 2.7.2
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 +681 -685
- package/README.md +9 -13
- package/cordis.patch.yml +12 -6
- package/docs/AUDIT-CHECKLIST.md +518 -518
- package/docs/COMPAT-AUDIT-ROUND3.md +154 -154
- package/docs/final-paper.md +172 -172
- package/docs/formal-verification.md +588 -588
- package/docs/math-computation.md +187 -121
- package/docs/release-notes/README.en.md +48 -0
- package/docs/release-notes/README.md +47 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.1.en.md +86 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.1.md +86 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.2.en.md +72 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.2.md +72 -0
- package/docs/release-notes/TEMPLATE.md +120 -0
- package/docs/test-timing.md +154 -154
- package/package.json +208 -201
- package/prompt-corpus-persona/persona-corpus.json +8 -8
- package/prompt-corpus-persona/persona-corpus.md +24 -12
- package/prompt-corpus-v2/formal-verify-v2.json +176 -98
- package/prompt-corpus-v2/formal-verify-v2.md +1161 -96
- package/prompt-corpus-v3/formal-verify-v3.json +201 -63
- package/prompt-corpus-v3/formal-verify-v3.md +1401 -57
- package/prompt-corpus-v4/formal-verify-v4.json +10 -10
- package/prompt-corpus-v4/formal-verify-v4.md +40 -20
- package/prompt-corpus-v5/prompt-corpus-v5.json +81 -81
- package/prompt-corpus-v5/prompt-corpus-v5.md +199 -128
- package/tests/audit-math-computation-contract.mjs +56 -1
- package/tests/audit-math-computation-parity.mjs +30 -1
- package/tests/audit-math-computation-sensitivity.mjs +2 -2
- package/tests/audit-v5-sensitivity.mjs +3 -1
- package/tests/e2e-v5-round2.test.mjs +116 -0
- package/tests/formal-verify-v2.test.mjs +108 -2
- package/tests/formal-verify-v3.test.mjs +117 -2
- package/tests/formal-verify-v4.test.mjs +87 -0
- package/tests/helpers/math-computation-fake-seam.mjs +30 -2
- package/tests/math-computation-shared.test.mjs +430 -3
- package/vibe-math-v2/agent.cordis.yml +4 -2
- package/vibe-math-v2/math-computation.js +509 -55
- package/vibe-math-v2/math-engines.js +47 -6
- package/vibe-math-v2/vibe-math-v2.js +92 -12
- package/vibe-math-v3/agent.cordis.yml +4 -2
- package/vibe-math-v3/math-computation.js +509 -55
- package/vibe-math-v3/math-engines.js +47 -6
- package/vibe-math-v3/vibe-math-v3.js +77 -14
- package/vibe-math-v4/agent.cordis.yml +8 -4
- package/vibe-math-v4/math-computation.js +509 -55
- package/vibe-math-v4/math-engines.js +47 -6
- package/vibe-math-v4/vibe-math-v4.js +70 -15
- package/vibe-math-v5/agent.cordis.yml +8 -4
- package/vibe-math-v5/math-computation.js +509 -55
- package/vibe-math-v5/math-engines.js +47 -6
- package/vibe-math-v5/vibe-math-v5.js +152 -31
- package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +1660 -1655
- package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +693 -693
|
@@ -22,14 +22,47 @@ export const MATH_ENGINES = {
|
|
|
22
22
|
candidates: ['python3', 'python', 'py'], winPrefix: ['-3'],
|
|
23
23
|
versionArgv: ['--version'], versionRe: '(\\d+\\.\\d+\\.\\d+)',
|
|
24
24
|
scriptArgv: ['<script>'], evalArgv: ['-c', '<expr>'], stdinArgv: ['-'], ext: '.py',
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// round-7 (live): NO shell metacharacters (`|`, `%`) in any probe argv - a real host rejected
|
|
26
|
+
// them with a null spawn. One line per package: "name ok|missing".
|
|
27
|
+
probeCode: "import importlib.util,sys\nfor p in sys.argv[1:]: print(p, 'ok' if importlib.util.find_spec(p) is not None else 'missing')",
|
|
28
|
+
// round-7 (finding 6, found on the real bundled python): the probe code iterates `sys.argv[1:]`,
|
|
29
|
+
// so the names must be SEPARATE argv items - the old comma-joined `<pkgs>` made it look up one
|
|
30
|
+
// package literally named "numpy,pandas" and reported everything missing.
|
|
31
|
+
packageProbe: { argv: ['-c', '<probeCode>', '<pkgs...>'], parse: 'pairs' },
|
|
27
32
|
license: 'free', argErrorHints: ARG_ERROR_HINTS,
|
|
28
33
|
install: {
|
|
29
34
|
manager: 'pip',
|
|
30
35
|
userArgv: ['<exe>', '-m', 'pip', 'install', '--user', '<pkg>'],
|
|
31
36
|
systemArgv: ['<exe>', '-m', 'pip', 'install', '<pkg>'],
|
|
32
37
|
uninstallArgv: ['<exe>', '-m', 'pip', 'uninstall', '-y', '<pkg>'],
|
|
38
|
+
// Round-6 (B): DISPATCH by the detected interpreter environment. `pip` is the documented
|
|
39
|
+
// fallback; the module picks conda/mamba/uv only when the interpreter's OWN environment says so
|
|
40
|
+
// (detect, don't guess) and the plan states which manager it assumed.
|
|
41
|
+
managers: {
|
|
42
|
+
conda: {
|
|
43
|
+
detect: 'conda-markers',
|
|
44
|
+
userArgv: ['conda', 'install', '-y', '-c', 'conda-forge', '<pkg>'],
|
|
45
|
+
uninstallArgv: ['conda', 'remove', '-y', '<pkg>'],
|
|
46
|
+
systemUnsupportedReason: 'conda 环境是用户级的;系统级安装不在 conda 的概念里(本工具只出用户级计划)',
|
|
47
|
+
},
|
|
48
|
+
mamba: {
|
|
49
|
+
detect: 'conda-markers',
|
|
50
|
+
userArgv: ['mamba', 'install', '-y', '-c', 'conda-forge', '<pkg>'],
|
|
51
|
+
uninstallArgv: ['mamba', 'remove', '-y', '<pkg>'],
|
|
52
|
+
systemUnsupportedReason: 'mamba 环境是用户级的;系统级安装不在 mamba 的概念里(本工具只出用户级计划)',
|
|
53
|
+
},
|
|
54
|
+
uv: {
|
|
55
|
+
detect: 'uv-next-to-interpreter',
|
|
56
|
+
userArgv: ['uv', 'pip', 'install', '<pkg>'],
|
|
57
|
+
uninstallArgv: ['uv', 'pip', 'uninstall', '<pkg>'],
|
|
58
|
+
systemUnsupportedReason: 'uv 装进当前虚拟环境(用户级);系统级需要发行版包管理器,本工具只出用户级计划',
|
|
59
|
+
},
|
|
60
|
+
pip: {
|
|
61
|
+
detect: 'fallback',
|
|
62
|
+
systemArgv: ['<exe>', '-m', 'pip', 'install', '<pkg>'],
|
|
63
|
+
uninstallArgv: ['<exe>', '-m', 'pip', 'uninstall', '-y', '<pkg>'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
33
66
|
},
|
|
34
67
|
userInstall: { windows: 'winget install --id Python.Python.3.12', macos: 'brew install python', linux: 'apt install python3' },
|
|
35
68
|
},
|
|
@@ -38,13 +71,16 @@ export const MATH_ENGINES = {
|
|
|
38
71
|
candidates: ['Rscript', 'R'],
|
|
39
72
|
versionArgv: ['--version'], versionRe: '(\\d+\\.\\d+\\.\\d+)',
|
|
40
73
|
scriptArgv: ['<script>'], evalArgv: ['-e', '<expr>'], stdinArgv: ['--no-save', '--slave'], ext: '.R',
|
|
41
|
-
|
|
74
|
+
// round-7 (live): line-per-package, no `|` in the argv.
|
|
75
|
+
probeCode: "for (p in strsplit('__PKGS__',',')[[1]]) cat(p, if (requireNamespace(p, quietly=TRUE)) 'ok' else 'missing', '\\n')",
|
|
42
76
|
packageProbe: { argv: ['-e', '<probeCode>'], parse: 'pairs' },
|
|
43
77
|
license: 'free', argErrorHints: ARG_ERROR_HINTS,
|
|
44
78
|
install: {
|
|
45
79
|
manager: 'r',
|
|
46
80
|
userArgv: ['<exe>', '-e', "install.packages('__PKG__',lib=Sys.getenv('R_LIBS_USER'),repos='https://cloud.r-project.org')"],
|
|
47
81
|
uninstallArgv: ['<exe>', '-e', "remove.packages('__PKG__',lib=Sys.getenv('R_LIBS_USER'))"],
|
|
82
|
+
// Round-6 (C): user-only BY DESIGN, and the refusal now says why per engine.
|
|
83
|
+
systemUnsupportedReason: 'R 的用户库是 R_LIBS_USER;系统级安装要写发行版的包目录(需 root 或发行版包管理器),本工具只出用户级计划',
|
|
48
84
|
},
|
|
49
85
|
userInstall: { windows: 'winget install --id RProject.R', macos: 'brew install r', linux: 'apt install r-base' },
|
|
50
86
|
},
|
|
@@ -54,13 +90,15 @@ export const MATH_ENGINES = {
|
|
|
54
90
|
versionArgv: ['--version'], versionRe: '(\\d+\\.\\d+\\.\\d+)',
|
|
55
91
|
scriptArgv: ['--no-gui', '--quiet', '<script>'], evalArgv: ['--no-gui', '--quiet', '--eval', '<expr>'],
|
|
56
92
|
stdinArgv: ['--no-gui', '--quiet'], ext: '.m',
|
|
57
|
-
|
|
93
|
+
// round-7 (live): line-per-package via disp, no `|` and no `%` in the argv.
|
|
94
|
+
probeCode: "s=pkg('list'); n={'__QPKGS__'}; for i=1:numel(n); if any(cellfun(@(x) strcmp(x.name,n{i}),s)); disp([n{i} ' ok']); else; disp([n{i} ' missing']); end; end",
|
|
58
95
|
packageProbe: { argv: ['--no-gui', '--quiet', '--eval', '<probeCode>'], parse: 'pairs' },
|
|
59
96
|
license: 'free', argErrorHints: ARG_ERROR_HINTS,
|
|
60
97
|
install: {
|
|
61
98
|
manager: 'octave-pkg',
|
|
62
99
|
userArgv: ['<exe>', '--no-gui', '--quiet', '--eval', "pkg install -forge __PKG__"],
|
|
63
100
|
uninstallArgv: ['<exe>', '--no-gui', '--quiet', '--eval', 'pkg uninstall __PKG__'],
|
|
101
|
+
systemUnsupportedReason: 'Octave 的 pkg install 装进用户包目录;系统级要写 Octave 的 share/packages(需 root),本工具只出用户级计划',
|
|
64
102
|
},
|
|
65
103
|
userInstall: { windows: 'winget install --id GNU.Octave', macos: 'brew install octave', linux: 'apt install octave' },
|
|
66
104
|
},
|
|
@@ -76,6 +114,7 @@ export const MATH_ENGINES = {
|
|
|
76
114
|
manager: 'julia-pkg',
|
|
77
115
|
userArgv: ['<exe>', '-e', 'using Pkg; Pkg.add("__PKG__")'],
|
|
78
116
|
uninstallArgv: ['<exe>', '-e', 'using Pkg; Pkg.rm("__PKG__")'],
|
|
117
|
+
systemUnsupportedReason: 'Julia 的 Pkg.add 装进当前活动环境 / 用户 depot(JULIA_DEPOT_PATH);系统级不是 Julia 的概念,本工具只出用户级计划',
|
|
79
118
|
},
|
|
80
119
|
userInstall: { windows: 'winget install --id Julialang.Julia', macos: 'brew install julia', linux: 'apt install julia' },
|
|
81
120
|
},
|
|
@@ -84,7 +123,8 @@ export const MATH_ENGINES = {
|
|
|
84
123
|
candidates: ['matlab'],
|
|
85
124
|
versionArgv: ['-batch', 'disp(version)'], versionRe: '(\\d+\\.\\d+)',
|
|
86
125
|
scriptArgv: ['-batch', "run('<script>')"], evalArgv: ['-batch', '<expr>'], stdinArgv: null, ext: '.m',
|
|
87
|
-
|
|
126
|
+
// round-7 (live): no `|`/`%` in any probe argv (a real host rejected such argv with a null spawn).
|
|
127
|
+
probeCode: "t={'__QPKGS__'}; for i=1:numel(t); if license('test',t{i}); disp([t{i} ' ok']); else; disp([t{i} ' missing']); end; end",
|
|
88
128
|
packageProbe: { argv: ['-batch', '<probeCode>'], parse: 'pairs' },
|
|
89
129
|
license: 'commercial', argErrorHints: ARG_ERROR_HINTS,
|
|
90
130
|
licenseProbe: { argv: ['-batch', "disp(license('test','MATLAB'))"], okWhen: 'trim-1' },
|
|
@@ -96,7 +136,8 @@ export const MATH_ENGINES = {
|
|
|
96
136
|
candidates: ['maple'],
|
|
97
137
|
versionArgv: ['--version'], versionRe: '(\\d+\\.\\d+)',
|
|
98
138
|
scriptArgv: ['-q', '<script>'], evalArgv: ['-q', '-c', '<expr>'], stdinArgv: null, ext: '.mpl',
|
|
99
|
-
|
|
139
|
+
// round-7 (live): `print` instead of printf so the argv carries no `|`/`%`.
|
|
140
|
+
probeCode: 'for p in ["__PKGS__"] do try with(p) catch: print(p, " missing"); next end try; print(p, " ok") end do',
|
|
100
141
|
packageProbe: { argv: ['-q', '-c', '<probeCode>'], parse: 'pairs' },
|
|
101
142
|
license: 'commercial', argErrorHints: ARG_ERROR_HINTS,
|
|
102
143
|
licenseProbe: { argv: ['-q', '-c', 'printf("1")'], okWhen: 'exit-0' },
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
MATH_TOOL_DESCRIPTION,
|
|
50
50
|
MATH_TOOL_SCHEMA,
|
|
51
51
|
MATH_ARCHIVE_WORKFLOW_LINE,
|
|
52
|
+
MATH_SUBSTITUTION_RULE_LINE,
|
|
52
53
|
MATH_PERSONA_TOOL_LINE,
|
|
53
54
|
MATH_RULE_LINES,
|
|
54
55
|
normalizeMathParams,
|
|
@@ -524,6 +525,11 @@ export function apply(ctx) {
|
|
|
524
525
|
// **可选能力,按宿主实际情况声明**:宿主 fs 没有 listDir 就不给这个回调(模块据此跳过
|
|
525
526
|
// "每项目 run 目录数 >200" 的保留检查,而不是每次运行都白问一次)。
|
|
526
527
|
listDir: (typeof fs.listDir === 'function') ? mathListDir : undefined,
|
|
528
|
+
// round-7 (fix 2): DSH's own bundled runtimes (`<home>/.dsh/dsh-runtimes/*/dependencies/<engine>/`)
|
|
529
|
+
// as a LAST resort after PATH. Only generic roots are supplied here; the module globs the tree
|
|
530
|
+
// name (never `dsh-primary-runtime`) and only accepts the descriptor's own candidate names.
|
|
531
|
+
runtimeRoots: () => { const env = process.env.DSH_HOME; if (env) return [String(env)]; const home = String(process.env.HOME || process.env.USERPROFILE || ''); return home ? [home.replace(/[\\/]+$/, '') + '/.dsh'] : [] },
|
|
532
|
+
listDirAbs: async function (abs) { try { const t = await fsTarget(abs); const st = await fs.stat(t); if (!st) return []; return (await fs.listDir(t)) || [] } catch (e) { return [] } },
|
|
527
533
|
writeText: async function (rel, text) { return await writeText(rel, text) },
|
|
528
534
|
readText: async function (rel) { return await readText(rel) },
|
|
529
535
|
exists: mathExists,
|
|
@@ -544,7 +550,15 @@ export function apply(ctx) {
|
|
|
544
550
|
async function refreshMathProbe(force) {
|
|
545
551
|
if (params.mathComputation === 'off') { mathProbe = null; mathProbeAt = 0; return null }
|
|
546
552
|
if (!mathTool) return null
|
|
547
|
-
try {
|
|
553
|
+
try {
|
|
554
|
+
mathProbe = await mathTool.probe(force ? { refresh: true } : undefined)
|
|
555
|
+
mathProbeAt = now()
|
|
556
|
+
// 规则漂移自检(A 项):可用性行必须带"替代必须声明(诚实性)"。引用**常量**而非比对文本:
|
|
557
|
+
// 模块改措辞不会误报,只有整条规则消失才会响。
|
|
558
|
+
if (mathProbe && mathAvailabilityLine(mathProbe, 'zh', params.mathMode).indexOf(MATH_SUBSTITUTION_RULE_LINE) === -1) {
|
|
559
|
+
console.error('vibe-math-v3: 可用性行缺少替代声明规则(MATH_SUBSTITUTION_RULE_LINE)')
|
|
560
|
+
}
|
|
561
|
+
} catch (e) { mathProbe = null }
|
|
548
562
|
return mathProbe
|
|
549
563
|
}
|
|
550
564
|
/** 心跳里的 TTL 刷新(每拍一次布尔判断)。 */
|
|
@@ -904,6 +918,7 @@ export function apply(ctx) {
|
|
|
904
918
|
// 引理只在摘要给"索引"(id+标题),完整叙述在各方向文件 Progress/<qid>/<dirId>.md(由代理或调度器写入)
|
|
905
919
|
if (d.lemmas && d.lemmas.length) { lines.push('**引理索引**:' + d.lemmas.map(function (l) { return '「' + l.title + '」(' + l.id + ')' }).join(';')) }
|
|
906
920
|
lines.push('**完整叙述**:见 `Progress/' + qid + '/' + d.id + '.md`')
|
|
921
|
+
lines.push(PAPER_PATH_NOTE)
|
|
907
922
|
for (const j of (d.journal || [])) {
|
|
908
923
|
lines.push('')
|
|
909
924
|
lines.push('### 第 ' + j.round + ' 轮|' + (j.agent || '') + '|' + (j.at || ''))
|
|
@@ -1295,7 +1310,22 @@ export function apply(ctx) {
|
|
|
1295
1310
|
// 并发计数由 agentRegistry 推导,无需手工 +1。
|
|
1296
1311
|
await saveAll()
|
|
1297
1312
|
}
|
|
1298
|
-
|
|
1313
|
+
/**
|
|
1314
|
+
* 中断一个**在册**子代理。审计 D1:旧实现 `try{…}catch(e){}` 把"没有这个 child / 宿主拒绝中断"
|
|
1315
|
+
* 都吞成静默成功(调用方拿到 undefined,还以为已经中断)。现在:空/未知 id ⇒ `code` +
|
|
1316
|
+
* `next{tool,hint}`,宿主抛错 ⇒ 保留错误文本并给替代出口;只有真的发出中断才 `ok:true`。
|
|
1317
|
+
*/
|
|
1318
|
+
async function interruptChild(childId) {
|
|
1319
|
+
const id = String(childId == null ? '' : childId).trim()
|
|
1320
|
+
if (!id) return { ok: false, code: 'VIBE_MATH_INVALID_ARGUMENT', message: 'interruptChild 失败:childId 为空——无法确定要中断哪个子代理。', next: { kind: 'reason', tool: 'vibe_math_list_agents', hint: '用 vibe_math_list_agents 列出本会话在册子代理的 id,再带 childId 调用。' } }
|
|
1321
|
+
if (!Object.prototype.hasOwnProperty.call(agentRegistry, id)) return { ok: false, code: 'VIBE_MATH_CHILD_NOT_FOUND', message: 'interruptChild 失败:' + id + ' 不在本会话的在册子代理里(在册 ' + Object.keys(agentRegistry).length + ' 个)——可能已经结束,或不是本会话的子代理。', next: { kind: 'reason', tool: 'vibe_math_list_agents', hint: '用 vibe_math_list_agents 取当前在册 id;要停整个会话用 vibe_math_abort。' } }
|
|
1322
|
+
try {
|
|
1323
|
+
await subagents.interrupt(id, { kind: 'ancestor', agent: rootAgent })
|
|
1324
|
+
return { ok: true, childId: id }
|
|
1325
|
+
} catch (e) {
|
|
1326
|
+
return { ok: false, code: 'VIBE_MATH_INTERRUPT_FAILED', message: 'interruptChild 失败:宿主拒绝中断 ' + id + '(' + String((e && e.message) || e) + ')。', next: { kind: 'reason', tool: 'vibe_math_abort', hint: '宿主不接受单点中断时,用 vibe_math_abort 终止整个会话的子代理。' } }
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1299
1329
|
|
|
1300
1330
|
// ================= prompts =================
|
|
1301
1331
|
function personaText(key) { return params[key] ? (String(params[key]) + '\n\n') : '' }
|
|
@@ -1611,7 +1641,10 @@ export function apply(ctx) {
|
|
|
1611
1641
|
}
|
|
1612
1642
|
return {}
|
|
1613
1643
|
}
|
|
1614
|
-
async function resolveDecision(id, resolution) {
|
|
1644
|
+
async function resolveDecision(id, resolution) {
|
|
1645
|
+
const d = decisionQueue.find(function (x) { return x.id === id })
|
|
1646
|
+
if (!d) return { ok: false, code: 'VIBE_MATH_DECISION_NOT_FOUND', message: 'decision not found:' + String(id == null ? '' : id) + ' 不在人工决策队列里(队列现有 ' + decisionQueue.length + ' 个' + (decisionQueue.length ? (':' + decisionQueue.map(function (x) { return x.id }).join(', ')) : '') + ')——可能已被 vibe_math_decide 结清,或被 start/resume/abort/换项目作废(作废是终态,不会回到 pending)。', next: { kind: 'reason', tool: 'vibe_math_list_decisions', hint: '先用 vibe_math_list_decisions 取当前 pending 的 id;若期望它还在,检查是否发生过 start/resume/abort/换项目。' } }
|
|
1647
|
+
if (d.status !== 'pending') return { ok: false, code: 'VIBE_MATH_DECISION_ALREADY_RESOLVED', message: 'decision already resolved:' + String(id) + ' 当前状态是 ' + String(d.status) + '——每次决策只生效一次。', next: { kind: 'reason', tool: 'vibe_math_list_decisions', hint: '用 vibe_math_list_decisions 看还有哪些 pending;要重新决策请等框架发起新的决策项。' } }; d.status = 'resolved'; d.resolution = resolution; if (scheduler.gate && scheduler.gate.decisionId === id) scheduler.gate = null; logActivity('decide', id + ' resolved: ' + resolution.action + (resolution.verdict !== undefined ? ' ' + resolution.verdict : '')); await saveAll(); scheduleTick(); return { ok: true, message: 'decision resolved' } }
|
|
1615
1648
|
|
|
1616
1649
|
// ================= scheduler core =================
|
|
1617
1650
|
function scheduleTick() { tick().catch(function (e) { console.error('vibe-math-v3 tick error: ' + String((e && e.stack) || e)) }) }
|
|
@@ -1863,7 +1896,7 @@ export function apply(ctx) {
|
|
|
1863
1896
|
return true
|
|
1864
1897
|
}
|
|
1865
1898
|
async function backfillVerifiers(t) {
|
|
1866
|
-
while (t.children.length < t.
|
|
1899
|
+
while (t.children.length < voteCount(t).expected) {
|
|
1867
1900
|
if (activeCount() >= params.maxParallelThreshold) break
|
|
1868
1901
|
const index = t.children.length
|
|
1869
1902
|
const childId = await spawnChild('verifier:' + t.rId + ':' + index, verifierReviewPrompt(t.r), { role: 'verifier', rId: t.rId, round: 1, index: index })
|
|
@@ -1877,7 +1910,7 @@ export function apply(ctx) {
|
|
|
1877
1910
|
const t = tasks[ids[i]]
|
|
1878
1911
|
if (t.type !== 'verify') continue
|
|
1879
1912
|
if (t.status === 'paused') {
|
|
1880
|
-
const allReported =
|
|
1913
|
+
const allReported = voteCount(t, t.round).quorum
|
|
1881
1914
|
if (allReported) { t.status = 'debating'; await advanceVerification(t, t.round); continue }
|
|
1882
1915
|
}
|
|
1883
1916
|
if (t.status !== 'spawning') continue
|
|
@@ -3742,15 +3775,35 @@ export function apply(ctx) {
|
|
|
3742
3775
|
* 下限取 max(2, expectedCount):verifierCount=1 不是"允许单票",只是"至少 2 个独立验证器"
|
|
3743
3776
|
* (与 createVerifyTask 的 `Math.max(2, ...)` 一致)。
|
|
3744
3777
|
*/
|
|
3745
|
-
|
|
3746
|
-
|
|
3778
|
+
/**
|
|
3779
|
+
* 票数/参与集的**单一来源**(审计 D4)。一个 verify 任务的参与集与"需要几票"只由**创建时的快照**
|
|
3780
|
+
* `t.expectedCount`(createVerifyTask 写入)与当前 round 决定——**绝不**读实时 `params.verifierCount`
|
|
3781
|
+
* 或实时名册:否则同一个计数会在 quorum 判据 / 裁决聚合 / 任务簿记之间给出不同答案,中途调参会让
|
|
3782
|
+
* 已经在进行中的表决永远凑不够票数而卡死(守卫见 formal-verify-v3 §5c)。
|
|
3783
|
+
*/
|
|
3784
|
+
function voteCount(t, round) {
|
|
3785
|
+
const tt = t || {}
|
|
3786
|
+
const expected = Math.max(2, Number(tt.expectedCount) || 0)
|
|
3787
|
+
const r = Number((round === undefined || round === null) ? (tt.round || 1) : round) || 1
|
|
3788
|
+
const participants = Array.isArray(tt.children) ? tt.children.slice() : []
|
|
3789
|
+
const results = tt.childResults || {}
|
|
3790
|
+
const reportedIds = Object.keys(results)
|
|
3791
|
+
const thisRoundIds = participants.filter(function (cid) { const x = results[cid]; return x && Number(x.round) === r })
|
|
3792
|
+
return {
|
|
3793
|
+
round: r, expected: expected, participants: participants,
|
|
3794
|
+
dispatched: participants.length, reported: reportedIds.length, reportedIds: reportedIds,
|
|
3795
|
+
reportedThisRound: thisRoundIds.length,
|
|
3796
|
+
quorum: participants.length > 0 && participants.length >= expected && thisRoundIds.length === participants.length,
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
function minVotes(t) { return voteCount(t).expected } // 快照值;**故意**不读实时 params.verifierCount
|
|
3800
|
+
function haveEnoughVotes(t) { const v = voteCount(t); return v.reported >= minVotes(t) } // 单一来源:minVotes 也只读快照
|
|
3747
3801
|
/** 所有已派出的验证器都报了本轮的票,**且**票数达到下限——否则不得推进/裁决。 */
|
|
3748
3802
|
function allReportedWithQuorum(t, round) {
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
return t.children.every(function (cid) { const r = t.childResults[cid]; return r && r.round === round })
|
|
3803
|
+
const v = voteCount(t, round)
|
|
3804
|
+
return v.dispatched > 0 && v.dispatched >= v.expected && v.quorum
|
|
3752
3805
|
}
|
|
3753
|
-
function consensus(t) { const vs =
|
|
3806
|
+
function consensus(t) { const vs = voteCount(t).reportedIds.map(function (cid) { return t.childResults[cid].Result }); if (vs.length === 0) return false; return vs.every(function (v) { return v === 1 }) || vs.every(function (v) { return v === 0 }) }
|
|
3754
3807
|
function buildTranscript(t) { const parts = []; const cids = Object.keys(t.childResults); for (let i = 0; i < cids.length; i++) { const r = t.childResults[cids[i]]; parts.push('Reviewer ' + i + ': Result=' + r.Result + ' Reason=' + r.Reason) } return parts.join('\n') }
|
|
3755
3808
|
async function handleVerifier(childId, meta, output, stopReason) {
|
|
3756
3809
|
const rId = meta.rId
|
|
@@ -3824,7 +3877,7 @@ export function apply(ctx) {
|
|
|
3824
3877
|
}
|
|
3825
3878
|
// 近共识 + forced/flat 裁决(修复 v2 flat 高置信分歧误判:全部同侧且均值≥0.85/≤0.15 取均值)
|
|
3826
3879
|
function finalVerdict(t) {
|
|
3827
|
-
const rs =
|
|
3880
|
+
const rs = voteCount(t).reportedIds.map(function (cid) { return t.childResults[cid] })
|
|
3828
3881
|
if (rs.length === 0) return 0.5
|
|
3829
3882
|
if (rs.every(function (r) { return r.Result === 1 })) return 1
|
|
3830
3883
|
if (rs.every(function (r) { return r.Result === 0 })) return 0
|
|
@@ -4039,6 +4092,12 @@ export function apply(ctx) {
|
|
|
4039
4092
|
/** 证据索引(spec §3.9):只列**确实存在**的文件;排除 Paper/ 自己(避免自我污染)。
|
|
4040
4093
|
* formalVerify=off 必须真的是无操作(formal-verify-v3 的语料守卫断言"off 的提示词里零 Lean 文本"),
|
|
4041
4094
|
* 所以 off 档不把形式化路径写进材料。 */
|
|
4095
|
+
/**
|
|
4096
|
+
* 成员可见路径说明(跨预设审计 P0)。成员/子代理的**文件工具按会话 cwd 解析**相对路径,而下面列出的
|
|
4097
|
+
* 路径都是**项目根相对**的 ⇒ 必须显式说明"先拼绝对前缀";计算产物用回执里的绝对字段。一处常量、
|
|
4098
|
+
* 五处材料文本各自 append(绝不替换既有行)。
|
|
4099
|
+
*/
|
|
4100
|
+
const PAPER_PATH_NOTE = '(路径说明:成员/子代理的文件工具按**会话 cwd** 解析相对路径,因此上面列出的相对路径都必须先拼上**项目根的绝对前缀**再使用;计算产物请用回执里的绝对字段 `receipt.scriptAbs`,或把 `receipt.cwd` 与 `receipt.scriptPath` 拼起来。)'
|
|
4042
4101
|
async function paperEvidenceIndex() {
|
|
4043
4102
|
const formalNow = formalOn()
|
|
4044
4103
|
const out = []
|
|
@@ -4074,6 +4133,7 @@ export function apply(ctx) {
|
|
|
4074
4133
|
const ps = allPropos()
|
|
4075
4134
|
L.push('')
|
|
4076
4135
|
L.push('[PROPOSITIONS] (Propos/<分类>/<id>.md)')
|
|
4136
|
+
L.push(PAPER_PATH_NOTE)
|
|
4077
4137
|
if (ps.length === 0) L.push('- (none)')
|
|
4078
4138
|
for (let i = 0; i < ps.length; i++) {
|
|
4079
4139
|
const p = ps[i]
|
|
@@ -4084,6 +4144,7 @@ export function apply(ctx) {
|
|
|
4084
4144
|
}
|
|
4085
4145
|
L.push('')
|
|
4086
4146
|
L.push('[METHODS / ARTEFACTS] (Methods/ + 全局 VibeMath/Methods/)')
|
|
4147
|
+
L.push(PAPER_PATH_NOTE)
|
|
4087
4148
|
const ms = Array.from(methods.values()).concat(Array.from(globalMethods.values()))
|
|
4088
4149
|
if (ms.length === 0) L.push('- (none)')
|
|
4089
4150
|
for (let i = 0; i < ms.length; i++) {
|
|
@@ -4682,10 +4743,10 @@ export function apply(ctx) {
|
|
|
4682
4743
|
registerTool('vibe_math_set_project', TOOL_DESC.vibe_math_set_project, objParams({ name: { type: 'string' } }, ['name']), async function (args) { const slug = slugify(args.name); return await setProject(slug, false) })
|
|
4683
4744
|
registerTool('vibe_math_list_projects', TOOL_DESC.vibe_math_list_projects, objParams({}), async function () { return { ok: true, current: currentProject, projects: await listDirsAt(vibeRoot(), 'Projects') } })
|
|
4684
4745
|
registerTool('vibe_math_list_decisions', TOOL_DESC.vibe_math_list_decisions, objParams({}), async function () { return { ok: true, decisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).map(function (d) { return { id: d.id, node: d.node, context: d.context } }) } })
|
|
4685
|
-
registerTool('vibe_math_decide', TOOL_DESC.vibe_math_decide, objParams({ id: { type: 'string' }, action: { type: 'string', enum: ['approve', 'reject', 'override'] }, verdict: { type: 'number' } }, ['id', 'action']), async function (args) { const d = decisionQueue.find(function (x) { return x.id === args.id });
|
|
4746
|
+
registerTool('vibe_math_decide', TOOL_DESC.vibe_math_decide, objParams({ id: { type: 'string' }, action: { type: 'string', enum: ['approve', 'reject', 'override'] }, verdict: { type: 'number' } }, ['id', 'action']), async function (args) { const d = decisionQueue.find(function (x) { return x.id === args.id }); const resolution = { action: args.action, verdict: args.verdict }; if (!d || d.status !== 'pending') return await resolveDecision(args.id, resolution); const applied = await applyDecision(d.node, d.data, resolution); const r = await resolveDecision(args.id, resolution); return Object.assign({ ok: true, applied: applied }, r) })
|
|
4686
4747
|
registerTool('vibe_math_list_agents', TOOL_DESC.vibe_math_list_agents, objParams({}), async function () { const out = []; const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) { const m = agentRegistry[ids[i]]; out.push({ childId: ids[i], role: m.role, qid: m.qid, direction: m.direction, round: m.round, rId: m.rId }) } return { ok: true, agents: out, count: out.length } })
|
|
4687
4748
|
registerTool('vibe_math_message_agent', TOOL_DESC.vibe_math_message_agent, objParams({ childId: { type: 'string' }, message: { type: 'string' } }, ['childId', 'message']), async function (args) { if (!agentRegistry[args.childId]) return { ok: false, message: 'unknown childId' }; await followupChild(args.childId, args.message); return { ok: true, message: 'message delivered' } })
|
|
4688
|
-
registerTool('vibe_math_interrupt_agent', TOOL_DESC.vibe_math_interrupt_agent, objParams({ childId: { type: 'string' } }, ['childId']), async function (args) { await interruptChild(args.childId); return { ok:
|
|
4749
|
+
registerTool('vibe_math_interrupt_agent', TOOL_DESC.vibe_math_interrupt_agent, objParams({ childId: { type: 'string' } }, ['childId']), async function (args) { const r = await interruptChild(args.childId); return Object.assign({}, r, { ok: !!(r && r.ok) }) })
|
|
4689
4750
|
registerTool('vibe_math_plan', TOOL_DESC.vibe_math_plan, objParams({ force: { type: 'boolean' } }), async function (args) { if (args && args.force && scheduler.running && !scheduler.gate) { await callPlanner(); return { ok: true, message: 'planning triggered', queued: planQueue.length } } return { ok: true, queued: planQueue, lastPlan: lastPlanSummary } })
|
|
4690
4751
|
registerTool('vibe_math_index', TOOL_DESC.vibe_math_index, objParams({}), async function () { await loadKnowledgeBase(); const r = await rebuildIndex(); return { ok: true, index: r, project: currentProject } })
|
|
4691
4752
|
registerTool('vibe_math_method_add', TOOL_DESC.vibe_math_method_add, objParams({ id: { type: 'string' }, 标题: { type: 'string' }, 类型: { type: 'string' }, 核心内容: { type: 'string' }, 适用场景: { type: 'string' } }, ['id', '标题']), async function (args) { const id = idSafe(args.id); if (methods.has(id)) return { ok: false, message: 'method id already exists' }; const m = { id: id, 标题: args.标题, 类型: args.类型 || '方法', 状态: '经验', 可信断言: [], 上级体系: [], 子方法: [], 相关: [], 适用场景: args.适用场景 || '', 核心内容: args.核心内容 || '', 定义与记号: '', applications: [], improvements: [], 来源: 'user' }; methods.set(m.id, m); await saveMethod(m, false); await rebuildIndex(); return { ok: true, method: m, file: methodRel(m, false) } })
|
|
@@ -4967,6 +5028,8 @@ export function apply(ctx) {
|
|
|
4967
5028
|
mathProbe: function () { return mathProbe },
|
|
4968
5029
|
// P2a:persona 两个文本块必须含的「归档→编辑→重跑」规则(文本取自共享模块常量,persona 不手抄)。
|
|
4969
5030
|
mathArchiveWorkflowLine: MATH_ARCHIVE_WORKFLOW_LINE,
|
|
5031
|
+
// A 项:替代声明规则(文本由共享模块给出;persona 两块与每轮可用性行都必须带它)。
|
|
5032
|
+
mathSubstitutionRuleLine: MATH_SUBSTITUTION_RULE_LINE,
|
|
4970
5033
|
// childOwner 裁剪用(审计 L1):这个会话当前仍"可能再发 subagent/end"的 child
|
|
4971
5034
|
// = 在册子代理 + 任何任务正在等的那几个。
|
|
4972
5035
|
referencedChildIds: function () {
|
|
@@ -73,8 +73,10 @@
|
|
|
73
73
|
too; they work in every mode.
|
|
74
74
|
- vibe_v4_formal_report — human-readable Lean formal-verification mirror (mode, Lean-passed
|
|
75
75
|
- math_computation {op: probe|run|receipt|install, engine, mode: code|file|expr, …} — 先 probe 预检引擎/包/许可,再调引擎计算并把脚本与输出归档成可复核回执;缺引擎/缺包只报告与给安装指引/计划;shell 兜底不算归档。
|
|
76
|
-
- 归档→编辑→重跑:mode:'code' 的脚本原件在回执的 scriptPath(Computation/<id>/script.<ext
|
|
77
|
-
- Archive -> edit -> re-run: for mode:'code' the script original is at the receipt's scriptPath (Computation/<id>/script.<ext
|
|
76
|
+
- 归档→编辑→重跑:mode:'code' 的脚本原件在回执的 scriptPath(Computation/<id>/script.<ext>,**相对项目根**);**成员的文件工具是按会话 cwd 解析的**,所以打开它要用**绝对路径** `receipt.scriptAbs`,或把 `receipt.cwd` 与 `receipt.scriptPath` 拼起来(回执两个字段都有);编辑后用 mode:'file' 指向它重跑,会写出一份**新回执/新 attempt**(含新的 scriptHash)。**旧回执对修改后的代码无效**——报告里必须引用与当前代码哈希一致的那份回执;工具会在 scriptChanged / scriptChangedDuringRun 为 true 时显式告警。
|
|
77
|
+
- Archive -> edit -> re-run: for mode:'code' the script original is at the receipt's scriptPath (Computation/<id>/script.<ext>, **relative to the project root**); member file tools resolve paths against the SESSION CWD, so open it via the ABSOLUTE `receipt.scriptAbs`, or join `receipt.cwd` with `receipt.scriptPath` (both are in the receipt); after editing, re-run it with mode:'file' to write a NEW receipt/attempt with a NEW scriptHash. **An old receipt is NOT evidence for edited code** - cite the receipt whose scriptHash matches the current code; the tool warns explicitly via scriptChanged / scriptChangedDuringRun.
|
|
78
|
+
- 替代必须声明(诚实性):当替代方案改变了**精确性或结论强度**时(精确符号解 → 数值近似、闭式解 → 采样/求积、改了精度/容差/假设、换了算法类),结论里**必须写明**,不得读起来像得到了原本(精确/所要求的)结果;拿不到精确结果就直说。
|
|
79
|
+
- Declare substitutions (honesty): when an alternative changes EXACTNESS or conclusion strength (exact symbolic solution -> numerical approximation, closed form -> sampling/quadrature, changed precision/tolerances/assumptions, a different algorithm class), the conclusion MUST say so explicitly and must not read as if the original (exact/requested) result had been obtained; if the exact result is unavailable, say so plainly.
|
|
78
80
|
objects, recorded blockers, formalization TODO, library paths).
|
|
79
81
|
- vibe_v4_prompts {which: brainstorm|normal|heartbeat|meeting|verify|coreRules, member?, target?, stage?} — read the exact prompt text a resident would receive (prompt auditing; prompt text is the product).
|
|
80
82
|
A /v4 slash command mirrors the main controls (configure|start [problem]|resume|pause|abort|status|report|message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|set <k=v>...).
|
|
@@ -183,8 +185,10 @@
|
|
|
183
185
|
too; they work in every mode.
|
|
184
186
|
- vibe_v4_formal_report — human-readable Lean formal-verification mirror (mode, Lean-passed
|
|
185
187
|
- math_computation {op: probe|run|receipt|install, engine, mode: code|file|expr, …} — 先 probe 预检引擎/包/许可,再调引擎计算并把脚本与输出归档成可复核回执;缺引擎/缺包只报告与给安装指引/计划;shell 兜底不算归档。
|
|
186
|
-
- 归档→编辑→重跑:mode:'code' 的脚本原件在回执的 scriptPath(Computation/<id>/script.<ext
|
|
187
|
-
- Archive -> edit -> re-run: for mode:'code' the script original is at the receipt's scriptPath (Computation/<id>/script.<ext
|
|
188
|
+
- 归档→编辑→重跑:mode:'code' 的脚本原件在回执的 scriptPath(Computation/<id>/script.<ext>,**相对项目根**);**成员的文件工具是按会话 cwd 解析的**,所以打开它要用**绝对路径** `receipt.scriptAbs`,或把 `receipt.cwd` 与 `receipt.scriptPath` 拼起来(回执两个字段都有);编辑后用 mode:'file' 指向它重跑,会写出一份**新回执/新 attempt**(含新的 scriptHash)。**旧回执对修改后的代码无效**——报告里必须引用与当前代码哈希一致的那份回执;工具会在 scriptChanged / scriptChangedDuringRun 为 true 时显式告警。
|
|
189
|
+
- Archive -> edit -> re-run: for mode:'code' the script original is at the receipt's scriptPath (Computation/<id>/script.<ext>, **relative to the project root**); member file tools resolve paths against the SESSION CWD, so open it via the ABSOLUTE `receipt.scriptAbs`, or join `receipt.cwd` with `receipt.scriptPath` (both are in the receipt); after editing, re-run it with mode:'file' to write a NEW receipt/attempt with a NEW scriptHash. **An old receipt is NOT evidence for edited code** - cite the receipt whose scriptHash matches the current code; the tool warns explicitly via scriptChanged / scriptChangedDuringRun.
|
|
190
|
+
- 替代必须声明(诚实性):当替代方案改变了**精确性或结论强度**时(精确符号解 → 数值近似、闭式解 → 采样/求积、改了精度/容差/假设、换了算法类),结论里**必须写明**,不得读起来像得到了原本(精确/所要求的)结果;拿不到精确结果就直说。
|
|
191
|
+
- Declare substitutions (honesty): when an alternative changes EXACTNESS or conclusion strength (exact symbolic solution -> numerical approximation, closed form -> sampling/quadrature, changed precision/tolerances/assumptions, a different algorithm class), the conclusion MUST say so explicitly and must not read as if the original (exact/requested) result had been obtained; if the exact result is unavailable, say so plainly.
|
|
188
192
|
objects, recorded blockers, formalization TODO, library paths).
|
|
189
193
|
- vibe_v4_prompts {which: brainstorm|normal|heartbeat|meeting|verify|coreRules, member?, target?, stage?} — read the exact prompt text a resident would receive (prompt auditing; prompt text is the product).
|
|
190
194
|
A /v4 slash command mirrors the main controls (configure|start [problem]|resume|pause|abort|status|report|message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|set <k=v>...).
|