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' },
|
|
@@ -67,6 +67,8 @@ import {
|
|
|
67
67
|
MATH_RULE_LINES_EN,
|
|
68
68
|
MATH_ARCHIVE_WORKFLOW_LINE,
|
|
69
69
|
MATH_ARCHIVE_WORKFLOW_LINE_EN,
|
|
70
|
+
MATH_SUBSTITUTION_RULE_LINE,
|
|
71
|
+
MATH_SUBSTITUTION_RULE_LINE_EN,
|
|
70
72
|
normalizeMathParams,
|
|
71
73
|
probeMathEngines,
|
|
72
74
|
registerMathComputation,
|
|
@@ -767,6 +769,14 @@ export function apply(ctx) {
|
|
|
767
769
|
const vibeRoot = () => (workspaceRoot() + '/VibeMath').replace(/\\/g, '/')
|
|
768
770
|
const projectRoot = () => vibeRoot() + '/Projects/' + project
|
|
769
771
|
const instRoot = () => projectRoot() + '/Institutes/' + instituteName
|
|
772
|
+
// DEFECT A (3rd self-test): member-facing text used INSTITUTE-relative paths
|
|
773
|
+
// (`Members/<id>/…`), but a member's OWN file tools resolve a relative path against the
|
|
774
|
+
// SESSION CWD — so a member following that text verbatim created `<cwd>/Members/…`, a stray
|
|
775
|
+
// tree outside `VibeMath/Projects/…` (the self-test's `Members/acad/Propos/p-*.md`). Every
|
|
776
|
+
// member-facing path now uses the CWD-relative form (their real baseline); the framework's own
|
|
777
|
+
// writes keep going through `instRoot()`. `instRel()` is the one place that composes it.
|
|
778
|
+
const instRootRel = () => 'VibeMath/Projects/' + project + '/Institutes/' + instituteName
|
|
779
|
+
const instRel = (rel) => instRootRel() + '/' + String(rel == null ? '' : rel).replace(/^\/+/, '')
|
|
770
780
|
|
|
771
781
|
function getPolicy() {
|
|
772
782
|
const sp = sandboxPolicyOf()
|
|
@@ -922,9 +932,95 @@ export function apply(ctx) {
|
|
|
922
932
|
const memberById = (id) => inst().members.find((m) => m.id === id)
|
|
923
933
|
const voters = () => activeMembers().filter((m) => m.kind === 'academician' || m.kind === 'researcher')
|
|
924
934
|
const voterCount = () => voters().length
|
|
925
|
-
function
|
|
935
|
+
function quorumMFrom(vc) {
|
|
926
936
|
const cap = Math.max(1, Math.floor(Number(params.quorumCap) || 3))
|
|
927
|
-
return Math.max(1, Math.min(cap,
|
|
937
|
+
return Math.max(1, Math.min(cap, vc))
|
|
938
|
+
}
|
|
939
|
+
function quorumM() { return quorumMFrom(voterCount()) }
|
|
940
|
+
// DEFECT B (3rd self-test): the office's digest showed `m=2` while `status`/`acad` showed
|
|
941
|
+
// `m=3` in the SAME window — two views had computed the voter set at different moments
|
|
942
|
+
// (roster-snapshot timing). Every quorum-bearing view now reads ONE live snapshot and carries
|
|
943
|
+
// its `rosterVersion`, so all views at one point in time are provably identical and a stale
|
|
944
|
+
// reader can be DETECTED instead of silently disagreeing.
|
|
945
|
+
function rosterVersion() {
|
|
946
|
+
const ids = voters().map((m) => String(m.id) + ':' + String(m.phase)).sort()
|
|
947
|
+
return 'r' + String((inst().members || []).length) + ':' + ids.join(',')
|
|
948
|
+
}
|
|
949
|
+
function rosterSnapshot() {
|
|
950
|
+
const vs = voters()
|
|
951
|
+
const vc = vs.length
|
|
952
|
+
const started = vc > 0
|
|
953
|
+
return {
|
|
954
|
+
version: rosterVersion(),
|
|
955
|
+
m: started ? quorumMFrom(vc) : 0,
|
|
956
|
+
voters: vs.map((m) => m.id),
|
|
957
|
+
voterCount: vc,
|
|
958
|
+
started: started,
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// DEFECT 2 (v5 self-test §18): before `start` there are no voters, yet the reported quorum
|
|
962
|
+
// was `m=1` with `voters=0` — it read like "a quorum of 1 is already there". The REPORTED
|
|
963
|
+
// quorum is therefore coherent and explicit: `started:false`, `m:0`, `voterCount:0` and the
|
|
964
|
+
// next step. `quorumM()` itself stays >=1 so the consensus arithmetic is unchanged, and
|
|
965
|
+
// `judgeVerdict` refuses to conclude at all when there are no voters.
|
|
966
|
+
function quorumView() {
|
|
967
|
+
const snap = rosterSnapshot()
|
|
968
|
+
const started = snap.started
|
|
969
|
+
const view = {
|
|
970
|
+
m: snap.m,
|
|
971
|
+
mode: params.quorumMode,
|
|
972
|
+
voters: snap.voters,
|
|
973
|
+
voterCount: snap.voterCount,
|
|
974
|
+
started: started,
|
|
975
|
+
phase: started ? 'voting' : 'not-started',
|
|
976
|
+
rosterVersion: snap.version,
|
|
977
|
+
}
|
|
978
|
+
if (!started) {
|
|
979
|
+
view.next = {
|
|
980
|
+
kind: 'start', tool: 'vibe_v5_start',
|
|
981
|
+
hint: '研究所尚未启动:有表决权者 0 人,因此 m 记为 0(不是"已满足 1 票门槛")。先 vibe_v5_start(或 /v5 start)建立名册,m 才有意义。',
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return view
|
|
985
|
+
}
|
|
986
|
+
// DEFECT 1 (v5 self-test §18): a member-scoped tool called before the institute exists — or
|
|
987
|
+
// by nobody identifiable — answered a BARE `V5_MEMBER_NOT_FOUND`, so an operator (or the
|
|
988
|
+
// model) could not tell WHY it failed or WHAT TO DO FIRST. The error CODE is unchanged (all
|
|
989
|
+
// existing gates keep matching on it); the answer now names the state and the next step, in
|
|
990
|
+
// the same `next`-hint shape the `math_computation` tool uses.
|
|
991
|
+
function memberDiagnosis(what, memberId) {
|
|
992
|
+
const total = (inst().members || []).length
|
|
993
|
+
const active = activeMembers().length
|
|
994
|
+
const who = String(memberId || '')
|
|
995
|
+
if (active === 0 && !running) {
|
|
996
|
+
return {
|
|
997
|
+
ok: false, code: 'V5_MEMBER_NOT_FOUND',
|
|
998
|
+
message: what + '失败:研究所尚未启动(在册 ' + total + ' 名、有表决权者 0 人),没有可归属的成员身份。',
|
|
999
|
+
next: {
|
|
1000
|
+
kind: 'start', tool: 'vibe_v5_start',
|
|
1001
|
+
hint: '先启动研究所(vibe_v5_start 或 /v5 start)让框架派出常驻成员,再由**成员子代理**调用该工具;所办(会话根)本身不是成员。',
|
|
1002
|
+
},
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (active === 0) {
|
|
1006
|
+
return {
|
|
1007
|
+
ok: false, code: 'V5_MEMBER_NOT_FOUND',
|
|
1008
|
+
message: what + '失败:研究所当前没有在册(active)成员。',
|
|
1009
|
+
next: { kind: 'staff', tool: 'vibe_v5_add_researcher', hint: '先用 vibe_v5_add_researcher / vibe_v5_hire 补充成员,再调用该工具。' },
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (!who) {
|
|
1013
|
+
return {
|
|
1014
|
+
ok: false, code: 'V5_MEMBER_NOT_FOUND',
|
|
1015
|
+
message: what + '失败:无法识别调用者(这是成员工具,所办/会话根没有成员身份)。',
|
|
1016
|
+
next: { kind: 'member-call', tool: 'vibe_v5_members', hint: '请由成员子代理调用;所办可用 vibe_v5_message 转达,或直接用所办工具(vibe_v5_report / vibe_v5_status)。' },
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
ok: false, code: 'V5_MEMBER_NOT_FOUND',
|
|
1021
|
+
message: what + '失败:名册中没有成员 "' + who + '"(可能已被解雇或尚未就位)。',
|
|
1022
|
+
next: { kind: 'roster', tool: 'vibe_v5_members', hint: '先用 vibe_v5_members 查看当前名册与阶段(active / provisioning / dismissed)。' },
|
|
1023
|
+
}
|
|
928
1024
|
}
|
|
929
1025
|
function byChild(childId) { return inst().members.find((m) => m.childId === childId) }
|
|
930
1026
|
// The live academician's id, or '' when the office founded the institute with
|
|
@@ -1147,7 +1243,7 @@ export function apply(ctx) {
|
|
|
1147
1243
|
L.push(' 2. 只有 Verified/ 目录下的结论(以及成果卡中标注"已验证·真/假"的条目)绝对可信。')
|
|
1148
1244
|
L.push(' 其余一切——他人的推测、你自己的未验结论、Progress/、Methods/ 里的未验证断言——')
|
|
1149
1245
|
L.push(' 都只是经验性参考,引用时必须注明"未验证"。')
|
|
1150
|
-
L.push(' 3. 任何人可以读任何人的成果库;你只能写自己的库(Members
|
|
1246
|
+
L.push(' 3. 任何人可以读任何人的成果库;你只能写自己的库(' + instRel('Members/<你>/') + ')。')
|
|
1151
1247
|
L.push(' 4. 你写下的有价值内容由你自己判断是否入库,但入库必须写明三项:')
|
|
1152
1248
|
L.push(' 价值程度、动机用途计划、你自己对"该对象为真"的概率估计。')
|
|
1153
1249
|
L.push(' 5. 你随时可以在群聊里说话;要单独找人可以私信。需要集体决策就提议开会。')
|
|
@@ -1162,8 +1258,10 @@ export function apply(ctx) {
|
|
|
1162
1258
|
L.push('')
|
|
1163
1259
|
// ── 三、libraries ───────────────────────────────────────────────────
|
|
1164
1260
|
L.push('【三、你的资料库、progress 与卡片格式】')
|
|
1165
|
-
L.push('
|
|
1261
|
+
L.push(' 你的资料库根目录(**相对会话工作目录**):' + instRel('Members/' + member.id + '/'))
|
|
1166
1262
|
L.push(' (以下路径都相对该目录。你**只写这里**,但可以读任何人的对应目录。)')
|
|
1263
|
+
L.push(' ⚠ 路径基准:你自己的文件工具的**相对路径以会话工作目录为基准**,所以直接读写文件时必须用上面这条完整路径;')
|
|
1264
|
+
L.push(' 若只想记录成果,直接用 vibe_v5_record_progress / vibe_v5_record_proposition / vibe_v5_record_method / vibe_v5_record_subproblem,框架会写到正确位置。')
|
|
1167
1265
|
L.push('')
|
|
1168
1266
|
L.push(LIB_SPEC)
|
|
1169
1267
|
L.push('')
|
|
@@ -1301,7 +1399,7 @@ export function apply(ctx) {
|
|
|
1301
1399
|
const ms = activeMembers()
|
|
1302
1400
|
const b = []
|
|
1303
1401
|
b.push('[状态] 你是 ' + member.id + '(' + kindLabel(member.kind) + ')|轮次 ' + (rounds.get(member.id) || 0) +
|
|
1304
|
-
'|法定票数 m=' + quorumM() + '|有表决权者 ' + voterCount() + ' 人')
|
|
1402
|
+
'|法定票数 m=' + (voterCount() > 0 ? quorumM() : '未启动') + '|有表决权者 ' + voterCount() + ' 人')
|
|
1305
1403
|
b.push('[在册] ' + (ms.length ? ms.map((x) => x.id).join('、') : '(无)'))
|
|
1306
1404
|
// Members that are on the books but NOT on the floor. Silently omitting them made a
|
|
1307
1405
|
// failed provision invisible to the whole institute.
|
|
@@ -1768,7 +1866,7 @@ export function apply(ctx) {
|
|
|
1768
1866
|
const soft = (contextPct.get(member.id) || 0) >= Number(params.compactThreshold) ||
|
|
1769
1867
|
(roundsSinceCompact.get(member.id) || 0) >= Number(params.compactAfterRounds)
|
|
1770
1868
|
if (soft) {
|
|
1771
|
-
prompt =
|
|
1869
|
+
prompt = coreRules() + '\n[CONTEXT COMPACT — 你的对话已接近上限。不要重新推导历史。\n' +
|
|
1772
1870
|
'请把当前工作状态浓缩成一段自述(已有发现、当前方向、已记录的关键成果、下一步具体动作、未决问题),' +
|
|
1773
1871
|
'然后照常以 JSON 回答本轮。请在回复里填 "contextPct": 15 与 "compacted": true。]\n\n' + prompt
|
|
1774
1872
|
// Reset the counter WITH the injection so the directive cannot repeat on the
|
|
@@ -1777,7 +1875,7 @@ export function apply(ctx) {
|
|
|
1777
1875
|
}
|
|
1778
1876
|
}
|
|
1779
1877
|
if (needReanchor.has(member.id)) {
|
|
1780
|
-
prompt =
|
|
1878
|
+
prompt = coreRules() + '\n' + prompt
|
|
1781
1879
|
needReanchor.delete(member.id)
|
|
1782
1880
|
}
|
|
1783
1881
|
try {
|
|
@@ -1859,7 +1957,7 @@ export function apply(ctx) {
|
|
|
1859
1957
|
L.push(' "prioritize": {"order":[{"task_id":"t-1","priority":2}],"why":"…"} ← 设定全所优先级,')
|
|
1860
1958
|
L.push(' "nudge": {"to":"r-2","why":"为何督办","next_step":"建议的具体下一步"},')
|
|
1861
1959
|
}
|
|
1862
|
-
L.push(' "task_create": {"subject":"…","description":"…","blocked_by":["t-1"],"write_scopes":["Members/r-1/Propos"]},')
|
|
1960
|
+
L.push(' "task_create": {"subject":"…","description":"…","blocked_by":["t-1"],"write_scopes":["' + instRel('Members/r-1/Propos') + '"]},')
|
|
1863
1961
|
L.push(' "task_claim": "t-3",')
|
|
1864
1962
|
L.push(' "task_done": "t-3",')
|
|
1865
1963
|
L.push(' "task_update": {"task_id":"t-3","expected_revision":2,"action":"complete|release|reopen|edit|set_dependencies|delete"},')
|
|
@@ -2902,6 +3000,11 @@ export function apply(ctx) {
|
|
|
2902
3000
|
// reports MATH_NO_SUBPROCESS before engine detection), and let it check the retention cap.
|
|
2903
3001
|
hasSubprocess: () => { const sub = subprocessOf(); return !!(sub && typeof sub.spawn === 'function') },
|
|
2904
3002
|
listDir: (rel) => mathListDirRel(rel),
|
|
3003
|
+
// round-7 (fix 2): DSH's own bundled runtimes (`<home>/.dsh/dsh-runtimes/*/dependencies/<engine>/`)
|
|
3004
|
+
// as a LAST resort after PATH. Generic roots only; the module globs the tree name and accepts
|
|
3005
|
+
// only the descriptor's own candidate names.
|
|
3006
|
+
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'] : [] },
|
|
3007
|
+
listDirAbs: async (abs) => { try { const t = await fs.resolve(String(abs)); const st = await fs.stat(t); if (!st) return []; return (await fs.listDir(t)) || [] } catch (e) { return [] } },
|
|
2905
3008
|
log: (kind, msg) => mathLog(kind, msg),
|
|
2906
3009
|
}
|
|
2907
3010
|
// The DYNAMIC per-round availability line: computed from a (cached) probe when the prompt is
|
|
@@ -2923,6 +3026,10 @@ export function apply(ctx) {
|
|
|
2923
3026
|
// `scriptChanged` actionable for a member).
|
|
2924
3027
|
if (mathLineZh.indexOf(MATH_ARCHIVE_WORKFLOW_LINE) === -1) console.error('vibe-math-v5: the math availability line lost the archive workflow rule')
|
|
2925
3028
|
if (mathLineEn.indexOf(MATH_ARCHIVE_WORKFLOW_LINE_EN) === -1) console.error('vibe-math-v5: the EN math availability line lost the archive workflow rule')
|
|
3029
|
+
// Round-6 (A): an undeclared SUBSTITUTION must never read like the exact result — the
|
|
3030
|
+
// honesty rule has to reach the prompt in both languages.
|
|
3031
|
+
if (mathLineZh.indexOf(MATH_SUBSTITUTION_RULE_LINE) === -1) console.error('vibe-math-v5: the math availability line lost the substitution-honesty rule')
|
|
3032
|
+
if (mathLineEn.indexOf(MATH_SUBSTITUTION_RULE_LINE_EN) === -1) console.error('vibe-math-v5: the EN math availability line lost the substitution-honesty rule')
|
|
2926
3033
|
return mathLineZh
|
|
2927
3034
|
} catch (e) {
|
|
2928
3035
|
// A probe failure must never break prompt construction: keep whatever we had.
|
|
@@ -2935,11 +3042,12 @@ export function apply(ctx) {
|
|
|
2935
3042
|
const line = lang === 'en' ? mathLineEn : mathLineZh
|
|
2936
3043
|
if (line) return '\n' + line
|
|
2937
3044
|
// Fallback: when the dynamic line could not be built, the member still needs to know the
|
|
2938
|
-
// tool exists, how to cite it,
|
|
3045
|
+
// tool exists, how to cite it, that an edited script needs a NEW receipt, and (round 6) that
|
|
3046
|
+
// a substitution weakening exactness must be declared.
|
|
2939
3047
|
if (String(params.mathComputation) === 'off') return ''
|
|
2940
3048
|
return lang === 'en'
|
|
2941
|
-
? '\n' + MATH_PERSONA_TOOL_LINE + '\n' + MATH_ARCHIVE_WORKFLOW_LINE_EN
|
|
2942
|
-
: '\n' + MATH_PERSONA_TOOL_LINE + '\n' + MATH_ARCHIVE_WORKFLOW_LINE
|
|
3049
|
+
? '\n' + MATH_PERSONA_TOOL_LINE + '\n' + MATH_ARCHIVE_WORKFLOW_LINE_EN + '\n' + MATH_SUBSTITUTION_RULE_LINE_EN
|
|
3050
|
+
: '\n' + MATH_PERSONA_TOOL_LINE + '\n' + MATH_ARCHIVE_WORKFLOW_LINE + '\n' + MATH_SUBSTITUTION_RULE_LINE
|
|
2943
3051
|
}
|
|
2944
3052
|
// Push the tool section into a prompt under construction (a no-op in the 'off' mode, which
|
|
2945
3053
|
// is the "off means zero mention" discipline).
|
|
@@ -3316,9 +3424,12 @@ export function apply(ctx) {
|
|
|
3316
3424
|
// A SHORT core-rules recap, injected ONLY (a) right after a REAL compaction, or
|
|
3317
3425
|
// (b) in the same wake as a soft-compact directive — never on every round. The
|
|
3318
3426
|
// charter itself lives in `persona` and needs no reinforcement otherwise.
|
|
3319
|
-
|
|
3427
|
+
// A function, not a frozen string: the member-facing library path must reflect the LIVE
|
|
3428
|
+
// project/institute (DEFECT A) — a path captured at session creation could point at the
|
|
3429
|
+
// default project after a `configure`.
|
|
3430
|
+
const coreRules = () => '[核心规则] 只有 Verified/(及标记"已验证·真/假"的卡片)算已确立;' +
|
|
3320
3431
|
'任何对象要进 Verified/,必须至少有 m 名有表决权者投出布尔值(恰好 1 或恰好 0)**且没有任何一张反向票**,否则留库附平均概率;' +
|
|
3321
|
-
'你只写自己的库(Members
|
|
3432
|
+
'你只写自己的库(' + instRel('Members/<你>/') + '——相对**会话工作目录**),可只读任何人的库;组织与分派由院士负责,但判断属于你自己;' +
|
|
3322
3433
|
'退出时只输出一个 JSON 对象。'
|
|
3323
3434
|
|
|
3324
3435
|
// ONE place accounts for context usage on EVERY reply (normal, meeting, verify,
|
|
@@ -3365,7 +3476,7 @@ export function apply(ctx) {
|
|
|
3365
3476
|
return n
|
|
3366
3477
|
}
|
|
3367
3478
|
async function publishProgress(memberId, text) {
|
|
3368
|
-
if (!memberId || !memberById(memberId)) return
|
|
3479
|
+
if (!memberId || !memberById(memberId)) return memberDiagnosis('记录研究进度(vibe_v5_record_progress)', memberId)
|
|
3369
3480
|
if (!String(text || '').trim()) return { ok: false, code: 'V5_INVALID_ARGUMENT', message: 'empty progress' }
|
|
3370
3481
|
const rel = 'Members/' + memberId + '/Progress/progress.md'
|
|
3371
3482
|
const prev = (await readTextRel(rel)) || ''
|
|
@@ -3378,7 +3489,7 @@ export function apply(ctx) {
|
|
|
3378
3489
|
// hard requirements. Missing fields are refused rather than silently defaulted,
|
|
3379
3490
|
// so the libraries keep their meaning.
|
|
3380
3491
|
async function recordCard(memberId, kind, o) {
|
|
3381
|
-
if (!memberId || !memberById(memberId)) return
|
|
3492
|
+
if (!memberId || !memberById(memberId)) return memberDiagnosis('记录成果卡片(vibe_v5_record_proposition/method/subproblem)', memberId)
|
|
3382
3493
|
const args = o || {}
|
|
3383
3494
|
const missing = []
|
|
3384
3495
|
if (args.value === undefined || args.value === null) missing.push('value(价值程度)')
|
|
@@ -3541,7 +3652,7 @@ export function apply(ctx) {
|
|
|
3541
3652
|
// An unknown caller must be refused HERE as well: `owner = task.ownerId === memberId` is true
|
|
3542
3653
|
// for an UNOWNED task (ownerId '') when memberId is '', so without this guard an
|
|
3543
3654
|
// unidentifiable caller would count as the owner of every unclaimed task (audit L6 follow-up).
|
|
3544
|
-
if (!memberId) return
|
|
3655
|
+
if (!memberId) return memberDiagnosis('修改任务(vibe_v5_task_update)', memberId)
|
|
3545
3656
|
const args = o || {}
|
|
3546
3657
|
const id = String(args.task_id || args.taskId || '')
|
|
3547
3658
|
const task = inst().tasks.find((x) => x.id === id)
|
|
@@ -3638,7 +3749,7 @@ export function apply(ctx) {
|
|
|
3638
3749
|
// it can never make any statement true, and the assignee may object with reasons
|
|
3639
3750
|
// (the objection is broadcast, not silently swallowed).
|
|
3640
3751
|
async function taskAssign(memberId, o) {
|
|
3641
|
-
if (!memberId) return
|
|
3752
|
+
if (!memberId) return memberDiagnosis('分派任务(vibe_v5_assign)', memberId)
|
|
3642
3753
|
if (!isOffice(memberId) && !(isAcademician(memberId) && params.academicianLeads)) {
|
|
3643
3754
|
return { ok: false, code: 'V5_NOT_ACADEMICIAN', message: 'only the academician (or the office) can assign tasks' }
|
|
3644
3755
|
}
|
|
@@ -3682,7 +3793,7 @@ export function apply(ctx) {
|
|
|
3682
3793
|
return { ok: true, task: taskView(withMeta) }
|
|
3683
3794
|
}
|
|
3684
3795
|
async function taskPrioritize(memberId, o) {
|
|
3685
|
-
if (!memberId) return
|
|
3796
|
+
if (!memberId) return memberDiagnosis('设置任务优先级(vibe_v5_task_prioritize)', memberId)
|
|
3686
3797
|
if (!isOffice(memberId) && !(isAcademician(memberId) && params.academicianLeads)) {
|
|
3687
3798
|
return { ok: false, code: 'V5_NOT_ACADEMICIAN', message: 'only the academician (or the office) can set priorities' }
|
|
3688
3799
|
}
|
|
@@ -3734,7 +3845,7 @@ export function apply(ctx) {
|
|
|
3734
3845
|
const s = inst()
|
|
3735
3846
|
const lines = ['# 研究所编制表(人读镜像)|' + instituteName + '|' + fmtTime(), '',
|
|
3736
3847
|
'> 权威状态在 State/<研究所>.v5state.json 里;本文件只是快照,勿手改。', '']
|
|
3737
|
-
lines.push('- 求真门槛:m = ' + quorumM() + '(模式 ' + params.quorumMode + ')|有表决权者 ' + voterCount() + ' 人')
|
|
3848
|
+
lines.push('- 求真门槛:' + (voterCount() > 0 ? 'm = ' + quorumM() : '未启动(有表决权者 0 人,m 未定义)') + '(模式 ' + params.quorumMode + ')|有表决权者 ' + voterCount() + ' 人')
|
|
3738
3849
|
lines.push('- 阶段:' + phase + '|运行中:' + running + '|已结题:' + autoDone)
|
|
3739
3850
|
lines.push('')
|
|
3740
3851
|
lines.push('| 代号 | 职位 | 状态 | 雇主 | 方向/用途 | 轮次 | 上下文% |')
|
|
@@ -3861,6 +3972,9 @@ export function apply(ctx) {
|
|
|
3861
3972
|
}
|
|
3862
3973
|
const mean = all.length ? all.reduce((a, x) => a + x, 0) / all.length : 0.5
|
|
3863
3974
|
const base = { m, P, bTrue, bFalse, abstain, mean, votedCount: all.length, voters: E }
|
|
3975
|
+
// DEFECT 2 hardening: with NO voters there is nothing to conclude — every `>= m` test
|
|
3976
|
+
// below would be trivially satisfiable if m were ever 0. Consensus needs voters.
|
|
3977
|
+
if (P === 0) return Object.assign(base, { outcome: 'undecided', reason: 'no voters: the institute has no voting members yet' })
|
|
3864
3978
|
if (params.quorumMode === 'all-unanimous') {
|
|
3865
3979
|
const allVoted = E.length > 0 && E.every((id) => votes[id])
|
|
3866
3980
|
if (!allVoted) return Object.assign(base, { outcome: 'undecided', reason: 'not every voter has voted' })
|
|
@@ -4207,7 +4321,7 @@ export function apply(ctx) {
|
|
|
4207
4321
|
// Record one vote and, when every voter has answered, settle the round.
|
|
4208
4322
|
async function castVerdict(memberId, target, verdict, reason) {
|
|
4209
4323
|
const member = memberById(memberId)
|
|
4210
|
-
if (!member) return
|
|
4324
|
+
if (!member) return memberDiagnosis('投票(vibe_v5_verdict)', memberId)
|
|
4211
4325
|
if (member.kind === 'temp') {
|
|
4212
4326
|
// Temp workers have no vote — but their judgement still matters, so it is
|
|
4213
4327
|
// relayed to the group instead of being silently dropped.
|
|
@@ -4566,7 +4680,7 @@ export function apply(ctx) {
|
|
|
4566
4680
|
L.push('【最终论文·撰写 —— ' + kindLabel(member.kind) + ' ' + member.id + '】')
|
|
4567
4681
|
L.push('本所对原问题的一致结论已经达成,现在撰写**最终论文**(第 ' + p.round + '/' + PAPER_MAX_ROUNDS + ' 轮)。')
|
|
4568
4682
|
L.push('请你**只写你自己库里已有证据支撑**的内容:')
|
|
4569
|
-
L.push('- 直接引用你的卡片(Members/' + member.id + '/Propos|Methods|Subproblems
|
|
4683
|
+
L.push('- 直接引用你的卡片(' + instRel('Members/' + member.id + '/Propos|Methods|Subproblems/') + ')、' + instRel('Members/' + member.id + '/Progress/progress.md') + '、你参与的表决记录;')
|
|
4570
4684
|
L.push('- **不得编造**:没有证据的推测不要写成结论;未决 / 被否证的条目必须显式标注“未定论 / 已被否证”;')
|
|
4571
4685
|
L.push('- 在 evidence 里写清证据路径,附录会逐条索引。')
|
|
4572
4686
|
if (p.round > 1) {
|
|
@@ -4586,7 +4700,7 @@ export function apply(ctx) {
|
|
|
4586
4700
|
L.push(' "methods": "你创造/发现的方法、理论、思想、有价值经验、数学理解",')
|
|
4587
4701
|
L.push(' "rules": "你从这些工作中归纳出的可复用规律",')
|
|
4588
4702
|
L.push(' "limits": "局限、未决、被否证之处(必须诚实、显式)",')
|
|
4589
|
-
L.push(' "evidence": ["Members/' + member.id + '/Propos/p-x.md"] } }')
|
|
4703
|
+
L.push(' "evidence": ["' + instRel('Members/' + member.id + '/Propos/p-x.md') + '"] } }')
|
|
4590
4704
|
return L.join('\n')
|
|
4591
4705
|
}
|
|
4592
4706
|
function paperReviewPrompt(member, p, ofId) {
|
|
@@ -5012,8 +5126,10 @@ export function apply(ctx) {
|
|
|
5012
5126
|
}
|
|
5013
5127
|
if (ev.formalKeys.length) out.push('Formal/Index.md', 'Formal/TODO.md')
|
|
5014
5128
|
for (const m of ev.members) {
|
|
5015
|
-
|
|
5016
|
-
|
|
5129
|
+
// Member-facing evidence paths are CWD-relative (DEFECT A): a member copying one of these
|
|
5130
|
+
// into its own file tool must not create a stray `<cwd>/Members/…` tree.
|
|
5131
|
+
out.push(instRel('Members/' + m.id + '/Progress/progress.md') + '(' + m.id + ' 的研究日志)')
|
|
5132
|
+
out.push(instRel('Members/' + m.id + '/{Propos,Methods,Subproblems}/') + '(' + m.id + ' 的卡片)')
|
|
5017
5133
|
}
|
|
5018
5134
|
out.push('Institutes.md(编制镜像)', 'Shared/TaskBoard.md(任务板镜像)')
|
|
5019
5135
|
out.push('Paper/' + p.id + '/paper.log.md(本论文流程往来)')
|
|
@@ -5496,7 +5612,7 @@ export function apply(ctx) {
|
|
|
5496
5612
|
return { ok: true, dismissed: id, reclaimedTasks: reclaimed, reason }
|
|
5497
5613
|
}
|
|
5498
5614
|
async function nudge(callerId, o) {
|
|
5499
|
-
if (!callerId) return
|
|
5615
|
+
if (!callerId) return memberDiagnosis('督办(vibe_v5_nudge)', callerId)
|
|
5500
5616
|
if (!isOffice(callerId) && !(isAcademician(callerId) && params.academicianLeads)) {
|
|
5501
5617
|
return { ok: false, code: 'V5_NOT_ACADEMICIAN', message: 'only the academician (or the office) can nudge members' }
|
|
5502
5618
|
}
|
|
@@ -6091,7 +6207,7 @@ export function apply(ctx) {
|
|
|
6091
6207
|
memberMayRejectAssign: params.memberMayRejectAssign,
|
|
6092
6208
|
researcherCount: params.researcherCount,
|
|
6093
6209
|
quorumCap: params.quorumCap, quorumMode: params.quorumMode,
|
|
6094
|
-
m:
|
|
6210
|
+
m: quorumView().m, voterCount: voterCount(), started: voterCount() > 0,
|
|
6095
6211
|
verdictMaxRounds: params.verdictMaxRounds,
|
|
6096
6212
|
maxTempPerMember: params.maxTempPerMember, maxTempTotal: params.maxTempTotal,
|
|
6097
6213
|
compactThreshold: params.compactThreshold, compactAfterRounds: params.compactAfterRounds,
|
|
@@ -6407,7 +6523,7 @@ export function apply(ctx) {
|
|
|
6407
6523
|
// failure modes that silently drop state were invisible in the operator's view.
|
|
6408
6524
|
diagnostics: s.diagnostics || [],
|
|
6409
6525
|
debug: Object.assign({ scheduling, reschedule }, dbg),
|
|
6410
|
-
quorum:
|
|
6526
|
+
quorum: quorumView(),
|
|
6411
6527
|
members: s.members.map((m) => ({
|
|
6412
6528
|
id: m.id, kind: m.kind, phase: m.phase, direction: m.direction, hiredBy: m.hiredBy,
|
|
6413
6529
|
rounds: rounds.get(m.id) || 0, busy: busy.has(m.id), contextPct: contextPct.get(m.id) || 0,
|
|
@@ -6444,12 +6560,15 @@ export function apply(ctx) {
|
|
|
6444
6560
|
function report() {
|
|
6445
6561
|
syncParamsFromState()
|
|
6446
6562
|
const s = inst()
|
|
6563
|
+
// ONE snapshot for the whole report: the text line and the returned `quorum` object can
|
|
6564
|
+
// never disagree (DEFECT B), and `rosterVersion` lets a reader detect a stale copy.
|
|
6565
|
+
const qv = quorumView()
|
|
6447
6566
|
const L = []
|
|
6448
6567
|
L.push('# 「' + instituteName + '」研究所汇报')
|
|
6449
6568
|
L.push('')
|
|
6450
6569
|
L.push('- 项目:' + project + '|阶段:' + phase + '|运行中:' + running + '|已结题:' + autoDone)
|
|
6451
6570
|
L.push('- 研究对象:' + (s.problem.statement ? s.problem.statement.slice(0, 200) : '(未设定)'))
|
|
6452
|
-
L.push('- 求真门槛:m = ' +
|
|
6571
|
+
L.push('- 求真门槛:' + (qv.started ? 'm = ' + qv.m : '未启动(有表决权者 0 人,m 未定义)') + '(模式 ' + params.quorumMode + ')|有表决权者 ' + qv.voterCount + ' 人|rosterVersion ' + qv.rosterVersion)
|
|
6453
6572
|
L.push('')
|
|
6454
6573
|
L.push('## 编制')
|
|
6455
6574
|
if (!s.members.length) L.push('(暂无成员)')
|
|
@@ -6521,8 +6640,8 @@ export function apply(ctx) {
|
|
|
6521
6640
|
L.push('')
|
|
6522
6641
|
L.push('## 文件位置')
|
|
6523
6642
|
L.push('- 根目录:' + instRoot())
|
|
6524
|
-
L.push('-
|
|
6525
|
-
return { ok: true, report: L.join('\n') }
|
|
6643
|
+
L.push('- 工作目录相对路径(你的文件工具的基准):' + instRootRel() + '/|已确立:Verified/|成员库:Members/<id>/|群聊:Shared/Chat/|会议:Shared/Meetings/|辩论:Shared/Debates/')
|
|
6644
|
+
return { ok: true, report: L.join('\n'), quorum: qv }
|
|
6526
6645
|
}
|
|
6527
6646
|
// Adding/removing a PERMANENT researcher is a change to the institute's public
|
|
6528
6647
|
// structure, so members may only propose it; the office decides and executes.
|
|
@@ -6594,6 +6713,8 @@ export function apply(ctx) {
|
|
|
6594
6713
|
startPaper, paperStatus: paperSummary, finalizePaperByOffice,
|
|
6595
6714
|
// authorization helpers (used by tool handlers)
|
|
6596
6715
|
memberIdOfAgent, isOffice, isAcademician, isProvablyOffice, officeCaller, memberById, activeMembers,
|
|
6716
|
+
// diagnosis helpers (defects 1/2 of the architecture self-test)
|
|
6717
|
+
memberDiagnosis, quorumM, quorumView, voterCount,
|
|
6597
6718
|
}
|
|
6598
6719
|
}
|
|
6599
6720
|
|
|
@@ -6788,7 +6909,7 @@ export function apply(ctx) {
|
|
|
6788
6909
|
// ── member-facing controls ────────────────────────────────────────────────
|
|
6789
6910
|
registerTool('vibe_v5_say', '(member) Speak in the group chat (omit "to"), send a private message ("to":"r-2"), or address only the voters ("to":"voters").', objParams({ text: S, to: S }, ['text']), (s, a, x) => {
|
|
6790
6911
|
const from = s.memberIdOfAgent(x)
|
|
6791
|
-
if (!from) return
|
|
6912
|
+
if (!from) return s.memberDiagnosis('发言(vibe_v5_say)', from)
|
|
6792
6913
|
const to = a.to || 'all'
|
|
6793
6914
|
return s.say(from, { to, text: a.text, kind: to === 'voters' ? 'voters' : (a.to ? 'dm' : 'chat') })
|
|
6794
6915
|
})
|