dsh-vibe-math 2.3.0 → 2.3.1

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 (30) hide show
  1. package/AUDIT-CHECKLIST.md +33 -3
  2. package/README.md +21 -0
  3. package/RELEASE-NOTES-2.3.1.md +134 -0
  4. package/audit-formal-sensitivity.mjs +125 -39
  5. package/audit-v5-integrity.mjs +5 -3
  6. package/docs/formal-verification.md +92 -12
  7. package/docs/test-timing.md +79 -0
  8. package/formal-verify-v2.test.mjs +286 -7
  9. package/formal-verify-v3.test.mjs +215 -8
  10. package/formal-verify-v4.test.mjs +282 -3
  11. package/formal-verify-v5.test.mjs +72 -0
  12. package/package.json +9 -2
  13. package/prompt-corpus-v2/formal-verify-v2.json +394 -0
  14. package/prompt-corpus-v2/formal-verify-v2.md +4250 -0
  15. package/prompt-corpus-v3/formal-verify-v3.json +159 -57
  16. package/prompt-corpus-v3/formal-verify-v3.md +1302 -285
  17. package/prompt-corpus-v4/formal-verify-v4.json +84 -0
  18. package/prompt-corpus-v4/formal-verify-v4.md +255 -0
  19. package/prompt-corpus-v5/prompt-corpus-v5.json +54 -16
  20. package/prompt-corpus-v5/prompt-corpus-v5.md +378 -153
  21. package/prompt-v5-integrity.test.mjs +1158 -1085
  22. package/run-tests.mjs +99 -0
  23. package/vibe-math-v2/vibe-math-v2.js +204 -22
  24. package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +77 -4
  25. package/vibe-math-v3/vibe-math-v3.js +82 -21
  26. package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +17 -0
  27. package/vibe-math-v4/vibe-math-v4.js +114 -22
  28. package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +29 -0
  29. package/vibe-math-v5/vibe-math-v5.js +81 -22
  30. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +27 -4
@@ -25,9 +25,9 @@
25
25
  //
26
26
  // Run: node formal-verify-v4.test.mjs
27
27
  // ============================================================
28
- import { mkdtempSync, existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs'
28
+ import { mkdtempSync, existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, rmSync } from 'node:fs'
29
29
  import { tmpdir } from 'node:os'
30
- import { join, dirname, isAbsolute } from 'node:path'
30
+ import { join, dirname, isAbsolute, resolve as pathResolve } from 'node:path'
31
31
  import { pathToFileURL, fileURLToPath } from 'node:url'
32
32
 
33
33
  // V4_PLUGIN (same convention the v5 suite uses for V5_PLUGIN): point the suite at a MUTATED copy of
@@ -41,6 +41,11 @@ function pluginUrl() {
41
41
  return pathToFileURL(fileURLToPath(new URL('file:///' + s.replace(/\\/g, '/'))))
42
42
  }
43
43
  const PLUGIN = pluginUrl()
44
+ // docs §10 item 10: the Lean prompt corpus is shipped under prompt-corpus-v4/ so a HUMAN can read
45
+ // the exact text the framework sends. V4_CORPUS_DIR overrides the destination (same convention v3
46
+ // and v5 use for V3_CORPUS_DIR / V5_CORPUS_DIR).
47
+ const HERE = dirname(fileURLToPath(import.meta.url))
48
+ const CORPUS_DIR = process.env.V4_CORPUS_DIR ? pathResolve(process.env.V4_CORPUS_DIR) : join(HERE, 'prompt-corpus-v4')
44
49
 
45
50
  let passed = 0, failed = 0
46
51
  const failures = []
@@ -56,6 +61,7 @@ const readIf = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : '')
56
61
  // ===============================================================
57
62
  let toolchainAvailable = true
58
63
  const leanRuns = [] // every spawn the framework made, for cwd/argv assertions
64
+ const shellCalls = [] // every platform-shell script (mkdir at mount, Remove-Item on defect)
59
65
 
60
66
  function makeSubprocess() {
61
67
  return {
@@ -65,6 +71,24 @@ function makeSubprocess() {
65
71
  return String(cmd)
66
72
  },
67
73
  spawn(spec) {
74
+ // The preset drives the platform shell through this SAME `subprocess` service (runShell:
75
+ // powershell / /bin/sh) for mkdir at mount, and — since docs §4.1 — for Remove-Item when a
76
+ // `defect` retracts an archived proof. Handle it here so that retraction is a REAL filesystem
77
+ // deletion, exactly as the real host performs it; otherwise "the proof file is gone" could
78
+ // only be asserted against a mock's bookkeeping.
79
+ const argv0 = String((spec.argv && spec.argv[0]) || '')
80
+ if (/powershell|cmd\.exe|\/bin\/sh|(^|\/)sh$/i.test(argv0)) {
81
+ const script = String(spec.argv[spec.argv.length - 1] || '')
82
+ shellCalls.push(script)
83
+ const m = script.match(/-LiteralPath\s+'((?:[^']|'')*)'/)
84
+ if (/Remove-Item/.test(script) && m) rmSync(m[1].replace(/''/g, "'"), { force: true })
85
+ if (/^rm -f /.test(script)) for (const q of script.slice(6).match(/'[^']*'/g) || []) rmSync(q.slice(1, -1), { force: true })
86
+ return {
87
+ done: Promise.resolve({ exitCode: 0, signal: null }),
88
+ collected: { stdout: { readFrom: () => ({ text: '', nextOffset: 0, lossy: false }) }, stderr: { readFrom: () => ({ text: '', nextOffset: 0, lossy: false }) } },
89
+ terminate() {},
90
+ }
91
+ }
68
92
  // The plugin passes an ABSOLUTE path as the last argv element, so resolve it directly; fall
69
93
  // back to scanning the argv for an existing .lean file (mirrors leanAbsPath).
70
94
  const last = spec.argv[spec.argv.length - 1]
@@ -420,7 +444,13 @@ assert(!existsSync(join(D.projectRoot, 'Verified', 'Lean', 'p-red.lean')), '★
420
444
  assert(/你不需要重新检查推导/.test(prompt), '★ it tells voters NOT to re-derive')
421
445
  assert(/忠实性审查/.test(prompt), '★ it tells voters the review subject is now fidelity')
422
446
  assert(/定义 \/ 对象 \/ 条件 \/ 假设 \/ 结论是否与命题原文\*\*完全一致\*\*/.test(prompt), 'it enumerates exactly what fidelity means')
423
- assert(/因此请把 verdict 用在\*\*忠实性\*\*上/.test(prompt), 'the shift is made explicit in the verdict instruction')
447
+ assert(/一致 → verdict = 1/.test(prompt), "the fidelity instruction uses v4's REAL reply field name (`verdict`)")
448
+ assert(/发现任何偏差,不要投 0/.test(prompt), '★ it forbids expressing a fidelity deviation as "0 / false"')
449
+ assert(/偏差只说明\*\*形式化不合格\*\*,不代表命题为假/.test(prompt), '★ it states WHY: a defect is a failed formalization, not a refutation')
450
+ assert(/formal:\{decision:'defect', note:'<具体偏差>'\}/.test(prompt), '★ it hands the voter the exact `defect` reply contract')
451
+ assert(/降级为 attempted、删除归档证明、写入形式化待办/.test(prompt), 'it says what the framework WILL do with a defect (downgrade + delete + TODO)')
452
+ assert(/只有当你\*\*独立于这份 Lean 代码\*\*也能确定命题为假时,才投 0/.test(prompt), 'only an INDEPENDENT refutation may be expressed as 0')
453
+ assert(!/偏离 → 0/.test(prompt), '★ the old "a deviation ⇒ 0" wording is gone (it would fabricate a negative conclusion)')
424
454
  assert(/Verified\/Lean\/p-proof\.lean/.test(prompt), 'it points at the archived proof')
425
455
  // the SAME object's work round must not inherit the fidelity framing (that is a voting
426
456
  // instruction), but it does carry the standing formalization line.
@@ -595,6 +625,255 @@ assert(/已通过:.*p-gate/.test(reportE.formalReport), 'the human report list
595
625
  assert(/已记录阻塞:.*p-blocked-ok/.test(reportE.formalReport), 'the human report lists blocked objects')
596
626
  assert(/形式化待办:.*p-gate-false/.test(reportE.formalReport), 'the human report lists the formalization TODO')
597
627
 
628
+ // ===============================================================
629
+ // 12. the fidelity rule + the prompt hard requirements (docs §6, §10 items 9/11)
630
+ // The v2 lesson (docs §10 item 8) is why every claim here is paired with a BEHAVIOURAL
631
+ // assertion in §13/§14: wording alone guards nothing.
632
+ // ===============================================================
633
+ section('12 the injected text states the fidelity rule and never abbreviates a tool name')
634
+ const G = await establish()
635
+ await G.callTool('vibe_v4_set', { formalVerify: 'encourage' })
636
+ {
637
+ const enc = await G.prompts('verify', 'r-1', { target: 'p-text', stage: 'independent' })
638
+ assert(/【Lean 形式化验证(鼓励模式)】/.test(enc), 'the encourage voting prompt keeps its header')
639
+ assert(/实现难度/.test(enc), 'it still asks for the implementation-difficulty judgement')
640
+ assert(/工具:vibe_v4_lean_run(执行)· vibe_v4_lean_archive(归档)· vibe_v4_lean_lib(查已有可复用库)/.test(enc), 'it names all three tools in FULL')
641
+ assert(/归档可复用定义\/引理前先跑通(vibe_v4_lean_archive run=true 或先 vibe_v4_lean_run);跑不通不要入库。/.test(enc), '★ a reusable definition/lemma must be RUN GREEN before it is archived')
642
+ assert(/宿主没有 Lean 工具链(LEAN_NOT_FOUND)时:把代码写下来归档,并在回执的 note 里写明"宿主无 Lean 工具链"/.test(enc), '★ the missing-toolchain path is written out (archive the code, record it as an explicit blocker)')
643
+ assert(/一旦 Lean 通过,你唯一需要确认的就是忠实性/.test(enc), 'a green Lean run still shrinks the open question to fidelity')
644
+ assert(/decision='blocked' 时必须写明 note/.test(enc), 'the encourage opt-out documents the mandatory note')
645
+ assert(!/偏离 → 0/.test(enc) && !/发现任何偏离/.test(enc), '★ no "deviation ⇒ 0" instruction anywhere in the encourage block')
646
+ await G.callTool('vibe_v4_set', { formalVerify: 'require' })
647
+ const req = await G.prompts('verify', 'r-1', { target: 'p-text', stage: 'independent' })
648
+ assert(/【Lean 形式化验证(强制模式)】/.test(req), 'the require voting prompt says 强制模式')
649
+ assert(/必须产出 Lean 形式化/.test(req) && /必须\*\*给出显式的阻塞原因/.test(req), "'require' states the formalization OR an explicit blocker is mandatory")
650
+ assert(/本次裁定不会生效/.test(req) && /进入「形式化待办」/.test(req), 'it warns the verdict is withheld as 未定论 (formal-required)')
651
+ assert(!/可以不做/.test(req), "'require' does NOT offer the encourage-mode opt-out")
652
+ assert(/归档可复用定义\/引理前先跑通/.test(req) && /宿主没有 Lean 工具链/.test(req), 'the run-before-archive and toolchain rules are in the require text as well')
653
+ assert(/\*\*本模式要求\*\*/.test(req), 'the require bullet replaces the encourage opt-out in place')
654
+ // normal / heartbeat / post-compact recap all carry the standing work line; the two that ARE a
655
+ // reply contract also document the `defect` decision (coreRules is a recap prefix, not a contract)
656
+ for (const [label, text] of [['normal', await G.prompts('normal', 'r-1')], ['heartbeat', await G.prompts('heartbeat', 'r-1')], ['coreRules', await G.prompts('coreRules', 'r-1')]]) {
657
+ assert(/【顺手形式化(强制)】/.test(text), label + ': the standing formalization line uses the require wording')
658
+ assert(/归档前先跑通(vibe_v4_lean_run 或 run=true);跑不通的定义不要进可复用库。/.test(text), label + ': ★ it requires a green run before archiving a reusable definition')
659
+ if (label !== 'coreRules') assert(/"decision":"used\|blocked\|defect"/.test(text), label + ': the reply contract documents the `defect` decision')
660
+ }
661
+ assert(/"decision":"used\|blocked\|defect"/.test(req) && /具体偏差/.test(req), '★ the voting reply contract documents decision=defect and its note')
662
+ }
663
+ {
664
+ // ---- the sweep: EVERY agent-facing string must spell the three tools in full (docs §6-1) ----
665
+ const scanned = []
666
+ const addText = (label, t) => { if (typeof t === 'string' && t) scanned.push({ label, text: t }) }
667
+ addText('captured run prompts', G.allPrompts())
668
+ for (const which of ['brainstorm', 'normal', 'heartbeat', 'coreRules']) addText(which, await G.prompts(which, 'r-1'))
669
+ for (const t of ['p-text', 'p-corpus']) for (const st of ['independent', 'debate']) addText('verify:' + t + ':' + st, await G.prompts('verify', 'r-1', { target: t, stage: st }))
670
+ // tool hints are injected text too (docs §6 hard requirement 1 names them explicitly)
671
+ const r1G = G.resAgent(G.childOf('r-1'))
672
+ const hintGreen = await G.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-hint', content: 'theorem p_hint : 1 = 1 := rfl\n' }, r1G)
673
+ assert(hintGreen.ok === true && hintGreen.passed === true, 'precondition: a green file exists for the hint sweep')
674
+ const hintRed = await G.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-hint-red', content: 'theorem p_hint_red : 1 = 2 := by sorry\n' }, r1G)
675
+ assert(hintRed.ok === true && hintRed.passed === false, 'precondition: a red file exists for the hint sweep')
676
+ addText('lean_run hint (green)', (await G.callTool('vibe_v4_lean_run', { file: 'Formal/p-hint.lean' }, r1G)).hint)
677
+ addText('lean_run hint (red)', (await G.callTool('vibe_v4_lean_run', { file: 'Formal/p-hint-red.lean' }, r1G)).hint)
678
+ addText('lean_lib hint', (await G.callTool('vibe_v4_lean_lib', {}, r1G)).hint)
679
+ toolchainAvailable = false
680
+ addText('LEAN_NOT_FOUND message', (await G.callTool('vibe_v4_lean_run', { file: 'Formal/p-hint.lean' }, r1G)).message)
681
+ toolchainAvailable = true
682
+ for (const t of G.toolRegs.filter((x) => /lean/.test(x.name))) addText('tool description ' + t.name, t.description)
683
+ const bare = [/(^|[^a-z_])lean_run/, /(^|[^a-z_])lean_archive/, /(^|[^a-z_])lean_lib/]
684
+ const offenders = []
685
+ for (const s of scanned) for (const re of bare) if (re.test(s.text)) offenders.push(s.label + ' :: ' + re.source)
686
+ assert(offenders.length === 0, '★ no injected text (prompt, tool hint or tool description) uses a bare tool abbreviation (' + offenders.slice(0, 3).join(' | ') + ')')
687
+ assert(scanned.length >= 15, 'the sweep really covered the injected-text surface (' + scanned.length + ' texts)')
688
+ }
689
+
690
+ // ===============================================================
691
+ // 13. the `defect` reply channel (docs §4.1 / §10 item 8) — BEHAVIOURAL, not wording
692
+ // ===============================================================
693
+ section('13 the `defect` reply withdraws a passing proof (spec §4.1)')
694
+ await G.callTool('vibe_v4_set', { formalVerify: 'encourage' })
695
+ const activityOf = (h) => { try { return JSON.parse(readIf(join(h.projectRoot, 'State', 'session.json')) || '{}').activityLog || [] } catch (e) { return [] } }
696
+ const g1 = G.resAgent(G.childOf('r-1'))
697
+ {
698
+ const arc = await G.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-defect', content: 'theorem p_defect : 2 + 2 = 4 := by decide\n' }, g1)
699
+ assert(arc.passed === true && existsSync(join(G.projectRoot, 'Verified', 'Lean', 'p-defect.lean')), 'precondition: p-defect has a green archived proof')
700
+ const w = await workWake(G, 'r-1')
701
+ G.fireEnd(w.childId, { summary: '我逐条核对了 Lean 代码,发现偏差。', formal: { target: 'p-defect', decision: 'defect', note: 'Lean 里的条件比命题弱:只证了 n ≥ 1 的情形' }, contextPct: 20 })
702
+ await sleep(90)
703
+ const st = await G.callTool('vibe_v4_status', {})
704
+ const rec = st.formal.objects.find((o) => o.target === 'p-defect')
705
+ assert(rec && rec.status === 'attempted', '★ a `formal.decision=defect` reply downgrades the object to attempted (never a refutation)')
706
+ assert(rec && rec.proof === '', '★ the archived proof is cleared from the record')
707
+ assert(rec && rec.note === 'Lean 里的条件比命题弱:只证了 n ≥ 1 的情形', 'the concrete deviation is stored as the record note')
708
+ assert(!existsSync(join(G.projectRoot, 'Verified', 'Lean', 'p-defect.lean')), '★ the archived proof file is DELETED (the formalization is 不合格 — the proposition is NOT false)')
709
+ assert(existsSync(join(G.projectRoot, 'Formal', 'p-defect.lean')), 'the WORKING file is kept — the code is not lost, only its "passed" claim')
710
+ assert(st.formal.passed.indexOf('p-defect') === -1, 'the object is no longer reported as Lean-passed')
711
+ assert(shellCalls.some((c) => /Remove-Item|^rm -f/.test(c) && /p-defect\.lean/.test(c)), 'the withdrawal really went through the platform shell (the fs service has no delete)')
712
+ const todo = readIf(join(G.projectRoot, 'Formal', 'TODO.md'))
713
+ assert(/p-defect/.test(todo) && /formal-defect/.test(todo), '★ Formal/TODO.md lists the object with the DEFECT reason')
714
+ assert(/只证了 n ≥ 1 的情形/.test(todo), '★ the concrete deviation reaches the human-readable TODO')
715
+ const idx = readIf(join(G.projectRoot, 'Formal', 'Index.md'))
716
+ assert(/\| p-defect \| attempted \|/.test(idx), 'Formal/Index.md downgrades the object to attempted')
717
+ assert(/只证了 n ≥ 1 的情形/.test(idx), 'the deviation is the record note in the index')
718
+ assert(activityOf(G).some((e) => /忠实性缺陷/.test(e.detail) && /p-defect/.test(e.detail)), '★ the retraction is announced in the activity log')
719
+ }
720
+ {
721
+ const arc2 = await G.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-defect2', content: 'theorem p_defect2 : 3 + 3 = 6 := by decide\n' }, g1)
722
+ assert(arc2.passed === true, 'precondition: p-defect2 is Lean-passed')
723
+ const w2 = await workWake(G, 'r-2')
724
+ G.fireEnd(w2.childId, { summary: '有偏差,但没写清是什么。', formal: { target: 'p-defect2', decision: 'defect' }, contextPct: 20 })
725
+ await sleep(90)
726
+ const st2 = await G.callTool('vibe_v4_status', {})
727
+ const rec2 = st2.formal.objects.find((o) => o.target === 'p-defect2')
728
+ assert(rec2 && rec2.status === 'passed' && rec2.proof === 'Verified/Lean/p-defect2.lean', '★ a `defect` reply WITHOUT a note is REJECTED — the record is untouched')
729
+ assert(existsSync(join(G.projectRoot, 'Verified', 'Lean', 'p-defect2.lean')), '★ and the archived proof is NOT withdrawn')
730
+ assert(activityOf(G).some((e) => /defect/.test(e.detail) && /V4_INVALID_ARGUMENT/.test(e.detail)), '★ the rejection carries the preset error code (V4_INVALID_ARGUMENT), not a silent drop')
731
+ assert((await G.callTool('vibe_v4_status', {})).ok === true, 'the refusal did not crash the run')
732
+ }
733
+ {
734
+ const blk = await G.callTool('vibe_v4_lean_archive', { kind: 'blocked', target: 'p-defect3', note: '前置知识未形式化,本轮不做' }, g1)
735
+ assert(blk.ok === true && blk.status === 'blocked', 'precondition: p-defect3 carries a reasoned blocker record')
736
+ const w3 = await workWake(G, 'r-1')
737
+ G.fireEnd(w3.childId, { summary: '复核后发现归档的形式化换了对象。', formal: { target: 'p-defect3', decision: 'defect', note: '归档的形式化证的是特例,换了对象' }, contextPct: 20 })
738
+ await sleep(90)
739
+ const st3 = await G.callTool('vibe_v4_status', {})
740
+ const rec3 = st3.formal.objects.find((o) => o.target === 'p-defect3')
741
+ assert(rec3 && rec3.status === 'attempted', '★ `defect` downgrades even a `blocked` record (spec §4.1: ALWAYS downgrade)')
742
+ assert(st3.formal.blocked.indexOf('p-defect3') === -1, '★ a bad formalization may not stay in the gate-passing `blocked` state')
743
+ }
744
+
745
+ // ===============================================================
746
+ // 14. `require` after a defect: the retraction closes the gate (docs §4.1-3 / §10 item 9)
747
+ // ===============================================================
748
+ section("14 'require' withholds the verdict after a defect, even on a unanimous 1")
749
+ const H = await establish()
750
+ await H.callTool('vibe_v4_set', { formalVerify: 'require' })
751
+ {
752
+ const h1 = H.resAgent(H.childOf('r-1'))
753
+ const arc = await H.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-defect-req', content: 'theorem p_defect_req : 4 * 1 ^ 2 - 2 = (1:Nat) ^ 2 := by decide\n' }, h1)
754
+ assert(arc.passed === true, 'precondition: p-defect-req is Lean-passed')
755
+ const before = await H.prompts('verify', 'r-1', { target: 'p-defect-req', stage: 'independent' })
756
+ assert(/不要投 0/.test(before) && /formal:\{decision:'defect'/.test(before), 'the voter is told (before voting) how to report a fidelity defect')
757
+ await H.callTool('vibe_v4_record_proposition', { id: 'p-defect-req', title: '缺陷不是证伪', statement: '形式化写窄了不代表命题为假', prob: 0.9, value: 0.6, motivation: 'm' }, h1)
758
+ const n0 = H.followups.length
759
+ await H.callTool('vibe_v4_message', { to: 'r-1', content: '请处理本轮工作。' })
760
+ for (let i = 0; i < 300 && H.followups.length === n0; i++) await sleep(10)
761
+ // r-1 reports the defect AND votes 1 in the SAME reply; r-2 votes 1. The framework must NOT take
762
+ // the unanimous 1: the formalization was just retracted, so the verdict has to be deferred.
763
+ const replyFor = (pt, rId) => (/团队验证/.test(pt)
764
+ ? (rId === 'r-1'
765
+ ? { vote: { verdict: 1, reason: '我发现形式化写窄了,但独立看命题仍为真' }, formal: { target: 'p-defect-req', decision: 'defect', note: 'Lean 只证了 x=1 的特例,命题要求所有整数 x' } }
766
+ : { vote: { verdict: 1, reason: '独立复核为真' } })
767
+ : { summary: '提议验证 p-defect-req。', solved: false, propose_verify: 'p-defect-req', contextPct: 20 })
768
+ for (let i = 0; i < 4; i++) { await drive(H, replyFor, verifySettled(H)); if (await verifySettled(H)()) break }
769
+ const st = await H.callTool('vibe_v4_status', {})
770
+ assert(st.verifyInProgress === false && st.pendingVerify === null, 'the verification actually settled (so the next assertion is falsifiable)')
771
+ assert(!existsSync(join(H.projectRoot, 'Verified', '命题', 'p-defect-req.md')), '★ a defect in `require` mode writes NO Verified card, even with a unanimous 1')
772
+ assert(st.formal.todo.indexOf('p-defect-req') !== -1, '★ the object stays 未定论 on the formalization TODO')
773
+ const rec = st.formal.objects.find((o) => o.target === 'p-defect-req')
774
+ assert(rec && rec.status === 'attempted' && rec.proof === '', 'the formal record is the retracted one (attempted, no proof)')
775
+ assert(rec && /只证了 x=1 的特例/.test(rec.note || ''), "the record's note is the reported deviation")
776
+ assert(!existsSync(join(H.projectRoot, 'Verified', 'Lean', 'p-defect-req.lean')), '★ the archived proof was withdrawn as part of the retraction')
777
+ assert(!/已验证·真/.test(readIf(join(H.projectRoot, 'Propos', 'r-1', 'p-defect-req.md'))), 'the source card was NOT rewritten to 已验证·真 (the conclusion was withheld, not taken)')
778
+ assert(!/已验证·真/.test(readIf(join(H.projectRoot, 'Shared', 'debates', 'p-defect-req.md'))), 'the debate record does not claim a 真 conclusion')
779
+ const todo = readIf(join(H.projectRoot, 'Formal', 'TODO.md'))
780
+ assert(/p-defect-req/.test(todo) && /formal-defect/.test(todo), '★ the TODO keeps the DEFECT reason, not merely formal-required')
781
+ assert(/只证了 x=1 的特例/.test(todo), 'the concrete deviation is what a human reads in the TODO')
782
+ }
783
+
784
+ // ===============================================================
785
+ // 15. the shipped Lean prompt corpus (docs §10 item 10) — a HUMAN must be able to re-read the
786
+ // exact text, not just the assertions about it.
787
+ // ===============================================================
788
+ section('15 the captured Lean prompt corpus is written for human review')
789
+ const K = await establish()
790
+ {
791
+ const vibe = K.vibeRoot
792
+ // Normalise BOTH slash forms, and the VibeMath root FIRST: it sits INSIDE the workspace, so
793
+ // replacing the workspace first would leave `<WS>/VibeMath` instead of `<VIBEMATH>`.
794
+ const scrub = (s) => String(s == null ? '' : s)
795
+ .split(vibe).join('<VIBEMATH>').split(vibe.replace(/\\/g, '/')).join('<VIBEMATH>')
796
+ .split(K.WS).join('<WS>').split(K.WS.replace(/\\/g, '/')).join('<WS>')
797
+ const corpus = []
798
+ const add = (kind, label, prompt) => corpus.push({ kind, label, prompt: scrub(prompt) })
799
+ // (a) off: a TRUE no-op must be visible in the corpus, not merely asserted
800
+ add('verify', 'off/verify', await K.prompts('verify', 'r-1', { target: 'p-corpus', stage: 'independent' }))
801
+ add('work', 'off/normal', await K.prompts('normal', 'r-1'))
802
+ // (b) encourage
803
+ await K.callTool('vibe_v4_set', { formalVerify: 'encourage' })
804
+ add('verify', 'encourage/verify', await K.prompts('verify', 'r-1', { target: 'p-corpus', stage: 'independent' }))
805
+ const encNormal = await K.prompts('normal', 'r-1')
806
+ add('work', 'encourage/normal', encNormal)
807
+ add('work', 'encourage/heartbeat', await K.prompts('heartbeat', 'r-1'))
808
+ add('work', 'encourage/coreRules', await K.prompts('coreRules', 'r-1'))
809
+ // (c) require
810
+ await K.callTool('vibe_v4_set', { formalVerify: 'require' })
811
+ add('verify', 'require/verify', await K.prompts('verify', 'r-1', { target: 'p-corpus', stage: 'independent' }))
812
+ add('verify', 'require/verify/debate', await K.prompts('verify', 'r-1', { target: 'p-corpus', stage: 'debate' }))
813
+ add('work', 'require/normal', await K.prompts('normal', 'r-1'))
814
+ // (d) a PASSED object: the review subject has changed to fidelity
815
+ const k1 = K.resAgent(K.childOf('r-1'))
816
+ await K.callTool('vibe_v4_lean_archive', { kind: 'proof', target: 'p-corpus-passed', content: 'theorem p_corpus_passed : 1 + 1 = 2 := by decide\n' }, k1)
817
+ const fid = await K.prompts('verify', 'r-1', { target: 'p-corpus-passed', stage: 'independent' })
818
+ add('verify', 'passed/fidelity', fid)
819
+ // (e) a BLOCKED object
820
+ await K.callTool('vibe_v4_lean_archive', { kind: 'blocked', target: 'p-corpus-blocked', note: '需要未形式化的解析数论框架' }, k1)
821
+ add('verify', 'blocked/verify', await K.prompts('verify', 'r-1', { target: 'p-corpus-blocked', stage: 'independent' }))
822
+ // (f) the `formal` reply contract line itself (the field whose parsing §13/§14 prove)
823
+ const contractLine = (t) => { const m = String(t).match(/"formal":\{[^\n]*\}\}/); return m ? m[0] : '' }
824
+ add('contract', 'formal reply contract (voting prompt)', contractLine(fid))
825
+ add('contract', 'formal reply contract (work prompt)', contractLine(encNormal))
826
+ // (g) a REAL delivered work wake (proves the builder is the one actually used to address a resident)
827
+ const w = await workWake(K, 'r-2')
828
+ add('work', 'require/real work wake', promptOf(w))
829
+ K.fireEnd(w.childId, { summary: '继续推进。', solved: false, contextPct: 20 })
830
+ await sleep(40)
831
+ // (h) the tool hints are injected text too (docs §6 hard requirement 1)
832
+ add('hint', 'lean_run hint (green)', (await K.callTool('vibe_v4_lean_run', { file: 'Formal/p-corpus-passed.lean' }, k1)).hint)
833
+ add('hint', 'lean_lib hint', (await K.callTool('vibe_v4_lean_lib', {}, k1)).hint)
834
+
835
+ // The corpus is REGENERATED on every run. Dumping it while V4_PLUGIN points at a MUTATED copy
836
+ // would let a sensitivity probe overwrite the SHIPPED corpus with mutated text (the probe's job
837
+ // is to run this suite against a broken plugin), so the dump is skipped then — unless the probe
838
+ // explicitly redirects it with V4_CORPUS_DIR. The IN-MEMORY corpus assertions below still run, so
839
+ // nothing is weakened.
840
+ const writeCorpus = !process.env.V4_PLUGIN || !!process.env.V4_CORPUS_DIR
841
+ if (writeCorpus) {
842
+ mkdirSync(CORPUS_DIR, { recursive: true })
843
+ writeFileSync(join(CORPUS_DIR, 'formal-verify-v4.json'), JSON.stringify({ entries: corpus }, null, 2), 'utf8')
844
+ const md = ['# V4 形式化验证交互语料(prompt corpus)', '',
845
+ '> 由 `formal-verify-v4.test.mjs` 落盘:非 `off` 模式下常驻**真正会读到**的 Lean 提示词原文',
846
+ '> (`vibe_v4_prompts` 的只读回显 + 一条真实投递的工作轮 + 工具 `hint`)。',
847
+ '> 工作区路径归一化为 `<WS>`,VibeMath 根归一化为 `<VIBEMATH>`:确定、可 diff、不含任何本机路径。', '',
848
+ '> 覆盖:`off`(无 Lean 文本)、`encourage`、**`require`**、对象 `passed` 后的**忠实性分支**、',
849
+ '> `blocked` 分支、平时工作轮的「顺手形式化」,以及回执契约里的 `formal` 字段。', '']
850
+ for (let i = 0; i < corpus.length; i++) {
851
+ const c = corpus[i]
852
+ md.push('## [' + i + '] ' + c.kind + ' · ' + c.label)
853
+ md.push('')
854
+ md.push('```text')
855
+ md.push(c.prompt)
856
+ md.push('```')
857
+ md.push('')
858
+ }
859
+ writeFileSync(join(CORPUS_DIR, 'formal-verify-v4.md'), md.join('\n'), 'utf8')
860
+ assert(existsSync(join(CORPUS_DIR, 'formal-verify-v4.json')) && existsSync(join(CORPUS_DIR, 'formal-verify-v4.md')), 'the prompt corpus was written (JSON + Markdown)')
861
+ }
862
+ assert(corpus.length >= 14, 'the corpus covers the whole Lean prompt surface (' + corpus.length + ' prompts)')
863
+ const byLabel = (l) => corpus.find((c) => c.label === l)
864
+ assert(!!byLabel('off/verify') && !/Lean/.test(byLabel('off/verify').prompt) && !/Lean/.test(byLabel('off/normal').prompt), '★ the corpus keeps the off-mode entries and they contain NO Lean text')
865
+ assert(/【Lean 形式化验证(鼓励模式)】/.test(byLabel('encourage/verify').prompt), 'the corpus carries the encourage voting prompt')
866
+ assert(/【Lean 形式化验证(强制模式)】/.test(byLabel('require/verify').prompt), '★ the corpus carries the REQUIRE voting prompt')
867
+ assert(/不要投 0/.test(byLabel('passed/fidelity').prompt), '★ the corpus carries the passed/fidelity branch')
868
+ assert(/【顺手形式化(强制)】/.test(byLabel('require/normal').prompt), 'the corpus carries the ordinary work-round line')
869
+ assert(/"decision":"used\|blocked\|defect"/.test(byLabel('formal reply contract (voting prompt)').prompt), '★ the corpus carries the `formal` reply contract line with decision=defect')
870
+ assert(/【顺手形式化/.test(byLabel('require/real work wake').prompt), 'the corpus also keeps a prompt the framework REALLY delivered')
871
+ const joined = corpus.map((c) => c.prompt).join('\n')
872
+ assert(joined.indexOf(K.WS) === -1 && joined.indexOf(K.WS.replace(/\\/g, '/')) === -1 && joined.indexOf(vibe) === -1 && joined.indexOf(vibe.replace(/\\/g, '/')) === -1, '★ every captured prompt normalises <WS> and <VIBEMATH> (diffable, no machine paths)')
873
+ assert(!/\[object Object\]|\bNaN\b|:\s*undefined|["']undefined["']|undefined\s*[,}\]]/.test(joined), 'no captured prompt contains placeholder garbage')
874
+ assert(corpus.every((c) => c.prompt && c.prompt.length > 20), 'every corpus entry carries real prompt text')
875
+ }
876
+
598
877
  // ===============================================================
599
878
  console.log('')
600
879
  console.log('passed=' + passed + ' failed=' + failed)
@@ -262,6 +262,9 @@ async function wakeAndReply(root, memberId, reply, fromMember) {
262
262
  const instRootOf = (root) => join(WS, 'VibeMath', 'Projects', 'default', 'Institutes', 'institute')
263
263
  const vibeRoot = join(WS, 'VibeMath')
264
264
  const readIf = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : '')
265
+ // Every Lean tool mention in AGENT-FACING text must be the registered name (vibe_v5_lean_*).
266
+ // An abbreviated `lean_archive` is not a tool: an agent that copies it calls nothing.
267
+ const noBareLeanTool = (t) => !/(^|[^a-z_])lean_(run|archive|lib)/.test(String(t || ''))
265
268
 
266
269
  // ===============================================================
267
270
  console.log('-- V5 Lean formal verification --')
@@ -318,6 +321,8 @@ await callTool('vibe_v5_set', { formalVerify: 'encourage' }, RC)
318
321
  assert(/\[形式化\] 鼓励 Lean/.test(work), "the state block gains a [形式化] 鼓励 Lean line")
319
322
  assert(/【顺手形式化(鼓励)】/.test(work), 'the work round tells members to formalize reusable objects as they go')
320
323
  assert(/vibe_v5_lean_archive kind='def'/.test(work), 'the work round points at the archive tool for reusable definitions')
324
+ assert(noBareLeanTool(work), 'no abbreviated tool name appears in the injected work-round prompt')
325
+ assert(/归档前先跑通/.test(work), 'the work-round prompt requires a green run before archiving into the reuse library')
321
326
  }
322
327
  await callTool('vibe_v5_record_proposition', { id: 'p-enc', statement: '鼓励模式下的忠实性审查', value: 0.6, motive: 'm', p: 0.8 }, childAgent(childOf(RC, 'r-1')))
323
328
  await callTool('vibe_v5_propose_verify', { target: 'p-enc', kind: 'proposition', reason: '先看看提示词' }, childAgent(childOf(RC, 'r-1')))
@@ -326,9 +331,13 @@ await settle(); delivered.length = 0; await drainWakes(3, RC)
326
331
  const vp = delivered.filter(d => d.rootId === RC.id).map(d => d.prompt).join('\n')
327
332
  assert(/【Lean 形式化验证(鼓励模式)】/.test(vp), 'the voting prompt explains the Lean mode')
328
333
  assert(/一旦 Lean 通过,你唯一需要确认的就是忠实性/.test(vp), 'the voting prompt states that a passing Lean run shrinks the question to fidelity')
334
+ assert(/归档可复用定义\/引理前先跑通/.test(vp), 'the voting prompt requires a GREEN RUN before archiving a reusable definition')
335
+ assert(/LEAN_NOT_FOUND/.test(vp) && /宿主无 Lean 工具链/.test(vp), 'the voting prompt says what to do when the host has no Lean toolchain')
336
+ assert(noBareLeanTool(vp), 'no abbreviated tool name appears in the injected voting prompt')
329
337
  assert(/实现难度/.test(vp), 'the voting prompt asks for the implementation-difficulty judgement')
330
338
  assert(/可以不做,但请在回执的 formal 字段写明难度判断/.test(vp), "'encourage' explicitly allows skipping (with a recorded judgement)")
331
339
  assert(/"formal":/.test(vp), 'the reply contract documents the formal field')
340
+ assert(/"decision":"used\|blocked\|defect"/.test(vp), '★ the reply contract offers the defect decision (a faithfulness defect is recordable)')
332
341
  }
333
342
  await drainWakes(10, RC)
334
343
 
@@ -392,6 +401,11 @@ await settle(); delivered.length = 0; await drainWakes(3, RD)
392
401
  assert(/你不需要重新检查推导/.test(vp), '★ it tells voters NOT to re-derive')
393
402
  assert(/忠实性审查/.test(vp), '★ it tells voters the review subject is now fidelity')
394
403
  assert(/定义 \/ 对象 \/ 条件 \/ 假设 \/ 结论是否与命题原文\*\*完全一致\*\*/.test(vp), 'it enumerates exactly what fidelity means')
404
+ assert(/不要投 0/.test(vp), '★ a faithfulness defect must NOT be expressed as 0 (that would record 命题为假)')
405
+ assert(/形式化不合格/.test(vp), 'it names the failure a formalisation defect, not a refutation')
406
+ assert(/decision:'defect'/.test(vp), 'it names the defect reply channel')
407
+ assert(!/偏离 → 0/.test(vp), '★ the old "any deviation → 0" instruction is GONE')
408
+ assert(noBareLeanTool(vp), 'no abbreviated tool name appears in the fidelity prompt')
395
409
  }
396
410
  await drainWakes(10, RD)
397
411
 
@@ -443,6 +457,8 @@ await settle(); delivered.length = 0; await drainWakes(3, RE)
443
457
  assert(/【Lean 形式化验证(强制模式)】/.test(vp), 'the voting prompt says 强制模式')
444
458
  assert(/必须产出 Lean 形式化/.test(vp), "'require' states the formalization is mandatory")
445
459
  assert(/本次裁定不会生效/.test(vp), 'the prompt warns that the verdict will not take effect without it')
460
+ assert(/formal-required/.test(vp), 'it names the machine-readable reason code in the prompt itself')
461
+ assert(/vibe_v5_lean_archive/.test(vp) && /kind='blocked'/.test(vp), 'it gives the full tool name for the blocker route')
446
462
  }
447
463
  const gated = await voteToConclusion(RE, 'p-gate', new Map([['acad', 1], ['r-1', 1], ['r-2', 1]]))
448
464
  assert(gated.verified.indexOf('p-gate') === -1, '★ a unanimous TRUE verdict did NOT promote the object to Verified/')
@@ -519,6 +535,62 @@ assert(/已记录阻塞:.*p-blocked-ok/.test(rep.report), 'the report lists bl
519
535
  const stOff = await callTool('vibe_v5_report', {}, RA)
520
536
  assert(/未启用(`formalVerify` = off/.test(stOff.report), 'in off mode the report says the feature is not enabled')
521
537
 
538
+ // ---------- 11. a faithfulness defect withdraws the proof, it is NOT a refutation ----------
539
+ section('11 ★ a faithfulness defect withdraws the proof instead of recording 命题为假 (contract §4.1)')
540
+ const RG = makeRoot()
541
+ await foundInstitute(RG, '忠实性缺陷语义测试')
542
+ await callTool('vibe_v5_set', { formalVerify: 'encourage' }, RG)
543
+ const instG = instRootOf(RG)
544
+ // make p-def Lean-PASSED first: the archived proof is what a defect must withdraw
545
+ const arcG = await callTool('vibe_v5_lean_archive', {
546
+ kind: 'proof', target: 'p-def', content: 'theorem p_def : (1:Nat) + 1 = 2 := by decide\n',
547
+ }, childAgent(childOf(RG, 'r-1')))
548
+ assert(arcG.ok === true && arcG.passed === true, 'p-def is Lean-passed before the review')
549
+ assert(existsSync(join(instG, 'Verified', 'Lean', 'p-def.lean')), 'its archived proof exists on disk')
550
+ // a voter reports a FIDELITY DEFECT through the reply channel
551
+ const wDef = await wakeAndReply(RG, 'r-1', {
552
+ progress: '逐条核对后发现 Lean 陈述与命题不一致。',
553
+ formal: { target: 'p-def', decision: 'defect', note: 'Lean 里把"连续"写成了逐点连续,条件被加强了' },
554
+ contextPct: 20,
555
+ })
556
+ assert(!!wDef, 'the reviewer was woken and answered with a defect report')
557
+ const stG = await callTool('vibe_v5_status', {}, RG)
558
+ const recG = (stG.formal.objects || []).find(o => o.target === 'p-def') || {}
559
+ assert(recG.status === 'attempted', '★ the object is demoted to attempted (a defect is not a proof any more), got ' + recG.status)
560
+ assert(!recG.proof, '★ the archived proof is cleared from the record')
561
+ assert(/加强了/.test(String(recG.note || '')), 'the concrete deviation is recorded on the object')
562
+ const proofPathG = join(instG, 'Verified', 'Lean', 'p-def.lean')
563
+ const proofNowG = existsSync(proofPathG) ? readFileSync(proofPathG, 'utf8') : ''
564
+ assert(!existsSync(proofPathG) || /已撤回/.test(proofNowG),
565
+ '★ the archived proof is withdrawn from Verified/Lean/ (deleted, or replaced by a withdrawal notice when the host cannot delete)')
566
+ assert(!/theorem p_def/.test(proofNowG), '★ the original proof text is no longer readable as the object\'s proof')
567
+ assert(existsSync(join(instG, 'Formal', 'p-def.lean')), 'the working file is kept (the code is not lost)')
568
+ const todoG = readIf(join(instG, 'Formal', 'TODO.md'))
569
+ assert(/p-def/.test(todoG) && /忠实性缺陷/.test(todoG), 'the object enters Formal/TODO.md as a formalisation defect')
570
+ const idxG = readIf(join(instG, 'Formal', 'Index.md'))
571
+ assert(/加强了/.test(idxG), 'the human-readable index carries the concrete deviation')
572
+ assert(/attempted/.test(idxG) && !/Verified\/Lean\/p-def\.lean/.test(idxG.split('p-def')[1] || ''),
573
+ 'the index shows attempted and no longer points at a proof')
574
+ // a defect without a note is refused
575
+ delivered.length = 0
576
+ const wDef2 = await wakeAndReply(RG, 'r-1', { formal: { target: 'p-def2', decision: 'defect' }, contextPct: 20 })
577
+ assert(!!wDef2, 'the reviewer was woken for the note-less defect')
578
+ assert(/必须写明 note/.test(delivered.map(d => d.prompt).join('\n')), 'a defect without a note is refused with an explicit notice')
579
+ assert((await callTool('vibe_v5_status', {}, RG)).formal.objects.every(o => o.target !== 'p-def2'),
580
+ 'and no record is created for the refused defect')
581
+
582
+ // ---------- 12. after a defect, require mode refuses to conclude -------------
583
+ section('12 ★ a defect makes the require gate block the conclusion (re-formalise, do not conclude 假)')
584
+ await callTool('vibe_v5_set', { formalVerify: 'require' }, RG)
585
+ await callTool('vibe_v5_record_proposition', { id: 'p-def', statement: '连续函数在闭区间上一致连续(被写窄的形式化)', value: 0.6, motive: 'm', p: 0.9 }, childAgent(childOf(RG, 'r-1')))
586
+ await callTool('vibe_v5_propose_verify', { target: 'p-def', kind: 'proposition', reason: '缺陷后重验' }, childAgent(childOf(RG, 'r-1')))
587
+ // every voter says TRUE — without the fix this would be recorded as a concluded object
588
+ const stDef = await voteToConclusion(RG, 'p-def', new Map([['acad', 1], ['r-1', 1], ['r-2', 1]]))
589
+ assert(stDef.verified.indexOf('p-def') === -1, '★ the object is NOT verified: a withdrawn proof cannot support a conclusion')
590
+ assert(stDef.undecided.indexOf('p-def') !== -1, '★ it is recorded as 未定论 and stays in the library')
591
+ assert(!existsSync(join(instG, 'Verified', '命题', 'p-def.md')), 'no Verified card is written for it')
592
+ assert(/p-def/.test(readIf(join(instG, 'Formal', 'TODO.md'))), 'it is on the formalisation TODO list')
593
+
522
594
  console.log('')
523
595
  console.log('passed=' + passed + ' failed=' + failed)
524
596
  if (failed) { console.error('FAILURES:'); for (const f of failures) console.error(' - ' + f); process.exit(1) }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-vibe-math",
3
3
  "description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — FOUR agent presets in one install: vibe-math-v2 (probability-driven: qs.json + Propos knowledge base + explorer→solver→review/debate verdict), vibe-math-v3 (THIRD-generation, recommended: paper-style Markdown knowledge base with Problems/Progress/Propos/Methods/Verified + planner-agent scheduling that decides the next N actions + universal theory/method invention library + agents write their own Markdown directly via a per-file write lock), and vibe-math-v4 (FOURTH-generation: persistent self-organizing resident subagents that message & meet to decide all tasks, verify only by unanimous consensus, /compact at a context threshold, and stop only when all agree the problem is solved), and vibe-math-v5 (FIFTH-generation research institute: an academician as the organizational centre who decomposes and ASSIGNS work and chairs meetings; permanent researchers who hold the vote and may hire/fire their own temp workers; temp workers with no vote; a group chat and meetings; a durable per-recipient mailbox; a compare-and-set task DAG; and a boolean m-vote consensus rule where an object enters Verified/ only when at least m voting members agree AND every one of them returns exactly 1 or exactly 0). Installing this bundle auto-installs all four presets (v1 was removed at v2.0.0).",
4
- "version": "2.3.0",
4
+ "version": "2.3.1",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -27,21 +27,28 @@
27
27
  "formal-verify-v5.test.mjs",
28
28
  "installer.js",
29
29
  "prompt-v5-integrity.test.mjs",
30
+ "run-tests.mjs",
30
31
  "RELEASE-NOTES-2.0.22.md",
31
32
  "RELEASE-NOTES-2.1.0.md",
32
33
  "RELEASE-NOTES-2.2.0.md",
33
34
  "RELEASE-NOTES-2.2.1.md",
34
35
  "RELEASE-NOTES-2.2.2.md",
35
36
  "RELEASE-NOTES-2.3.0.md",
37
+ "RELEASE-NOTES-2.3.1.md",
36
38
  "selfdrive-v5.mjs",
37
39
  "示例图/框架图-v5.svg",
38
40
  "docs/架构图.md",
39
41
  "docs/formal-verification.md",
42
+ "docs/test-timing.md",
40
43
  "docs/generate_framework_diagram_v5.mjs",
41
44
  "prompt-corpus-persona/persona-corpus.json",
42
45
  "prompt-corpus-persona/persona-corpus.md",
46
+ "prompt-corpus-v2/formal-verify-v2.json",
47
+ "prompt-corpus-v2/formal-verify-v2.md",
43
48
  "prompt-corpus-v3/formal-verify-v3.json",
44
49
  "prompt-corpus-v3/formal-verify-v3.md",
50
+ "prompt-corpus-v4/formal-verify-v4.json",
51
+ "prompt-corpus-v4/formal-verify-v4.md",
45
52
  "prompt-corpus-v5/prompt-corpus-v5.json",
46
53
  "prompt-corpus-v5/prompt-corpus-v5.md",
47
54
  "vibe-math-v2/实现方案.md",
@@ -91,7 +98,7 @@
91
98
  },
92
99
  "minVersion": "0.1.2-rc.1",
93
100
  "testedVersion": "0.1.5-rc.2",
94
- "compatNote": "依赖宿主提供的 subagents/agents/tools/commands/fs 服务与 @deepseek-ai/dsh-* 插件行;可选 subprocess/sandboxPolicy/compaction。persona 行同时携带 prefix 与 text 两个键,以兼容 0.1.3-alpha.2 的 schema 更名(prefix 必填)与 0.1.2 及更早的 text 键。已在 dsh-v0.1.5-rc.2(@deepseek-ai/dsh-persona 0.1.5-rc.2)上逐行校验全部预设行并通过(v2/v3/v4;v1 已于 v2.0.0 移除)。注意:DSH 0.1.2 起 subagents.startContinuable 的 agentOptions/toolFilter 需要宿主 provider 声明对应 capability(spawn/fork 进程内 provider 均支持),安装器启动时会做能力自检并在旧版宿主上告警。2026 兼容性修复:v2/v3 工具权限名表原先硬编码 web/fetch/bash(未注册名会使 tools.restrict() 抛错、子代理无法建立),现按真实注册名并加带守卫的重试;v4 真实 /compact 原先在 subagent/end 里查 agents.get()(该事件触发时子代理已移出注册表,属死代码),现改为在 subagent/start 捕获 Agent 引用;三套预设的可选服务改为惰性读取,不再在 apply() 快照;v4 的 tools/commands 注册补入 ctx.effect;安装器自检新增 subprocess/sandboxPolicy/compaction。v2.1.0 新增 v5 研究所体系:状态存于宿主 host-only 会话投影单元(键 vibeMathV5),因此自检新增 sessionProjections/sessions(均为可选;缺失时 v5 回退到加固 JSON 状态文件)。v5 不依赖任何 npm 实验包,纯 preset 内单文件实现。v2.3.0 为四个架构新增可调控的 Lean 形式化验证(参数 formalVerify = off/encourage/require,默认 off):验证时按实现难度决定是否用 Lean 形式化(写代码+执行),一旦通过则审查对象从「推导是否正确」变成「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」;形式化代码归档为命题的证明(Verified/Lean/<id>.lean),可复用定义与已证引理归档到跨项目的 VibeMath/Formal/{Lib,Proved}/。require 档带门禁:真/假结论必须先有 Lean 通过或显式阻塞记录,否则记为未定论并进入形式化待办。共用契约 docs/formal-verification.md,四套各带 formal-verify-vN 套件(v2 177 / v3 189 / v4 144 / v5 88 断言)与 audit-formal-sensitivity.mjs 探针。同一次审计还发现并修复了一整类**静态提示词面**缺陷(persona ↔ 工具注册表,既有套件全部盲):v2/v3/v4 的 persona 从未列出无条件注册的三个 *_lean_* 工具,v4 的 vibe_v4_set 参数表漏了 formalVerify/leanCommand/leanArgs/leanTimeoutMs,v3 漏了 setup/save_settings/template,v4 漏了 vibe_v4_prompts,v5 漏了增删常驻研究员的工具、且 prefix 与 text 两个块存在文字漂移;现由 audit-persona-surface.test.mjs(197 断言:双向一致性 + 未文档化工具显式快照 + prefix/text 逐行一致 + 斜杠命令 hint/usage/实际分支三处一致 + Lean 参数/档位/路径,并生成随包发布的 prompt-corpus-persona/ 人读语料)与 audit-persona-sensitivity.mjs(11 条探针,含「未变异副本必须为绿」的对照)守护,AUDIT-CHECKLIST.md 新增 §1.6。v2.2.2 新增 v5 架构图(示例图/框架图-v5.svg + docs/generate_framework_diagram_v5.mjs 零依赖 Node 生成器 + vibe-math-v5/架构图.md 全套 Mermaid 细节图),并修复在绘制架构图时暴露的真实缺陷:会议进行中提出的验证会并发启动(会议与验证的互斥此前只做了单向),现改为排队。v2.2.1 把「全面检查必查清单」(AUDIT-CHECKLIST.md) 作为随包强制流程发布,提示词/交互正确性列为第一优先审计维度。v2.2.0 修复实测发现的提示词身份错乱:状态块改为显式接收它所描述的成员,创建成员时先落盘进编制再构造入职提示词,章程快照冻结在入职时,重建会话不再自称“刚入职”,所办调用不再被误判成某位研究员,框架反馈改为独立发送者投递,一次提示词不再重复投递同一条消息,并新增 prompt-v5-integrity 提示词完整性套件 + 可人工复核的提示词语料(随包发布)。",
101
+ "compatNote": "依赖宿主提供的 subagents/agents/tools/commands/fs 服务与 @deepseek-ai/dsh-* 插件行;可选 subprocess/sandboxPolicy/compaction。persona 行同时携带 prefix 与 text 两个键,以兼容 0.1.3-alpha.2 的 schema 更名(prefix 必填)与 0.1.2 及更早的 text 键。已在 dsh-v0.1.5-rc.2(@deepseek-ai/dsh-persona 0.1.5-rc.2)上逐行校验全部预设行并通过(v2/v3/v4;v1 已于 v2.0.0 移除)。注意:DSH 0.1.2 起 subagents.startContinuable 的 agentOptions/toolFilter 需要宿主 provider 声明对应 capability(spawn/fork 进程内 provider 均支持),安装器启动时会做能力自检并在旧版宿主上告警。2026 兼容性修复:v2/v3 工具权限名表原先硬编码 web/fetch/bash(未注册名会使 tools.restrict() 抛错、子代理无法建立),现按真实注册名并加带守卫的重试;v4 真实 /compact 原先在 subagent/end 里查 agents.get()(该事件触发时子代理已移出注册表,属死代码),现改为在 subagent/start 捕获 Agent 引用;三套预设的可选服务改为惰性读取,不再在 apply() 快照;v4 的 tools/commands 注册补入 ctx.effect;安装器自检新增 subprocess/sandboxPolicy/compaction。v2.1.0 新增 v5 研究所体系:状态存于宿主 host-only 会话投影单元(键 vibeMathV5),因此自检新增 sessionProjections/sessions(均为可选;缺失时 v5 回退到加固 JSON 状态文件)。v5 不依赖任何 npm 实验包,纯 preset 内单文件实现。v2.3.1 是审计驱动的提示词/交互修复版(无破坏性变更,默认仍为 off):① 忠实性缺陷不再被记成「命题为假」——新增回执取值 decision='defect'(表决者发现 Lean 代码与命题原文不一致时不得投 0,给中间值并记录具体偏差;框架随即把该对象降级为 attempted、清空 proof、撤回归档证明 Verified/Lean/<id>.lean、写入 Formal/TODO.md,require 档下本次裁定不定论),encourage 档不承诺它无法强制的搁置;② 修复 v2 的 formal 回执通道是死代码(提示词要求写进回执、契约里却没有该字段、框架也从不解析)——补齐 formalJsonField/formalReplyNote/absorbFormalFromReply 并接进初评与辩论两条路径,套件改为行为断言而非措辞断言;③ 修复 v2/v3 忠实性分支的字段名错误(写成 verdict,真实字段是 Result,会导致该票被静默丢弃);④ 注入文本里的工具名一律改为注册名全称(v2/v3/v5 原先出现 lean_lib/lean_archive 缩写,含工具自身返回的 hint);⑤ 新增「归档可复用定义/引理前先跑通」与「宿主无 Lean 工具链(LEAN_NOT_FOUND)时把代码归档并在 note 写明,算显式阻塞原因」两条硬要求;⑥ 四套各自新增随包发布的人工复核语料 prompt-corpus-vN/(覆盖 off/encourage/require/忠实性/工作轮/回执契约),并修复 v5 语料路径归一化在 Windows 大小写差异下漏掉 VibeMath 根绝对路径、导致语料不确定且泄露本机路径的问题;⑦ 新增 16 条提示词灵敏度探针(删掉「不要投 0」、工具名换缩写、删掉 require 门禁措辞、回执契约去掉 defect,各四套),全套件断言 v2 261 / v3 247 / v4 226 / v5 120 / prompt-v5-integrity 588。v2.3.0 为四个架构新增可调控的 Lean 形式化验证(参数 formalVerify = off/encourage/require,默认 off):验证时按实现难度决定是否用 Lean 形式化(写代码+执行),一旦通过则审查对象从「推导是否正确」变成「Lean 的定义/对象/条件/假设/结论是否忠实于命题原文」;形式化代码归档为命题的证明(Verified/Lean/<id>.lean),可复用定义与已证引理归档到跨项目的 VibeMath/Formal/{Lib,Proved}/。require 档带门禁:真/假结论必须先有 Lean 通过或显式阻塞记录,否则记为未定论并进入形式化待办。共用契约 docs/formal-verification.md,四套各带 formal-verify-vN 套件(v2 177 / v3 189 / v4 144 / v5 88 断言)与 audit-formal-sensitivity.mjs 探针。同一次审计还发现并修复了一整类**静态提示词面**缺陷(persona ↔ 工具注册表,既有套件全部盲):v2/v3/v4 的 persona 从未列出无条件注册的三个 *_lean_* 工具,v4 的 vibe_v4_set 参数表漏了 formalVerify/leanCommand/leanArgs/leanTimeoutMs,v3 漏了 setup/save_settings/template,v4 漏了 vibe_v4_prompts,v5 漏了增删常驻研究员的工具、且 prefix 与 text 两个块存在文字漂移;现由 audit-persona-surface.test.mjs(197 断言:双向一致性 + 未文档化工具显式快照 + prefix/text 逐行一致 + 斜杠命令 hint/usage/实际分支三处一致 + Lean 参数/档位/路径,并生成随包发布的 prompt-corpus-persona/ 人读语料)与 audit-persona-sensitivity.mjs(11 条探针,含「未变异副本必须为绿」的对照)守护,AUDIT-CHECKLIST.md 新增 §1.6。v2.2.2 新增 v5 架构图(示例图/框架图-v5.svg + docs/generate_framework_diagram_v5.mjs 零依赖 Node 生成器 + vibe-math-v5/架构图.md 全套 Mermaid 细节图),并修复在绘制架构图时暴露的真实缺陷:会议进行中提出的验证会并发启动(会议与验证的互斥此前只做了单向),现改为排队。v2.2.1 把「全面检查必查清单」(AUDIT-CHECKLIST.md) 作为随包强制流程发布,提示词/交互正确性列为第一优先审计维度。v2.2.0 修复实测发现的提示词身份错乱:状态块改为显式接收它所描述的成员,创建成员时先落盘进编制再构造入职提示词,章程快照冻结在入职时,重建会话不再自称“刚入职”,所办调用不再被误判成某位研究员,框架反馈改为独立发送者投递,一次提示词不再重复投递同一条消息,并新增 prompt-v5-integrity 提示词完整性套件 + 可人工复核的提示词语料(随包发布)。",
95
102
  "compatibility": {
96
103
  "dshReleases": {
97
104
  "0.1.2-alpha.4": "compatible",