dsh-vibe-math 2.2.1 → 2.3.0

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.
Files changed (38) hide show
  1. package/AUDIT-CHECKLIST.md +48 -2
  2. package/README.md +368 -81
  3. package/RELEASE-NOTES-2.2.2.md +88 -0
  4. package/RELEASE-NOTES-2.3.0.md +207 -0
  5. package/audit-formal-sensitivity.mjs +247 -0
  6. package/audit-persona-sensitivity.mjs +249 -0
  7. package/audit-persona-surface.test.mjs +349 -0
  8. package/audit-v5-integrity.mjs +40 -1
  9. package/audit-v5-sensitivity.mjs +84 -6
  10. package/docs/formal-verification.md +321 -0
  11. package/docs/generate_framework_diagram_v5.mjs +290 -0
  12. package/docs//346/236/266/346/236/204/345/233/276.md +75 -0
  13. package/formal-verify-v2.test.mjs +672 -0
  14. package/formal-verify-v3.test.mjs +824 -0
  15. package/formal-verify-v4.test.mjs +603 -0
  16. package/formal-verify-v5.test.mjs +526 -0
  17. package/package.json +33 -15
  18. package/prompt-corpus-persona/persona-corpus.json +32 -0
  19. package/prompt-corpus-persona/persona-corpus.md +674 -0
  20. package/prompt-corpus-v3/formal-verify-v3.json +280 -0
  21. package/prompt-corpus-v3/formal-verify-v3.md +2826 -0
  22. package/prompt-corpus-v5/prompt-corpus-v5.json +131 -713
  23. package/prompt-corpus-v5/prompt-corpus-v5.md +1828 -5225
  24. package/prompt-v5-integrity.test.mjs +154 -11
  25. package/vibe-math-v2/agent.cordis.yml +40 -2
  26. package/vibe-math-v2/vibe-math-v2.js +627 -19
  27. package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +145 -1
  28. package/vibe-math-v3/agent.cordis.yml +46 -2
  29. package/vibe-math-v3/vibe-math-v3.js +749 -21
  30. package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +87 -2
  31. package/vibe-math-v4/agent.cordis.yml +46 -4
  32. package/vibe-math-v4/vibe-math-v4.js +652 -15
  33. package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +226 -0
  34. package/vibe-math-v5/agent.cordis.yml +41 -5
  35. package/vibe-math-v5/vibe-math-v5.js +572 -9
  36. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +122 -4
  37. package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +426 -0
  38. package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5.svg +173 -0
@@ -130,16 +130,29 @@ const ctx = {
130
130
  if (name === 'compaction') return undefined
131
131
  if (name === 'subprocess') {
132
132
  return {
133
- async spawn({ argv }) {
134
- const script = argv[argv.length - 1] || ''
135
- if (/New-Item/.test(script)) {
133
+ async resolveExecutable(cmd) { return String(cmd) },
134
+ spawn({ argv }) {
135
+ const last = argv[argv.length - 1] || ''
136
+ // directory creation still goes through the same mock (mkdirs uses a shell)
137
+ if (/New-Item/.test(last)) {
136
138
  const paths = []
137
139
  const re = /'((?:[^']|'')*)'/g
138
140
  let m
139
- while ((m = re.exec(script)) !== null) paths.push(m[1].replace(/''/g, "'"))
140
- for (const p of paths) if (p && !/^-/.test(p)) mkdirSync(p, { recursive: true })
141
+ while ((m = re.exec(last)) !== null) paths.push(m[1].replace(/''/g, "'"))
142
+ for (const q of paths) if (q && !/^-/.test(q)) mkdirSync(q, { recursive: true })
143
+ return { done: Promise.resolve({ exitCode: 0, signal: null }), collected: {}, terminate() {} }
144
+ }
145
+ // The fake Lean toolchain: GREEN unless the file still uses sorry / carries -- FAIL.
146
+ // Case 12 needs a proof that really passes so the prompt switches to fidelity review.
147
+ const text = existsSync(last) ? readFileSync(last, 'utf8') : ''
148
+ const bad = /sorry|-- FAIL/.test(text)
149
+ const ok = { text: 'ok\n', nextOffset: 3, lossy: false }
150
+ const err = { text: bad ? 'error: declaration uses sorry\n' : '', nextOffset: 0, lossy: false }
151
+ return {
152
+ done: Promise.resolve({ exitCode: bad ? 1 : 0, signal: null }),
153
+ collected: { stdout: { readFrom: () => ok }, stderr: { readFrom: () => err } },
154
+ terminate() {},
141
155
  }
142
- return { done: Promise.resolve({ exitCode: 0 }) }
143
156
  },
144
157
  }
145
158
  }
@@ -221,8 +234,39 @@ const spawnOf = (root, memberId) => spawns.find(s => s.rootId === root.id && s.l
221
234
  const childOf = (root, memberId) => { const s = spawnOf(root, memberId); return s ? s.childId : '' }
222
235
  const spawnsFor = (root) => spawns.filter(s => s.rootId === root.id)
223
236
 
237
+ // Pull exactly the VOTING prompts for one root. A plain FIFO drain returns whatever was
238
+ // queued first (work rounds, heartbeats), which is how an earlier version of this case ended
239
+ // up asserting against the wrong prompt entirely.
240
+ async function takeVerifyPrompts(root, n) {
241
+ const got = []
242
+ for (let guard = 0; guard < 400 && got.length < n; guard++) {
243
+ const idx = wakes.findIndex(w => w.rootId === root.id && /【求真表决/.test(w.prompt))
244
+ if (idx === -1) {
245
+ const other = wakes.findIndex(w => w.rootId === root.id)
246
+ if (other !== -1) {
247
+ const w = wakes.splice(other, 1)[0]
248
+ delivered.push({ prompt: w.prompt, owner: memberOfChild(w.childId), rootId: w.rootId })
249
+ fireEnd(w.childId, { progress: '(语料采样时略过非表决轮)', contextPct: 20 })
250
+ await settle()
251
+ continue
252
+ }
253
+ await settle()
254
+ continue
255
+ }
256
+ const w = wakes.splice(idx, 1)[0]
257
+ got.push(w)
258
+ delivered.push({ prompt: w.prompt, owner: memberOfChild(w.childId), rootId: w.rootId })
259
+ fireEnd(w.childId, { verdict: { target: (/"target"\s*:\s*"([^"]+)"/.exec(w.prompt) || [])[1] || '', verdict: 0.5, reason: '语料采样' }, contextPct: 20 })
260
+ await settle()
261
+ }
262
+ return got
263
+ }
264
+
224
265
  let votePlan = new Map() // memberId -> verdict number for the next verify prompts
225
266
  let replyOverride = new Map() // memberId -> the exact reply its NEXT wake must produce
267
+ // Roots whose MEETING prompts the driver must NOT answer, so the meeting stays in flight
268
+ // (case 10b needs a live meeting to test that a verification cannot preempt it).
269
+ const hushed = new Set()
226
270
  // Handle queued sends. `delivered` collects what was actually sent for the case under
227
271
  // test, because the queue is consumed here and assertions must not read it afterwards.
228
272
  // Wakes belonging to OTHER roots are skipped over rather than allowed to block: a case
@@ -231,7 +275,8 @@ let replyOverride = new Map() // memberId -> the exact reply its NEXT wake mus
231
275
  async function drainWakes(budget, root) {
232
276
  let n = 0
233
277
  while (n < budget) {
234
- const idx = wakes.findIndex(w => !root || w.rootId === root.id)
278
+ const idx = wakes.findIndex(w => (!root || w.rootId === root.id)
279
+ && !(hushed.has(w.rootId) && /【研究所会议/.test(w.prompt)))
235
280
  if (idx === -1) break
236
281
  const w = wakes.splice(idx, 1)[0]
237
282
  const owner = memberOfChild(w.childId)
@@ -793,6 +838,44 @@ assert(/【研究所·致全体表决者 from r-1】[^\n]*提议开会/.test(pro
793
838
  'the meeting proposal is relayed SIGNED BY ITS TRUE PROPOSER r-1, not by whoever was woken last')
794
839
  await endCase(RI)
795
840
 
841
+ // =============== CASE 10b: meetings and verifications are mutually exclusive ======
842
+ section('10b a verification proposed DURING a meeting must queue, never preempt it')
843
+ const RN = makeRoot()
844
+ await callTool('vibe_v5_start', { problem: '会议与验证互斥测试', researcherCount: 1 }, RN)
845
+ for (const sp of spawnsFor(RN)) { fireEnd(sp.childId, { progress: memberOfChild(sp.childId) + ':初始见解。', solved: false, contextPct: 10 }); await settle() }
846
+ await settleInstitute(RN)
847
+ await callTool('vibe_v5_set', { maxParallel: 8 }, RN)
848
+ await callTool('vibe_v5_record_proposition', { id: 'p-mid', statement: '会议期间提出的对象', value: 0.6, motive: 'm', p: 0.7 }, childAgent(childOf(RN, 'r-1')))
849
+ // Convene a meeting and stop before it has collected every input, so it stays in flight.
850
+ hushed.add(RN.id)
851
+ delivered.length = 0
852
+ const convened = await callTool('vibe_v5_meeting', { agenda: '先开这个会', kind: 'sync' }, childAgent(childOf(RN, 'acad')))
853
+ assert(convened.ok === true, 'a meeting was convened (' + JSON.stringify(convened).slice(0, 80) + ')')
854
+ await settle(); await drainWakes(1, RN)
855
+ const during = await callTool('vibe_v5_status', {}, RN)
856
+ assert(!!during.meeting, 'the meeting is still in flight (not everyone has spoken)')
857
+ // A member proposing a verification mid-meeting must NOT start a second, concurrent
858
+ // consensus process: the design says meetings and verifications never overlap, and a
859
+ // verification that preempts a meeting starves the meeting's watchdog clock.
860
+ const propMid = await callTool('vibe_v5_propose_verify', { target: 'p-mid', kind: 'proposition', reason: '想在会上定' }, childAgent(childOf(RN, 'r-1')))
861
+ assert(propMid.ok === true, 'the proposal is accepted (' + JSON.stringify(propMid).slice(0, 90) + ')')
862
+ await settle()
863
+ const afterProp = await callTool('vibe_v5_status', {}, RN)
864
+ assert(!!afterProp.meeting, 'the meeting is STILL in flight after the proposal')
865
+ assert(afterProp.verify === null,
866
+ 'NO verification started while the meeting was in flight (got ' + JSON.stringify(afterProp.verify && afterProp.verify.target) + ')')
867
+ assert((afterProp.verifyQueue || []).indexOf('p-mid') !== -1,
868
+ 'the proposal is QUEUED instead (queue=' + JSON.stringify(afterProp.verifyQueue) + ')')
869
+ // Once the meeting ends, the queued proposal must run — queueing must not drop it.
870
+ hushed.delete(RN.id)
871
+ await settleInstitute(RN)
872
+ const afterMtg = await callTool('vibe_v5_status', {}, RN)
873
+ assert(afterMtg.meeting === null, 'the meeting finished')
874
+ assert(afterMtg.verify !== null || afterMtg.undecided.indexOf('p-mid') !== -1 || afterMtg.verified.indexOf('p-mid') !== -1,
875
+ 'the queued proposal was started after the meeting ended (verify=' + JSON.stringify(afterMtg.verify && afterMtg.verify.target)
876
+ + ', queue=' + JSON.stringify(afterMtg.verifyQueue) + ')')
877
+ await endCase(RN)
878
+
796
879
  // =============== CASE 11: no unpaced re-wake loop ===============================
797
880
  section('11 a task owner is pushed on a PACED cadence, not in a tight loop')
798
881
  const RJ = makeRoot()
@@ -813,8 +896,66 @@ const unpaced = wakes.filter(w => w.rootId === RJ.id)
813
896
  assert(unpaced.length === 0, 'no unpaced re-wake of the task owner within the idle window (got ' + unpaced.length + ')')
814
897
  await endCase(RJ)
815
898
 
899
+ // =============== CASE 12: Lean mode prompt text =================================
900
+ section('12 Lean formal-verification text enters the prompts (and the corpus)')
901
+ const RK = makeRoot()
902
+ await callTool('vibe_v5_start', { problem: 'Lean 提示词测试', researcherCount: 2 }, RK)
903
+ for (const sp of spawnsFor(RK)) { fireEnd(sp.childId, { progress: memberOfChild(sp.childId) + ':初始见解。', solved: false, contextPct: 10 }); await settle() }
904
+ await settleInstitute(RK)
905
+ await callTool('vibe_v5_set', { maxParallel: 8, formalVerify: 'encourage' }, RK)
906
+ // (a) an ordinary work round carries the "formalize reusable things as you go" request
907
+ delivered.length = 0
908
+ await callTool('vibe_v5_say', { to: 'r-1', text: '继续推进。' }, childAgent(childOf(RK, 'acad')))
909
+ await settle(); await drainWakes(3, RK)
910
+ for (const w of delivered.filter(d => d.rootId === RK.id)) recordAndCheck('lean-work', w.owner, w.prompt)
911
+ {
912
+ const txt = delivered.filter(d => d.rootId === RK.id).map(d => d.prompt).join('\n')
913
+ assert(/\[形式化\] 鼓励 Lean/.test(txt), 'the state block announces the Lean mode with its counts')
914
+ assert(/【顺手形式化(鼓励)】/.test(txt), 'the work round asks for reusable objects to be formalized as work proceeds')
915
+ }
916
+ // (b) a voting round on an object WITHOUT a proof carries the "decide by difficulty" block
917
+ await callTool('vibe_v5_record_proposition', { id: 'p-lean-a', statement: 'Lean 语料对象甲', value: 0.6, motive: 'm', p: 0.8 }, childAgent(childOf(RK, 'r-1')))
918
+ const propA = await callTool('vibe_v5_propose_verify', { target: 'p-lean-a', kind: 'proposition', reason: '语料' }, childAgent(childOf(RK, 'r-1')))
919
+ assert(propA.ok === true && propA.started === true, 'the Lean corpus ballot for object 甲 actually started (' + JSON.stringify(propA).slice(0, 90) + ')')
920
+ delivered.length = 0
921
+ const vwA = await takeVerifyPrompts(RK, 3)
922
+ assert(vwA.length === 3, 'captured three voting prompts for object 甲 (got ' + vwA.length + ')')
923
+ for (const w of vwA) recordAndCheck('lean-verify', memberOfChild(w.childId), w.prompt)
924
+ {
925
+ const txt = delivered.filter(d => d.rootId === RK.id).map(d => d.prompt).join('\n')
926
+ assert(/【Lean 形式化验证(鼓励模式)】/.test(txt), 'the voting prompt explains the Lean mode')
927
+ assert(/你唯一需要确认的就是忠实性/.test(txt), 'the voting prompt states the fidelity question')
928
+ }
929
+ await endCase(RK)
930
+ // (c) once a proof passes, the voting prompt switches to the fidelity review. This uses its
931
+ // own root: object 甲's ballot may still be in flight above, and a queued proposal would
932
+ // make the drained prompts belong to the WRONG ballot (the assertion would then fail for a
933
+ // reason that has nothing to do with the feature).
934
+ const RL2 = makeRoot()
935
+ await callTool('vibe_v5_start', { problem: 'Lean 忠实性提示词测试', researcherCount: 2 }, RL2)
936
+ for (const sp of spawnsFor(RL2)) { fireEnd(sp.childId, { progress: memberOfChild(sp.childId) + ':初始见解。', solved: false, contextPct: 10 }); await settle() }
937
+ await settleInstitute(RL2)
938
+ await callTool('vibe_v5_set', { maxParallel: 8, formalVerify: 'encourage' }, RL2)
939
+ await callTool('vibe_v5_record_proposition', { id: 'p-lean-b', statement: 'Lean 语料对象乙', value: 0.6, motive: 'm', p: 0.9 }, childAgent(childOf(RL2, 'r-1')))
940
+ const leanB = await callTool('vibe_v5_lean_archive', { kind: 'proof', target: 'p-lean-b', content: 'theorem p_lean_b : 1 + 1 = 2 := by decide\n' }, childAgent(childOf(RL2, 'r-1')))
941
+ assert(leanB.ok === true && leanB.passed === true, 'object 乙 has a proof that really passes (' + JSON.stringify({ ok: leanB.ok, passed: leanB.passed, code: leanB.run && leanB.run.code }) + ')')
942
+ const propB = await callTool('vibe_v5_propose_verify', { target: 'p-lean-b', kind: 'proposition', reason: '已有证明' }, childAgent(childOf(RL2, 'r-1')))
943
+ assert(propB.ok === true && propB.started === true, 'the Lean corpus ballot for object 乙 actually started (' + JSON.stringify(propB).slice(0, 90) + ')')
944
+ delivered.length = 0
945
+ const vwB = await takeVerifyPrompts(RL2, 3)
946
+ assert(vwB.length === 3, 'captured three voting prompts for object 乙 (got ' + vwB.length + ')')
947
+ for (const w of vwB) recordAndCheck('lean-fidelity', memberOfChild(w.childId), w.prompt)
948
+ {
949
+ const txt = vwB.map(w => w.prompt).join('\n')
950
+ assert(/该对象已有\*\*通过的 Lean 形式化证明\*\*/.test(txt), 'the prompt announces the passing proof')
951
+ assert(/你不需要重新检查推导/.test(txt), 'with a proof in hand the prompt tells voters not to re-derive')
952
+ assert(/忠实性审查/.test(txt), 'and asks for a fidelity review instead')
953
+ }
954
+ await drainWakes(10, RL2)
955
+ await endCase(RL2)
956
+
816
957
  // =============== PART: full-corpus sweep ========================================
817
- section('12 full-corpus sweep over every prompt ever sent')
958
+ section('13 full-corpus sweep over every prompt ever sent')
818
959
  {
819
960
  let swept = 0
820
961
  for (const sp of spawns) {
@@ -829,7 +970,8 @@ section('12 full-corpus sweep over every prompt ever sent')
829
970
  const kinds = new Set(corpus.map(c => c.kind))
830
971
  for (const need of ['founding', 'founding-temp', 'founding-leaderless', 'resume', 'normal', 'checkpoint',
831
972
  'verify', 'verify-debate', 'meeting', 'meeting-proposal', 'inbox-dm', 'inbox-voters', 'inbox-chat',
832
- 'inbox-office', 'inbox-assign', 'inbox-nudge', 'notice', 'notice-claim', 'after-failure']) {
973
+ 'inbox-office', 'inbox-assign', 'inbox-nudge', 'notice', 'notice-claim', 'after-failure',
974
+ 'lean-work', 'lean-verify', 'lean-fidelity']) {
833
975
  assert(kinds.has(need), 'the corpus contains a ' + need + ' prompt')
834
976
  }
835
977
  assert(corpus.every(c => c.prompt && c.prompt.length > 200), 'no captured prompt is suspiciously short')
@@ -858,7 +1000,7 @@ section('12 full-corpus sweep over every prompt ever sent')
858
1000
  }
859
1001
 
860
1002
  // =============== corpus dump ====================================================
861
- section('13 the full prompt corpus is preserved for human review')
1003
+ section('14 the full prompt corpus is preserved for human review')
862
1004
  mkdirSync(CORPUS_DIR, { recursive: true })
863
1005
  const md = []
864
1006
  md.push('# Vibe Math V5 — 提示词与交互语料(自动生成,请勿手改)')
@@ -875,7 +1017,8 @@ md.push('')
875
1017
  const seenPersona = new Set()
876
1018
  const order = ['founding', 'founding-temp', 'founding-leaderless', 'resume', 'normal', 'checkpoint',
877
1019
  'verify', 'verify-debate', 'meeting', 'meeting-proposal', 'inbox-dm', 'inbox-voters', 'inbox-chat',
878
- 'inbox-office', 'inbox-assign', 'inbox-nudge', 'notice', 'notice-claim', 'after-failure']
1020
+ 'inbox-office', 'inbox-assign', 'inbox-nudge', 'notice', 'notice-claim', 'after-failure',
1021
+ 'lean-work', 'lean-verify', 'lean-fidelity']
879
1022
  const sorted = corpus.slice().sort((a, b) => order.indexOf(a.kind) - order.indexOf(b.kind))
880
1023
  for (let i = 0; i < sorted.length; i++) {
881
1024
  const c = sorted[i]
@@ -28,11 +28,14 @@
28
28
  - vibe_math_status / vibe_math_report — read scheduler status / full progress report (report also writes Progress_Logs/report.json).
29
29
  - vibe_math_pause / vibe_math_abort — pause / abort (abort interrupts all children).
30
30
  - vibe_math_set_mode {mode: manual|auto} — switch manual / auto control.
31
- - vibe_math_set_params {...} — tune any parameter (see vibe_math_setup for the full schema; e.g. reportMode file|push|both, promoteValueThreshold, verdictMode flat|forced).
31
+ - vibe_math_set_params {...} — tune any parameter (see vibe_math_setup for the full schema; e.g. reportMode file|push|both, promoteValueThreshold, verdictMode flat|forced, formalVerify off|encourage|require).
32
32
  - vibe_math_setup / vibe_math_save_settings / vibe_math_template — guided configuration / persist defaults / generate template.
33
33
  - vibe_math_new_project / vibe_math_set_project / vibe_math_list_projects — per-project folders.
34
34
  - vibe_math_list_decisions / vibe_math_decide {id, action: approve|reject|override, verdict?} — resolve manual decisions.
35
35
  - vibe_math_list_agents / vibe_math_message_agent / vibe_math_interrupt_agent — inspect / steer / interrupt subagents.
36
+ - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib — Lean formal
37
+ verification (execute / archive / list the reuse library). The scheduler's child agents
38
+ use them too; they work in every mode.
36
39
 
37
40
  A /vibe slash command mirrors the main controls. Data lives under {{cwd}}/VibeMath/Projects/<project>/
38
41
  (qs/qs.json, Propos/<分类>_Propos.json, Reliable/, Verified/, Verification_logs/, Progress_Logs/, VibeMath_State/)
@@ -53,6 +56,22 @@
53
56
  with its proofs/refutations transferred into the solution list (verification results sync back to the
54
57
  source proposition); a solver-reported sub-question q_sub registers THREE objects — the q_sub problem,
55
58
  the temporary-assumption proposition p_{q-tmp}, and the problem "判断下述命题是否成立:p_{q-tmp}".
59
+
60
+ LEAN FORMAL VERIFICATION (formalVerify, a tunable parameter):
61
+ - 'off' (default, no extra requirement) | 'encourage' (solver/verifier agents decide by
62
+ implementation difficulty whether to formalize in Lean; once a Lean run passes, the review
63
+ subject becomes FIDELITY — do the Lean definitions/objects/conditions/assumptions/conclusion
64
+ match the proposition as stated) | 'require' (same, plus a gate: a true/false verdict is
65
+ recorded as 未定论 with reason formal-required until the object is Lean-passed or carries an
66
+ explicit, reasoned blocker record; the scheduler is never wedged by it).
67
+ - Paths: work file Formal/<id>.lean; archived proof Verified/Lean/<id>.lean; reusable
68
+ definitions VibeMath/Formal/Lib/; proved lemmas VibeMath/Formal/Proved/.
69
+ - The toolchain knobs leanCommand / leanArgs / leanTimeoutMs are tunable as well
70
+ (e.g. leanCommand='lake' with leanArgs=['env','lean']); a missing Lean binary is
71
+ reported as LEAN_NOT_FOUND and still lets the code be written and archived.
72
+ - vibe_math_status / vibe_math_report show the mode, the per-object formal status and the
73
+ formalization TODO (Formal/TODO.md). The framework never installs Lean and never judges
74
+ fidelity for you.
56
75
  suffix: Your working directory is {{cwd}}.
57
76
  text: |-
58
77
  You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
@@ -72,11 +91,14 @@
72
91
  - vibe_math_status / vibe_math_report — read scheduler status / full progress report (report also writes Progress_Logs/report.json).
73
92
  - vibe_math_pause / vibe_math_abort — pause / abort (abort interrupts all children).
74
93
  - vibe_math_set_mode {mode: manual|auto} — switch manual / auto control.
75
- - vibe_math_set_params {...} — tune any parameter (see vibe_math_setup for the full schema; e.g. reportMode file|push|both, promoteValueThreshold, verdictMode flat|forced).
94
+ - vibe_math_set_params {...} — tune any parameter (see vibe_math_setup for the full schema; e.g. reportMode file|push|both, promoteValueThreshold, verdictMode flat|forced, formalVerify off|encourage|require).
76
95
  - vibe_math_setup / vibe_math_save_settings / vibe_math_template — guided configuration / persist defaults / generate template.
77
96
  - vibe_math_new_project / vibe_math_set_project / vibe_math_list_projects — per-project folders.
78
97
  - vibe_math_list_decisions / vibe_math_decide {id, action: approve|reject|override, verdict?} — resolve manual decisions.
79
98
  - vibe_math_list_agents / vibe_math_message_agent / vibe_math_interrupt_agent — inspect / steer / interrupt subagents.
99
+ - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib — Lean formal
100
+ verification (execute / archive / list the reuse library). The scheduler's child agents
101
+ use them too; they work in every mode.
80
102
 
81
103
  A /vibe slash command mirrors the main controls. Data lives under {{cwd}}/VibeMath/Projects/<project>/
82
104
  (qs/qs.json, Propos/<分类>_Propos.json, Reliable/, Verified/, Verification_logs/, Progress_Logs/, VibeMath_State/)
@@ -98,6 +120,22 @@
98
120
  source proposition); a solver-reported sub-question q_sub registers THREE objects — the q_sub problem,
99
121
  the temporary-assumption proposition p_{q-tmp}, and the problem "判断下述命题是否成立:p_{q-tmp}".
100
122
 
123
+ LEAN FORMAL VERIFICATION (formalVerify, a tunable parameter):
124
+ - 'off' (default, no extra requirement) | 'encourage' (solver/verifier agents decide by
125
+ implementation difficulty whether to formalize in Lean; once a Lean run passes, the review
126
+ subject becomes FIDELITY — do the Lean definitions/objects/conditions/assumptions/conclusion
127
+ match the proposition as stated) | 'require' (same, plus a gate: a true/false verdict is
128
+ recorded as 未定论 with reason formal-required until the object is Lean-passed or carries an
129
+ explicit, reasoned blocker record; the scheduler is never wedged by it).
130
+ - Paths: work file Formal/<id>.lean; archived proof Verified/Lean/<id>.lean; reusable
131
+ definitions VibeMath/Formal/Lib/; proved lemmas VibeMath/Formal/Proved/.
132
+ - The toolchain knobs leanCommand / leanArgs / leanTimeoutMs are tunable as well
133
+ (e.g. leanCommand='lake' with leanArgs=['env','lean']); a missing Lean binary is
134
+ reported as LEAN_NOT_FOUND and still lets the code be written and archived.
135
+ - vibe_math_status / vibe_math_report show the mode, the per-object formal status and the
136
+ formalization TODO (Formal/TODO.md). The framework never installs Lean and never judges
137
+ fidelity for you.
138
+
101
139
  - id: agent-instructions
102
140
  name: '@deepseek-ai/dsh-agent-instructions'
103
141
  config: