dsh-vibe-math 2.3.0 → 2.3.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/AUDIT-CHECKLIST.md +78 -3
- package/README.md +33 -2
- package/RELEASE-NOTES-2.3.1.md +134 -0
- package/RELEASE-NOTES-2.3.2.md +145 -0
- package/audit-formal-sensitivity.mjs +134 -39
- package/audit-prompt-invariants.mjs +414 -0
- package/audit-spec-traceability.mjs +173 -0
- package/audit-v5-integrity.mjs +5 -3
- package/docs/formal-verification.md +122 -19
- package/docs/generate_framework_diagram_v5.mjs +2 -1
- package/docs/test-timing.md +101 -0
- package/formal-verify-v2.test.mjs +526 -7
- package/formal-verify-v3.test.mjs +389 -10
- package/formal-verify-v4.test.mjs +462 -4
- package/formal-verify-v5.test.mjs +163 -4
- package/installer.js +3 -1
- package/package.json +12 -2
- package/prompt-corpus-persona/persona-corpus.json +2 -2
- package/prompt-corpus-persona/persona-corpus.md +6 -2
- package/prompt-corpus-v2/formal-verify-v2.json +484 -0
- package/prompt-corpus-v2/formal-verify-v2.md +5239 -0
- package/prompt-corpus-v3/formal-verify-v3.json +274 -100
- package/prompt-corpus-v3/formal-verify-v3.md +2057 -335
- package/prompt-corpus-v4/formal-verify-v4.json +89 -0
- package/prompt-corpus-v4/formal-verify-v4.md +283 -0
- package/prompt-corpus-v5/prompt-corpus-v5.json +186 -219
- package/prompt-corpus-v5/prompt-corpus-v5.md +485 -700
- package/prompt-v5-integrity.test.mjs +1272 -1085
- package/run-tests.mjs +118 -0
- package/vibe-math-v2/vibe-math-v2.js +341 -45
- package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +129 -8
- package/vibe-math-v3/vibe-math-v3.js +162 -36
- package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +21 -3
- package/vibe-math-v4/vibe-math-v4.js +201 -30
- package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +54 -2
- package/vibe-math-v5/agent.cordis.yml +6 -2
- package/vibe-math-v5/vibe-math-v5.js +133 -28
- package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +55 -5
- package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +16 -2
- package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5.svg +6 -5
package/run-tests.mjs
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PARALLEL TEST RUNNER — run every shipped suite (or a filtered subset) concurrently and report
|
|
4
|
+
* per-suite timings, so the strategy for the next run is chosen from DATA instead of guesswork.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: the suites are wildly uneven (one suite is ~160 s, most are under 2 s), so a
|
|
7
|
+
* sequential sweep spends almost all of its wall time waiting for the slowest one. Running them
|
|
8
|
+
* with a worker pool makes the sweep bounded by the slowest SUITE rather than by their SUM.
|
|
9
|
+
* On this machine (4 cores) the sweep went from ~5.5 min to ~2 min; see docs/test-timing.md.
|
|
10
|
+
*
|
|
11
|
+
* Every suite already isolates itself (each creates its own mkdtemp workspace), so parallelism is
|
|
12
|
+
* safe. Suites that WRITE a corpus take a per-run corpus dir from an env var; this runner points
|
|
13
|
+
* them at a scratch dir when it runs a suite more than once, which it never does — but the probe
|
|
14
|
+
* runner (audit-formal-sensitivity.mjs) does, and it passes its own dirs.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* node run-tests.mjs # every *.test.mjs, concurrency = min(4, cpus)
|
|
18
|
+
* node run-tests.mjs --concurrency=6
|
|
19
|
+
* node run-tests.mjs --only formal # substring match on the file name (repeatable, OR)
|
|
20
|
+
* node run-tests.mjs --exclude e2e-v4 # substring to skip (repeatable)
|
|
21
|
+
* node run-tests.mjs --json # machine-readable summary on stdout
|
|
22
|
+
*/
|
|
23
|
+
import { spawn } from 'node:child_process'
|
|
24
|
+
import { readdirSync } from 'node:fs'
|
|
25
|
+
import { cpus } from 'node:os'
|
|
26
|
+
import { join } from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
|
|
29
|
+
const HERE = fileURLToPath(new URL('./', import.meta.url))
|
|
30
|
+
const argv = process.argv.slice(2)
|
|
31
|
+
// Accept BOTH `--only=x` and `--only x` (the help text used the space form, which a value-taking
|
|
32
|
+
// flag() did not understand — the filter silently did nothing and every suite still ran).
|
|
33
|
+
const flag = (name) => {
|
|
34
|
+
const out = []
|
|
35
|
+
for (let i = 0; i < argv.length; i++) {
|
|
36
|
+
const a = argv[i]
|
|
37
|
+
if (a === '--' + name && i + 1 < argv.length && !argv[i + 1].startsWith('--')) out.push(argv[++i])
|
|
38
|
+
else if (a.startsWith('--' + name + '=')) out.push(a.split('=').slice(1).join('='))
|
|
39
|
+
}
|
|
40
|
+
return out
|
|
41
|
+
}
|
|
42
|
+
const has = (name) => argv.includes('--' + name)
|
|
43
|
+
const only = flag('only')
|
|
44
|
+
const exclude = flag('exclude')
|
|
45
|
+
const asJson = has('json')
|
|
46
|
+
const concurrency = Math.max(1, Number(flag('concurrency')[0] || Math.min(4, cpus().length)))
|
|
47
|
+
|
|
48
|
+
let suites = readdirSync(HERE).filter((f) => f.endsWith('.test.mjs')).sort()
|
|
49
|
+
if (only.length) suites = suites.filter((f) => only.some((o) => f.includes(o)))
|
|
50
|
+
if (exclude.length) suites = suites.filter((f) => !exclude.some((o) => f.includes(o)))
|
|
51
|
+
if (!suites.length) { console.error('no suites matched'); process.exit(2) }
|
|
52
|
+
|
|
53
|
+
function runSuite(file) {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const t0 = Date.now()
|
|
56
|
+
const child = spawn(process.execPath, [file], { cwd: HERE, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
57
|
+
let out = '', err = ''
|
|
58
|
+
child.stdout.on('data', (d) => { out += d.toString() })
|
|
59
|
+
child.stderr.on('data', (d) => { err += d.toString() })
|
|
60
|
+
child.on('error', (e) => resolve({ file, code: -1, ms: Date.now() - t0, out, err: err + '\n' + String(e) }))
|
|
61
|
+
child.on('close', (code) => resolve({ file, code, ms: Date.now() - t0, out, err }))
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const results = []
|
|
66
|
+
let cursor = 0
|
|
67
|
+
const started = Date.now()
|
|
68
|
+
async function worker(id) {
|
|
69
|
+
for (;;) {
|
|
70
|
+
const i = cursor++
|
|
71
|
+
if (i >= suites.length) return
|
|
72
|
+
const r = await runSuite(suites[i])
|
|
73
|
+
const tail = String(r.out).trim().split('\n').filter(Boolean).slice(-1)[0] || ''
|
|
74
|
+
results[i] = r
|
|
75
|
+
if (!asJson) {
|
|
76
|
+
const mark = r.code === 0 ? 'PASS' : 'FAIL'
|
|
77
|
+
console.log(
|
|
78
|
+
mark + ' ' + r.file.padEnd(38) +
|
|
79
|
+
' exit=' + String(r.code).padStart(3) +
|
|
80
|
+
' ' + (r.ms / 1000).toFixed(1).padStart(6) + 's' +
|
|
81
|
+
(tail ? ' ' + tail.slice(0, 78) : '')
|
|
82
|
+
)
|
|
83
|
+
if (r.code !== 0) {
|
|
84
|
+
const lines = (r.out + '\n' + r.err).split('\n').filter(Boolean)
|
|
85
|
+
for (const l of lines.slice(-15)) console.log(' ' + l)
|
|
86
|
+
}
|
|
87
|
+
} else if (r.code !== 0) {
|
|
88
|
+
// In --json mode keep stdout machine-readable: the failure detail travels in the JSON.
|
|
89
|
+
const lines = (r.out + '\n' + r.err).split('\n').filter(Boolean)
|
|
90
|
+
r.tailDetail = lines.slice(-15).join('\n')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, suites.length) }, (_, i) => worker(i)))
|
|
95
|
+
|
|
96
|
+
const wall = (Date.now() - started) / 1000
|
|
97
|
+
const sum = results.reduce((a, r) => a + r.ms, 0) / 1000
|
|
98
|
+
const bad = results.filter((r) => r.code !== 0)
|
|
99
|
+
const slowest = results.slice().sort((a, b) => b.ms - a.ms).slice(0, 5)
|
|
100
|
+
|
|
101
|
+
if (asJson) {
|
|
102
|
+
console.log(JSON.stringify({
|
|
103
|
+
concurrency, wallSeconds: Number(wall.toFixed(1)), sumSeconds: Number(sum.toFixed(1)),
|
|
104
|
+
pass: results.length - bad.length, fail: bad.length,
|
|
105
|
+
suites: results.map((r) => ({
|
|
106
|
+
file: r.file, exit: r.code, seconds: Number((r.ms / 1000).toFixed(1)),
|
|
107
|
+
...(r.tailDetail ? { detail: r.tailDetail } : {}),
|
|
108
|
+
})),
|
|
109
|
+
}, null, 2))
|
|
110
|
+
} else {
|
|
111
|
+
console.log('')
|
|
112
|
+
console.log('concurrency ' + concurrency + ' · wall ' + wall.toFixed(1) + 's · sum of suite times ' + sum.toFixed(1) + 's'
|
|
113
|
+
+ ' · speed-up x' + (sum / Math.max(wall, 0.001)).toFixed(2))
|
|
114
|
+
console.log('slowest: ' + slowest.map((r) => r.file.replace('.test.mjs', '') + ' ' + (r.ms / 1000).toFixed(1) + 's').join(' · '))
|
|
115
|
+
console.log('TOTAL ' + results.length + ' PASS ' + (results.length - bad.length) + ' FAIL ' + bad.length)
|
|
116
|
+
for (const b of bad) console.log(' FAILED: ' + b.file + ' (exit ' + b.code + ')')
|
|
117
|
+
}
|
|
118
|
+
process.exit(bad.length === 0 ? 0 : 1)
|