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
|
@@ -270,6 +270,24 @@ async function proposeAndVote(h, target, opts = {}) {
|
|
|
270
270
|
// The verification may only have been QUEUED by the proposal round; finish it.
|
|
271
271
|
return await settleVerify(h, opts.plan || (() => (opts.verdict === undefined ? 1 : opts.verdict)))
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Start a verification and KEEP IT IN FLIGHT (the first half of `proposeAndVote`): record a card, have
|
|
275
|
+
* r-1 propose verifying it, and stop driving as soon as the verify view is ACTIVE. Note the trap:
|
|
276
|
+
* `verifyInProgress` is already true while the verification is merely QUEUED, so `consensus.kind`
|
|
277
|
+
* is the only reliable "it is really running" signal.
|
|
278
|
+
*/
|
|
279
|
+
async function startVerifyOnly(h, target) {
|
|
280
|
+
const card = { id: target, title: target, statement: '待验证对象 ' + target, prob: 0.8, value: 0.6, motivation: 'm' }
|
|
281
|
+
await h.callTool('vibe_v4_record_proposition', card, h.resAgent(h.childOf('r-1')))
|
|
282
|
+
const before = h.followups.length
|
|
283
|
+
await h.callTool('vibe_v4_message', { to: 'r-1', content: '请处理本轮工作。' })
|
|
284
|
+
for (let i = 0; i < 300 && h.followups.length === before; i++) await sleep(10)
|
|
285
|
+
await drive(h, () => ({ summary: '提议验证 ' + target + '。', solved: false, propose_verify: target, contextPct: 20 }), async () => {
|
|
286
|
+
const s = await h.callTool('vibe_v4_status', {})
|
|
287
|
+
return !!(s.consensus && s.consensus.kind === 'verify')
|
|
288
|
+
})
|
|
289
|
+
return await h.callTool('vibe_v4_status', {})
|
|
290
|
+
}
|
|
273
291
|
/** Wake exactly the given resident with an ordinary work round and return that wake. */
|
|
274
292
|
async function workWake(h, rId) {
|
|
275
293
|
const before = h.followups.length
|
|
@@ -1479,6 +1497,75 @@ section('18 every daily-line injection site (coreRules/normal/heartbeat) obeys t
|
|
|
1479
1497
|
await N.callTool('vibe_v4_set', { formalVerify: 'off', leanInitiative: 'normal' })
|
|
1480
1498
|
}
|
|
1481
1499
|
|
|
1500
|
+
// ===============================================================
|
|
1501
|
+
// 22. cross-preset audit P0 (v4 station): the member-visible library text must state that the
|
|
1502
|
+
// residents' file tools resolve against the SESSION CWD, so the listed sub-paths must be used
|
|
1503
|
+
// with the ABSOLUTE prefix (and computation artifacts via receipt.scriptAbs / receipt.cwd).
|
|
1504
|
+
// ===============================================================
|
|
1505
|
+
section('22 audit P0: member-facing paths carry the session-cwd / absolute-prefix instruction')
|
|
1506
|
+
{
|
|
1507
|
+
const N = await establish()
|
|
1508
|
+
const texts = [await N.prompts('brainstorm', 'r-1'), await N.prompts('coreRules'), await N.prompts('normal', 'r-1')]
|
|
1509
|
+
const ok = texts.filter((t) => t.indexOf('会话 cwd') !== -1 && t.indexOf('绝对前缀') !== -1)
|
|
1510
|
+
assert(ok.length >= 1, '★ the member-visible library text says file tools resolve against the SESSION CWD and that listed paths need the ABSOLUTE prefix (found in ' + ok.length + '/3 variants)')
|
|
1511
|
+
assert(ok.some((t) => t.indexOf('receipt.scriptAbs') !== -1 || t.indexOf('receipt.cwd') !== -1), '★ …and names the absolute receipt field (receipt.scriptAbs / receipt.cwd) for computation artifacts')
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// ===============================================================
|
|
1515
|
+
// 23. cross-preset audit P1 (v4 stations): a member-facing not-found must be diagnosable — code +
|
|
1516
|
+
// live roster status + an actionable next step — including `removeMember` (previously a bare
|
|
1517
|
+
// {ok:false}) and the not-yet-started case.
|
|
1518
|
+
// ===============================================================
|
|
1519
|
+
section('23 audit P1: member-facing not-found carries code + status + next')
|
|
1520
|
+
{
|
|
1521
|
+
const N = await establish()
|
|
1522
|
+
const wrong = await N.callTool('vibe_v4_send_message', { to: 'r-404', content: 'hi' }, N.resAgent(N.childOf('r-1')))
|
|
1523
|
+
assert(wrong.ok === false && wrong.code === 'V4_NO_SUCH_RESIDENT' && !!wrong.next && !!wrong.next.tool, '★ wrong resident id ⇒ code V4_NO_SUCH_RESIDENT + next{tool} (not a bare message)')
|
|
1524
|
+
assert(wrong.status && wrong.status.residentCount >= 1 && wrong.status.running === true, '★ …plus LIVE roster status, so "wrong id" is distinguishable from "not started"')
|
|
1525
|
+
const kicked = await N.callTool('vibe_v4_remove_member', { id: 'r-404' })
|
|
1526
|
+
assert(kicked.ok === false && kicked.code === 'V4_NO_SUCH_RESIDENT' && !!kicked.next && kicked.next.tool === 'vibe_v4_remove_member', '★ vibe_v4_remove_member {id:r-404} ⇒ code + next naming that tool (was a bare {ok:false})')
|
|
1527
|
+
const fresh = await mount()
|
|
1528
|
+
const notStarted = await fresh.callTool('vibe_v4_publish_progress', { content: 'x' })
|
|
1529
|
+
assert(notStarted.ok === false && notStarted.code === 'V4_NO_SUCH_RESIDENT' && !!notStarted.next && !!notStarted.status && notStarted.status.running === false, '★ run not started ⇒ code + next + status.running=false')
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// ---------- D4 cross-view: mid-verify hire/fire must not split the views ----------
|
|
1533
|
+
section('D4 cross-view: one frozen participant set, seen identically by every view')
|
|
1534
|
+
{
|
|
1535
|
+
const h = await establish()
|
|
1536
|
+
const st = await startVerifyOnly(h, 'p-d4')
|
|
1537
|
+
assert(st.consensus && st.consensus.kind === 'verify', 'precondition: an ACTIVE verification for the cross-view check (' + JSON.stringify({ c: st.consensus && st.consensus.kind, ip: st.verifyInProgress }) + ')')
|
|
1538
|
+
const c0 = st.consensus
|
|
1539
|
+
const rp0 = await h.callTool('vibe_v4_report', {})
|
|
1540
|
+
const rv0 = rp0.verify
|
|
1541
|
+
assert(Array.isArray(st.frozenParticipants) && rv0, 'the frozen participant set and the report() verify view are both exposed')
|
|
1542
|
+
assert(c0.expected === st.frozenParticipants.length, '★ consensus.expected === the frozen participant set size')
|
|
1543
|
+
assert(Number(String(rv0.voted).split('/')[1]) === c0.expected, '★ the two views agree on the DENOMINATOR (status().consensus.expected vs report().verify k/N)')
|
|
1544
|
+
assert(rv0.rosterVersion === c0.rosterVersion && c0.rosterVersion === st.rosterVersion, '★ both views (and status) report the SAME rosterVersion')
|
|
1545
|
+
// HIRE mid-verify: the newcomer must NOT join the frozen set (that is what freezing means).
|
|
1546
|
+
const hire = await h.callTool('vibe_v4_add_member', { direction: 'D4 cross-view assertion' })
|
|
1547
|
+
const st2 = await h.callTool('vibe_v4_status', {})
|
|
1548
|
+
const rv2 = (await h.callTool('vibe_v4_report', {})).verify
|
|
1549
|
+
assert(st2.rosterVersion > c0.rosterVersion, 'hiring mid-verify bumps rosterVersion (' + c0.rosterVersion + ' -> ' + st2.rosterVersion + ')')
|
|
1550
|
+
assert(st2.consensus.expected === st2.frozenParticipants.length && st2.consensus.expected === c0.expected,
|
|
1551
|
+
'★ after a mid-verify HIRE the frozen set is UNCHANGED and both views still agree (newcomer did not join)')
|
|
1552
|
+
assert(rv2.rosterVersion === st2.consensus.rosterVersion,
|
|
1553
|
+
'★ after the HIRE the two FROZEN views (status().consensus + report().verify) still report the SAME rosterVersion')
|
|
1554
|
+
assert(st2.rosterVersion > rv2.rosterVersion,
|
|
1555
|
+
'★ …while status().rosterVersion is the LIVE counter and has moved past the snapshot (staleness is visible)')
|
|
1556
|
+
// FIRE mid-verify: the frozen set is pruned so the in-flight verification can still conclude.
|
|
1557
|
+
if (hire && hire.ok && hire.id) await h.callTool('vibe_v4_remove_member', { id: hire.id })
|
|
1558
|
+
const st3 = await h.callTool('vibe_v4_status', {})
|
|
1559
|
+
const rv3 = (await h.callTool('vibe_v4_report', {})).verify
|
|
1560
|
+
assert(st3.consensus.expected === st3.frozenParticipants.length && Number(String(rv3.voted).split('/')[1]) === st3.consensus.expected && rv3.rosterVersion === st3.consensus.rosterVersion,
|
|
1561
|
+
'★ after a mid-verify FIRE the two views STILL agree (the frozen set was pruned together)')
|
|
1562
|
+
assert(st3.frozenParticipants.indexOf(hire && hire.id) === -1, '★ the removed member is not in the frozen set any more (pruned on removal)')
|
|
1563
|
+
// …and the verification really does conclude after that churn (T26/T31 contract).
|
|
1564
|
+
await settleVerify(h, () => 1)
|
|
1565
|
+
const st4 = await h.callTool('vibe_v4_status', {})
|
|
1566
|
+
assert(st4.verifyInProgress === false, '★ the mid-verify hire/fire did NOT prevent the verification from concluding')
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1482
1569
|
// ===============================================================
|
|
1483
1570
|
console.log('')
|
|
1484
1571
|
console.log('passed=' + passed + ' failed=' + failed)
|
|
@@ -38,6 +38,18 @@ export function makeFakeHost(opts = {}) {
|
|
|
38
38
|
exit: opts.exit === undefined ? 0 : opts.exit,
|
|
39
39
|
hang: !!opts.hang,
|
|
40
40
|
hangProbe: !!opts.hangProbe,
|
|
41
|
+
versionProbeFails: !!opts.versionProbeFails,
|
|
42
|
+
// round-7: the real host intermittently answers a spawn with NULL early in a session. The first
|
|
43
|
+
// `nullSpawnTimes` spawns return null so a test can prove the retry recovers from it.
|
|
44
|
+
nullSpawnTimes: Number(opts.nullSpawnTimes) || 0,
|
|
45
|
+
// round-7 (live root cause): the real host answers a spawn whose cwd does NOT exist with
|
|
46
|
+
// `spawned:true, exit:null` (retries cannot help). `projectRoot` can be pointed at a
|
|
47
|
+
// non-existent path to emulate a FRESH workspace.
|
|
48
|
+
projectRoot: opts.projectRoot || '/fake/project',
|
|
49
|
+
freshRoot: opts.projectRoot || null,
|
|
50
|
+
// round-7: null answers ONLY for package probes (classify === 'packages'), so a test can isolate
|
|
51
|
+
// "the precheck could not run" from "the engine could not be probed".
|
|
52
|
+
packageProbeNullTimes: Number(opts.packageProbeNullTimes) || 0,
|
|
41
53
|
stdoutBytes: opts.stdoutBytes || 0,
|
|
42
54
|
argError: opts.argError || null,
|
|
43
55
|
probeCalls: 0,
|
|
@@ -56,9 +68,9 @@ export function makeFakeHost(opts = {}) {
|
|
|
56
68
|
state.registrations.push({ name, description, parameters, handler })
|
|
57
69
|
},
|
|
58
70
|
params: () => (typeof opts.params === 'function' ? opts.params() : (opts.params || {})),
|
|
59
|
-
projectRoot: () => opts.root || FAKE_ROOT,
|
|
71
|
+
projectRoot: () => state.freshRoot || opts.root || FAKE_ROOT,
|
|
60
72
|
designator: opts.designator || 'vibe-math-v2',
|
|
61
|
-
writeText: async (rel, text) => { files.set(String(rel).replace(/\\/g, '/'), String(text)); return true },
|
|
73
|
+
writeText: async (rel, text) => { files.set(String(rel).replace(/\\/g, '/'), String(text)); state.rootCreated = true; return true },
|
|
62
74
|
// Optional capability flag (INTERFACE-FREEZE §4): a host that knows it has no subprocess service
|
|
63
75
|
// says so, and the module reports MATH_NO_SUBPROCESS instead of a misleading ENGINE_NOT_FOUND.
|
|
64
76
|
hasSubprocess: ('hasSubprocess' in opts) ? (() => opts.hasSubprocess) : undefined,
|
|
@@ -72,10 +84,18 @@ export function makeFakeHost(opts = {}) {
|
|
|
72
84
|
if (typeof opts.listDir === 'function') return opts.listDir(rel)
|
|
73
85
|
return (opts.listDir || []).slice()
|
|
74
86
|
}) : undefined,
|
|
87
|
+
// round-7 (fix 2): OPTIONAL bundled-runtime discovery. `runtimeRoots` are absolute roots and
|
|
88
|
+
// `absTree` maps an absolute directory to its entries, so a test can fake DSH's layout
|
|
89
|
+
// (`<root>/dsh-runtimes/<tree>/dependencies/python/python.exe`).
|
|
90
|
+
runtimeRoots: ('runtimeRoots' in opts) ? (async () => (opts.runtimeRoots || [])) : undefined,
|
|
91
|
+
listDirAbs: opts.absTree ? (async (abs) => (opts.absTree[String(abs).replace(/\\/g, '/')] || [])) : undefined,
|
|
75
92
|
resolveExecutable: async (cmd) => {
|
|
76
93
|
if (opts.resolveThrows) throw new Error('resolve failed: ' + cmd)
|
|
77
94
|
const raw = String(cmd)
|
|
78
95
|
const base = raw.split(/[\\/]/).pop()
|
|
96
|
+
// round-6 (B): an explicit path map lets a test place the interpreter in a conda-style
|
|
97
|
+
// directory or next to a `uv` binary - the manager dispatch reads those facts.
|
|
98
|
+
if (opts.resolveMap && Object.prototype.hasOwnProperty.call(opts.resolveMap, base)) return opts.resolveMap[base]
|
|
79
99
|
// A caller-supplied path (engine='cli') is accepted as-is: that is the whole point of the
|
|
80
100
|
// escape hatch - the tool only has to resolve the command, not know the engine.
|
|
81
101
|
if (raw.indexOf('/') !== -1 || raw.indexOf('\\') !== -1) return raw
|
|
@@ -85,10 +105,18 @@ export function makeFakeHost(opts = {}) {
|
|
|
85
105
|
return '/fake/bin/' + base
|
|
86
106
|
},
|
|
87
107
|
spawn: async ({ argv, cwd, timeoutMs, stdoutCap, stderrCap }) => {
|
|
108
|
+
if (state.nullSpawnTimes > 0) { state.nullSpawnTimes--; state.spawns.push({ argv: argv.slice(), cwd, timeoutMs, stdoutCap, stderrCap, nullSpawn: true }); return null }
|
|
88
109
|
state.spawns.push({ argv: argv.slice(), cwd, timeoutMs, stdoutCap, stderrCap })
|
|
110
|
+
// Emulate the real host: a spawn whose cwd does not exist answers with exit:null. Note the
|
|
111
|
+
// host's writeText creates parent directories (writeFileAtomic), so a RUN (which writes the
|
|
112
|
+
// receipt first) legitimately makes the project root exist before it spawns.
|
|
113
|
+
if (state.freshRoot && !state.rootCreated && String(cwd) === String(state.freshRoot)) return { exit: null, timedOut: false, killed: false, ms: 130, stdout: '', stderr: '' }
|
|
89
114
|
const kind = classify(argv)
|
|
115
|
+
if (kind === 'packages' && state.packageProbeNullTimes > 0) { state.packageProbeNullTimes--; state.spawns[state.spawns.length - 1].nullSpawn = true; return null }
|
|
90
116
|
if (kind === 'version') {
|
|
91
117
|
if (state.hangProbe) return { exit: null, timedOut: true, killed: true, ms: timeoutMs, stdout: '', stderr: '' }
|
|
118
|
+
// round-7: a DETERMINISTIC probe failure (engine resolved, probe exits non-zero, no version).
|
|
119
|
+
if (state.versionProbeFails) return { exit: 1, timedOut: false, killed: false, ms: 5, stdout: '', stderr: 'probe boom' }
|
|
92
120
|
const exeName = String(argv[0] || 'engine').split(/[\\/]/).pop()
|
|
93
121
|
return { exit: 0, timedOut: false, killed: false, ms: 5, stdout: exeName + ' ' + state.version + '\n', stderr: '' }
|
|
94
122
|
}
|