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
@@ -1382,7 +1382,7 @@ export function apply(ctx) {
1382
1382
  L.push(' "task_update": {"task_id":"t-3","expected_revision":2,"action":"complete|release|reopen|edit|set_dependencies|delete"},')
1383
1383
  L.push(' "input": "本轮会议/辩论的发言正文(会议轮用;也可直接用 say)",')
1384
1384
  if (formalOn()) {
1385
- L.push(' "formal": {"target":"p-x","decision":"used|blocked","file":"Formal/p-x.lean","note":"难度判断/阻塞原因"}')
1385
+ L.push(' "formal": {"target":"p-x","decision":"used|blocked|defect","file":"Formal/p-x.lean","note":"难度判断/阻塞原因"}')
1386
1386
  L.push(' ← Lean 形式化:' + (formalMode() === 'require'
1387
1387
  ? '**强制**:定论前必须有「Lean 已通过」或显式阻塞原因(note 必填),否则本轮裁定记为未定论,'
1388
1388
  : '**鼓励**:按实现难度自行决定;做了就归档,没做就写明难度判断,') + '详见提示词里的【Lean 形式化验证】段')
@@ -1511,11 +1511,9 @@ export function apply(ctx) {
1511
1511
  L.push(formalPromptBlock(vs.target))
1512
1512
  // Make the SHIFT explicit: with a machine-checked proof in hand, re-deriving is
1513
1513
  // wasted effort and the real risk is a statement that does not say what we meant.
1514
- if (rec.status === 'passed') {
1515
- L.push(' ▸ 因此请把 verdict 用在**忠实性**上:一致 → 1;发现任何偏离 → 0(或按不确定度给中间值并说明)。')
1516
- } else if (rec.status === 'blocked') {
1514
+ if (rec.status === 'blocked') {
1517
1515
  L.push(' ▸ 因此请把 verdict 用在"这个阻塞判断是否成立 / 是否仍有别的形式化路线"上,并给出理由。')
1518
- } else {
1516
+ } else if (rec.status !== 'passed') {
1519
1517
  L.push(' ▸ 若你在本轮把它形式化并跑通(vibe_v5_lean_archive kind=\'proof\'),后续轮次的')
1520
1518
  L.push(' 审查对象就会从"推导是否正确"变成"Lean 代码是否忠实于命题"。')
1521
1519
  }
@@ -1541,7 +1539,7 @@ export function apply(ctx) {
1541
1539
  // The formal field belongs in the VOTING contract too: voters are exactly the agents
1542
1540
  // who must either formalize the object or record why they judged it infeasible.
1543
1541
  L.push('若你本轮做了形式化或给出难度判断,请一并加上:')
1544
- L.push('{"formal":{"target":"' + vs.target + '","decision":"used|blocked","file":"Formal/' + vs.target + '.lean","note":"难度判断/阻塞原因"}}')
1542
+ L.push('{"formal":{"target":"' + vs.target + '","decision":"used|blocked|defect","file":"Formal/' + vs.target + '.lean","note":"难度判断/阻塞原因"}}')
1545
1543
  }
1546
1544
  return L.join('\n')
1547
1545
  }
@@ -1751,6 +1749,48 @@ export function apply(ctx) {
1751
1749
  updatedAt: now(),
1752
1750
  }))
1753
1751
  }
1752
+ // Withdrawing an archived proof needs a DELETE, but the fs service exposes no unlink and
1753
+ // subprocess is optional. Use the shell when it is available; otherwise overwrite the
1754
+ // file with an explicit withdrawal notice, so it can never be read as the object's proof.
1755
+ async function removeArchivedProof(rel) {
1756
+ const abs = leanAbsPath(rel)
1757
+ if (abs === null) return false
1758
+ const sub = subprocessOf()
1759
+ if (sub !== undefined && typeof sub.spawn === 'function') {
1760
+ const script = isWindows()
1761
+ ? 'Remove-Item -LiteralPath ' + psQuote(abs) + ' -Force -ErrorAction SilentlyContinue'
1762
+ : 'rm -f ' + shQuote(abs)
1763
+ try { await runShell(script) } catch (e) { /* fall through to the overwrite */ }
1764
+ // A shell that exits 0 without removing anything (a stub host, a permissions quirk)
1765
+ // must not leave a withdrawn proof where everyone looks for proofs: verify, fall back.
1766
+ if (await readTextAbs(abs) === undefined) return true
1767
+ }
1768
+ return (await writeTextAbs(abs, '-- 已撤回(' + fmtTime() + '):该形式化被认定与命题原文不一致。\n'
1769
+ + '-- 原代码保留在工作文件 Formal/' + String(rel).split('/').pop() + ';修正并重新跑通后重新归档。\n')) !== false
1770
+ }
1771
+ // A formalization that says something else than the proposition is NOT a refutation:
1772
+ // withdraw the proof instead of letting the group conclude 假 (contract §4.1).
1773
+ async function recordFidelityDefect(memberId, rawTarget, note) {
1774
+ const t = idSafe(String(rawTarget || ''))
1775
+ const why = String(note || '').trim()
1776
+ if (!t) return { ok: false, code: 'V5_INVALID_ARGUMENT', message: 'target is required' }
1777
+ if (!why) return { ok: false, code: 'V5_INVALID_ARGUMENT', message: "decision='defect' 必须写明 note(具体偏差)" }
1778
+ const prev = formalOf(t)
1779
+ const proofRel = String(prev.proof || ('Verified/Lean/' + t + '.lean'))
1780
+ await putFormal(t, Object.assign({}, prev, {
1781
+ status: 'attempted', proof: '', decision: 'defect', note: why,
1782
+ fidelity: { at: now(), by: String(memberId || ''), note: why }, updatedAt: now(),
1783
+ }))
1784
+ const removed = await removeArchivedProof(proofRel)
1785
+ const list = formalTodo().filter((x) => x.id !== t)
1786
+ list.push({ id: t, at: now(), why: '形式化不合格(忠实性缺陷):' + why })
1787
+ await putFormal(t, formalOf(t), list)
1788
+ await writeFormalTodo()
1789
+ await writeFormalIndex()
1790
+ await saveChatLine('【形式化】' + (memberId || '成员') + ' 认定 ' + t + ' 的形式化**不忠实**:' + why
1791
+ + ' —— 已撤回「已通过」状态并从 Verified/Lean/ 删除归档证明;请修正形式化、重新跑通后再投票。')
1792
+ return { ok: true, target: t, status: 'attempted', removed }
1793
+ }
1754
1794
  function formalPromptBlock(target) {
1755
1795
  if (!formalOn()) return ''
1756
1796
  const mode = formalMode()
@@ -1758,11 +1798,24 @@ export function apply(ctx) {
1758
1798
  const L = []
1759
1799
  L.push('【Lean 形式化验证(' + (mode === 'require' ? '强制' : '鼓励') + '模式)】')
1760
1800
  if (rec.status === 'passed') {
1761
- // The whole point of the feature: the review subject CHANGES.
1801
+ // The whole point of the feature: the review subject CHANGES. And the SECOND half of
1802
+ // that point (docs/formal-verification.md §4.1): a statement that says something else
1803
+ // than the proposition is NOT a refutation — "偏离 → 0" would make the very mechanism
1804
+ // that exists for rigour fabricate a false negative conclusion.
1762
1805
  L.push(' · 该对象已有**通过的 Lean 形式化证明**(' + (rec.proof || rec.file) + ',最近运行 exit 0)。')
1763
1806
  L.push(' **你不需要重新检查推导**。你的任务是**忠实性审查**:逐条核对 Lean 代码里的')
1764
- L.push(' 定义 / 对象 / 条件 / 假设 / 结论是否与命题原文**完全一致**(有偏差就指出偏差),')
1765
- L.push(' 并据此给出 verdict。')
1807
+ L.push(' 定义 / 对象 / 条件 / 假设 / 结论是否与命题原文**完全一致**。')
1808
+ L.push(' ▸ 一致 → verdict = 1。')
1809
+ L.push(' ▸ **发现任何偏差,不要投 0**:偏差只说明**形式化不合格**,不代表命题为假。此时请:')
1810
+ L.push(' ① verdict 给一个严格介于 0 与 1 之间的值(记为弃权),并在 reason 里写清偏差;')
1811
+ L.push(" ② 用回执 formal:{decision:'defect', note:'<具体偏差>'} 记录它。框架会撤回这条证明的")
1812
+ // Only `require` actually GATES the conclusion; in `encourage` the framework still
1813
+ // withdraws the proof (and records the defect) but must not promise a hold it cannot
1814
+ // enforce — the voter's own abstention is what keeps the ballot from concluding.
1815
+ L.push(' 「已通过」状态(降级为 attempted、删除归档证明、写入形式化待办)'
1816
+ + (mode === 'require' ? ',本次裁定**不定论**;' : ';'))
1817
+ L.push(' 修正形式化并重新跑通后再投票。')
1818
+ L.push(' ▸ 只有当你**独立于这份 Lean 代码**也能确定命题为假时,才投 0,并在 reason 里写清独立理由。')
1766
1819
  } else if (rec.status === 'blocked') {
1767
1820
  L.push(' · 该对象已被记录为**形式化阻塞**:' + (rec.note || '未说明') + '。')
1768
1821
  L.push(' 请复核这个判断是否成立;若你认为其实可以形式化,请指出来并动手做。')
@@ -1770,16 +1823,18 @@ export function apply(ctx) {
1770
1823
  L.push(' · 请先判断该对象的**实现难度**:若能在可接受的工作量内形式化,优先写 Lean 代码并执行。')
1771
1824
  L.push(' · 工具:vibe_v5_lean_run(执行)· vibe_v5_lean_archive(归档)· vibe_v5_lean_lib(查已有可复用库)')
1772
1825
  L.push(' · 工作目录:Formal/(相对研究所根);可复用定义放 ' + formalLibRoot().replace(/\\/g, '/') + '/,')
1773
- L.push(' 已证引理放 ' + formalProvedRoot().replace(/\\/g, '/') + '/;写之前先 lean_lib 查重。')
1774
- L.push(' · **一旦 Lean 通过,你唯一需要确认的就是忠实性**:定义/对象/条件/假设/结论是否与')
1826
+ L.push(' 已证引理放 ' + formalProvedRoot().replace(/\\/g, '/') + '/;写之前先 vibe_v5_lean_lib 查重。')
1827
+ L.push(' · **一旦 Lean 通过,你唯一需要确认的就是忠实性**:定义 / 对象 / 条件 / 假设 / 结论是否与')
1775
1828
  L.push(' 命题原文逐条一致。请把注意力放在这种核对上,而不是重新做一遍推导。')
1776
1829
  if (mode === 'require') {
1777
- L.push(' · **本模式要求**:必须产出 Lean 形式化,或**必须**给出显式的阻塞原因(lean_archive')
1830
+ L.push(' · **本模式要求**:必须产出 Lean 形式化,或**必须**给出显式的阻塞原因(vibe_v5_lean_archive')
1778
1831
  L.push(' kind=\'blocked\' note=… 或回执 formal.note)。若两者都没有,本次裁定不会生效,')
1779
1832
  L.push(' 会被记为未定论(原因 formal-required)并进入「形式化待办」。')
1780
1833
  } else {
1781
- L.push(' · 若你判断不值得或无法形式化,可以不做,但请在回执的 formal 字段写明难度判断。')
1834
+ L.push(" · 若你判断不值得或无法形式化,可以不做,但请在回执的 formal 字段写明难度判断(decision='blocked' 时必须写明 note)。")
1782
1835
  }
1836
+ L.push(' · 归档可复用定义/引理前先跑通(vibe_v5_lean_archive run=true 或先 vibe_v5_lean_run);跑不通不要入库。')
1837
+ L.push(' · 宿主没有 Lean 工具链(LEAN_NOT_FOUND)时:把代码写下来归档,并在回执的 note 里写明"宿主无 Lean 工具链"——这算显式阻塞原因,定论门禁可以据此放行。')
1783
1838
  }
1784
1839
  return L.join('\n')
1785
1840
  }
@@ -1788,6 +1843,7 @@ export function apply(ctx) {
1788
1843
  return '【顺手形式化(' + (formalMode() === 'require' ? '强制' : '鼓励') + ')】把你工作中常用或可能复用的对象、假设、'
1789
1844
  + '新定义用 Lean 形式化定义并归档到全局可复用库(vibe_v5_lean_archive kind=\'def\'),已成立的引理归到 Proved/'
1790
1845
  + '(kind=\'lemma\');写之前先 vibe_v5_lean_lib 查重,避免重复定义。'
1846
+ + '归档前先跑通(vibe_v5_lean_run 或 run=true);跑不通的定义不要进可复用库。'
1791
1847
  + (formalMode() === 'require'
1792
1848
  ? '本模式下,任何要定论为真/假的对象都必须先有 Lean 通过或显式阻塞记录。'
1793
1849
  : '这会让后续的验证与证明省掉大量重复工作。')
@@ -1879,8 +1935,8 @@ export function apply(ctx) {
1879
1935
  }
1880
1936
  return Object.assign({ ok: !!run.ok }, run, {
1881
1937
  hint: run.ok
1882
- ? '通过。若是某个对象的证明,请用 lean_archive kind=\'proof\' 归档(会写入 Verified/Lean/ 并把审查对象变成忠实性);若是可复用定义/引理,用 kind=\'def\'/\'lemma\' 归档到全局库。'
1883
- : '未通过。请按上面的编译器输出修复后重跑;若判断无法完成,用 lean_archive kind=\'blocked\' 记录原因。',
1938
+ ? '通过。若是某个对象的证明,请用 vibe_v5_lean_archive kind=\'proof\' 归档(会写入 Verified/Lean/ 并把审查对象变成忠实性);若是可复用定义/引理,用 kind=\'def\'/\'lemma\' 归档到全局库。'
1939
+ : '未通过。请按上面的编译器输出修复后重跑;若判断无法完成,用 vibe_v5_lean_archive kind=\'blocked\' 记录原因。',
1884
1940
  })
1885
1941
  }
1886
1942
  async function leanArchive(memberId, o) {
@@ -3404,10 +3460,13 @@ export function apply(ctx) {
3404
3460
  const target = idSafe(String(f.target || ''))
3405
3461
  if (target) {
3406
3462
  const decision = String(f.decision || '').trim()
3407
- if (decision === 'blocked') {
3408
- const note = String(f.note || '').trim()
3409
- if (!note) await notice(member.id, 'formal.decision=\'blocked\' 必须写明 note(难度判断/阻塞原因)——本次未记录。')
3410
- else {
3463
+ const note = String(f.note || '').trim()
3464
+ if (decision === 'blocked' || decision === 'defect') {
3465
+ if (!note) await notice(member.id, "formal.decision='" + decision + "' 必须写明 note(难度判断/阻塞原因/具体偏差)——本次未记录。")
3466
+ else if (decision === 'defect') {
3467
+ const r = await recordFidelityDefect(member.id, target, note)
3468
+ if (r.ok === false) await notice(member.id, '记录忠实性缺陷失败(' + (r.code || '') + '):' + (r.message || ''))
3469
+ } else {
3411
3470
  const r = await leanArchive(member.id, { kind: 'blocked', target, note })
3412
3471
  if (r.ok === false) await notice(member.id, '记录形式化阻塞失败(' + (r.code || '') + '):' + (r.message || ''))
3413
3472
  }
@@ -3420,7 +3479,7 @@ export function apply(ctx) {
3420
3479
  }))
3421
3480
  await writeFormalIndex()
3422
3481
  } else if (decision) {
3423
- await notice(member.id, 'formal.decision 只能是 \'used\' 或 \'blocked\'(收到 ' + decision + ')')
3482
+ await notice(member.id, "formal.decision 只能是 'used' / 'blocked' / 'defect'(收到 " + decision + ')')
3424
3483
  }
3425
3484
  }
3426
3485
  }
@@ -4009,7 +4068,7 @@ export function apply(ctx) {
4009
4068
  publishProgress, recordCard, readLibrary,
4010
4069
  // Lean formal verification (docs/formal-verification.md)
4011
4070
  formalMode, formalOn, formalRecords, formalTodo, formalOf, rebuildLeanLibIndexes,
4012
- leanArchive, leanRunTool, writeFormalIndex, writeFormalTodo,
4071
+ leanArchive, leanRunTool, writeFormalIndex, writeFormalTodo, recordFidelityDefect,
4013
4072
  leanRunToolApi: async (relPath, timeoutMs) => await leanRunFile(relPath, timeoutMs),
4014
4073
  // consensus / meetings
4015
4074
  maybeQueueVerify, castVerdict, currentVerify, hasVerifyInFlight, startMeeting, quorumM, voterCount,
@@ -4163,7 +4222,7 @@ export function apply(ctx) {
4163
4222
  counts: r, todo: s.formalTodo(),
4164
4223
  objects: Object.keys(s.formalRecords()).map((k) => ({ target: k, status: (s.formalRecords()[k] || {}).status, file: (s.formalRecords()[k] || {}).file, proof: (s.formalRecords()[k] || {}).proof, note: (s.formalRecords()[k] || {}).note })),
4165
4224
  paths: { project: 'Formal/(相对研究所根)', lib: 'VibeMath/Formal/Lib/', proved: 'VibeMath/Formal/Proved/', proofs: 'Verified/Lean/' },
4166
- hint: '复用优先:先在 Lib/ 里找现成定义;新定义用 lean_archive kind=\'def\' 归档,已证引理用 kind=\'lemma\'。',
4225
+ hint: '复用优先:先在 Lib/ 里找现成定义;新定义用 vibe_v5_lean_archive kind=\'def\' 归档,已证引理用 kind=\'lemma\'。',
4167
4226
  verify: st.verify ? st.verify.target : null,
4168
4227
  }
4169
4228
  })
@@ -1113,10 +1113,33 @@ Progress/,不要从头再来",**不得**复用 `【入职首轮】` 与"你
1113
1113
  **门禁位置**:`continueVerifyRound` 里"判定为真/假 → `closeVerify`"这**唯一**收口点
1114
1114
  (`finalizeUndecided` 不受影响)。门禁不通过时调用 `deferForFormal`。
1115
1115
 
1116
- **测试**:`formal-verify-v5.test.mjs`(88 断言,fake-Lean 通过 `subprocess` 服务注入,
1117
- 因此不需要真的装 Lean)+ `audit-v5-sensitivity.mjs` 里 9 个专打形式化的探针;
1118
- `prompt-v5-integrity.test.mjs` 另有一组用例把 Lean 相关提示词原文写进语料
1119
- (`lean-work` / `lean-verify` / `lean-fidelity`),供人工复核"忠实性审查"那段的措辞。
1116
+ **忠实性缺陷(`decision='defect'`,契约 §4.1)**:`passed` 只保证"这段代码过了内核",
1117
+ **不保证它说的就是命题想说的**。首版提示词写的是"发现偏离 → 0"——那会让框架把
1118
+ "形式化写错了"记成"**命题为假**",在全 0 一致规则下直接把命题写进 `Verified/` 标注假,
1119
+ 等于用求真更严格的机制**伪造出一个错误的否定结论**。现在:
1120
+
1121
+ - 表决者发现偏差时**不得投 0**,要给严格介于 0 与 1 之间的值(记为弃权)并用回执
1122
+ `formal:{decision:'defect', note:'<具体偏差>'}` 记录;
1123
+ - `recordFidelityDefect` 把该对象**降级为 `attempted`**、清空 `proof`、撤回
1124
+ `Verified/Lean/<id>.lean`(`subprocess` 可删则删,否则用"已撤回"说明覆盖,见
1125
+ `removeArchivedProof`)、写入 `Formal/TODO.md` 并群聊公告;
1126
+ - `require` 档下这次裁定**不定论**,对象进「形式化待办」,修正形式化并重新跑通后再投票;
1127
+ - 只有表决者**独立于这份 Lean 代码**也能确定命题为假时才投 0。
1128
+
1129
+ **提示词硬要求(契约 §6,四套一致)**:注入文本里的工具名**一律全称**
1130
+ (`vibe_v5_lean_*`;缩写 `lean_archive` 不是注册名,代理照抄就调用了一个不存在的工具);
1131
+ 归档可复用定义/引理**前先跑通**(`run=true` 或先 `lean_run`),跑不通不许入库;
1132
+ **工具链缺失**(`LEAN_NOT_FOUND`)时把代码归档并在 `note` 写明"宿主无 Lean 工具链"——
1133
+ 这算显式阻塞原因,门禁可以据此放行,代理不会因为装不了 Lean 而卡死。
1134
+
1135
+ **测试**:`formal-verify-v5.test.mjs`(117 断言,含 `defect` 的端到端路径:降级 + 撤回归档证明 +
1136
+ 进待办 + `require` 档不定论;以及"注入文本不得出现缩写工具名"),fake-Lean 通过 `subprocess`
1137
+ 服务注入,因此不需要真的装 Lean;`audit-v5-sensitivity.mjs` 里 9 个专打形式化的探针;
1138
+ `prompt-v5-integrity.test.mjs`(588 断言)把 Lean 相关提示词原文写进语料
1139
+ (`lean-work` / `lean-verify` / `lean-fidelity` / **`lean-require`** / **`lean-after-defect`**,
1140
+ 共 70 条提示词),供人工复核"忠实性审查"那段与缺陷撤回后的状态;语料把工作区路径
1141
+ **大小写无关**地归一化为 `<WS>`(首版因 Windows 路径大小写差异漏掉了 VibeMath 根的绝对路径,
1142
+ 语料因此不确定且泄露本机路径——已修)。
1120
1143
 
1121
1144
 
1122
1145
  ---