dsh-vibe-math 2.4.1 → 2.6.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 (46) hide show
  1. package/README.en.md +53 -14
  2. package/README.md +54 -14
  3. package/cordis.patch.yml +54 -17
  4. package/docs/AUDIT-CHECKLIST.md +26 -7
  5. package/docs/COMPAT-AUDIT-ROUND3.md +16 -1
  6. package/docs/final-paper.md +171 -0
  7. package/docs/release-notes/RELEASE-NOTES-2.5.0.md +173 -0
  8. package/docs/release-notes/RELEASE-NOTES-2.6.0.md +268 -0
  9. package/docs/test-timing.md +77 -40
  10. package/installer.js +225 -45
  11. package/package.json +6 -3
  12. package/preset-declaration.js +38 -7
  13. package/prompt-corpus-persona/persona-corpus.json +13 -13
  14. package/prompt-corpus-persona/persona-corpus.md +113 -39
  15. package/prompt-corpus-v2/formal-verify-v2.json +128 -92
  16. package/prompt-corpus-v2/formal-verify-v2.md +380 -142
  17. package/prompt-corpus-v3/formal-verify-v3.json +117 -81
  18. package/prompt-corpus-v3/formal-verify-v3.md +693 -1189
  19. package/prompt-corpus-v4/formal-verify-v4.json +38 -8
  20. package/prompt-corpus-v4/formal-verify-v4.md +157 -25
  21. package/prompt-corpus-v5/prompt-corpus-v5.json +17 -17
  22. package/prompt-corpus-v5/prompt-corpus-v5.md +32 -26
  23. package/tests/audit-installer-compat.test.mjs +70 -4
  24. package/tests/audit-installer-policy.test.mjs +229 -4
  25. package/tests/audit-persona-sensitivity.mjs +68 -8
  26. package/tests/audit-persona-surface.test.mjs +21 -8
  27. package/tests/audit-v5-sensitivity.mjs +47 -6
  28. package/tests/e2e-v5-round2.test.mjs +825 -1
  29. package/tests/formal-verify-v2.test.mjs +57 -2
  30. package/tests/formal-verify-v3.test.mjs +67 -2
  31. package/tests/formal-verify-v4.test.mjs +120 -4
  32. package/tests/prompt-v5-integrity.test.mjs +9 -3
  33. package/tests/run-tests.mjs +104 -23
  34. package/tests/selfdrive-v5.mjs +55 -4
  35. package/vibe-math-v2/agent.cordis.yml +20 -4
  36. package/vibe-math-v2/vibe-math-v2.js +1318 -106
  37. package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +20 -4
  38. package/vibe-math-v3/agent.cordis.yml +36 -14
  39. package/vibe-math-v3/vibe-math-v3.js +1348 -179
  40. package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +3 -3
  41. package/vibe-math-v4/agent.cordis.yml +36 -8
  42. package/vibe-math-v4/vibe-math-v4.js +1195 -57
  43. package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +91 -9
  44. package/vibe-math-v5/agent.cordis.yml +16 -8
  45. package/vibe-math-v5/vibe-math-v5.js +1628 -112
  46. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +143 -41
@@ -107,8 +107,10 @@ const probes = [
107
107
  name: 'v3-tool-registered-but-not-documented',
108
108
  preset: 'vibe-math-v3', file: 'vibe-math-v3.js', nth: 'first', expect: 2,
109
109
  guarantee: 'a newly registered tool must be documented in the persona (or the snapshot updated on purpose)',
110
- from: " registerTool('vibe_math_lean_run', '(member) Execute the Lean toolchain",
111
- to: " registerTool('vibe_math_extra_tool', 'x', objParams({}), async function () { return {} })\n registerTool('vibe_math_lean_run', '(member) Execute the Lean toolchain",
110
+ // NOTE: the description argument is now `TOOL_DESC.<name>` (v3 audit of the two
111
+ // registration paths) — the anchor follows the call shape, not the literal text.
112
+ from: " registerTool('vibe_math_lean_run', TOOL_DESC.vibe_math_lean_run,",
113
+ to: " registerTool('vibe_math_extra_tool', 'x', objParams({}), async function () { return {} })\n registerTool('vibe_math_lean_run', TOOL_DESC.vibe_math_lean_run,",
112
114
  },
113
115
 
114
116
  // ── v4 ─────────────────────────────────────────────────────────────────────
@@ -146,22 +148,80 @@ const probes = [
146
148
  name: 'v4-usage-advertises-phantom-subcommand',
147
149
  preset: 'vibe-math-v4', file: 'vibe-math-v4.js', nth: 'first', expect: 1,
148
150
  guarantee: 'the unknown-subcommand usage string must not advertise a subcommand that no branch implements',
149
- from: "usage:'configure|start|resume|pause|abort|status|report|message <to|all> <content>|meeting|members|add|remove|set'",
150
- to: "usage:'configure|start|resume|pause|abort|status|report|message <to|all> <content>|meeting|members|add|remove|scan|set'",
151
+ // The anchor must be the CURRENT literal. Round A changed both the usage string and the
152
+ // hint to advertise `start [problem]` / `set <k=v>...` (vibe-math-v4.js:2492). While the
153
+ // anchor kept the pre-fix literal it matched 0 times, so this probe reported SETUP-FAIL
154
+ // every run and the F-section agreement between usage and branches had NO sensitivity
155
+ // proof at all (the hint probe at :155 was updated, this one was not).
156
+ // Round H (final paper) inserted `paper [...]` into both the usage and the hint; the anchor
157
+ // follows the new literal again, so the probe keeps proving the usage<->branches agreement.
158
+ from: "usage:'configure|start [problem]|resume|pause|abort|status|report|message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|set <k=v>...'",
159
+ to: "usage:'configure|start [problem]|resume|pause|abort|status|report|message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|scan|set <k=v>...'",
151
160
  },
152
161
  {
153
162
  name: 'v4-hint-advertises-unimplemented-subcommand',
154
163
  preset: 'vibe-math-v4', file: 'vibe-math-v4.js', nth: 'first', expect: 1,
155
164
  guarantee: 'the typing hint must not advertise a subcommand the handler does not implement',
156
- from: "message <to|all> <content>|meeting|members|add|remove|set]'",
157
- to: "message <to|all> <content>|meeting|add|remove|set]'",
165
+ // Same reason as above: the hint now carries the `paper [...]` entry between `meeting` and
166
+ // `members`, so the anchor names the current literal and drops an IMPLEMENTED subcommand
167
+ // (`members`) from the mutated hint — the suite's implemented-subset-of-hint check must fail.
168
+ from: "message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|set]'",
169
+ to: "message <to|all> <content>|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|add|remove|set]'",
158
170
  },
159
171
  {
160
172
  name: 'v5-persona-slash-list-drops-add-remove',
161
173
  preset: 'vibe-math-v5', file: 'agent.cordis.yml', nth: 'all', expect: 2,
162
174
  guarantee: "the persona's /v5 list must match the command hint (a human reading the persona must see every subcommand)",
163
- from: 'meeting|hire|fire|add|remove|set)',
164
- to: 'meeting|hire|fire|set)',
175
+ // Round H appended `|paper` to the persona's /v5 list (and to the command hint), so the
176
+ // anchor carries the new tail; the mutation still drops the two subcommands whose absence
177
+ // the Round-G persona actually had (`add|remove`), which the hint<->persona equality check
178
+ // must catch.
179
+ from: 'meeting|hire|fire|add|remove|set|paper)',
180
+ to: 'meeting|hire|fire|set|paper)',
181
+ },
182
+ // ── final-paper surface (Round H) ──────────────────────────────────────────
183
+ // These two pin the surface the final-paper feature ADDED, so a later edit cannot quietly
184
+ // drop it: the v4 hint must keep advertising the subcommand its handler implements, and the
185
+ // v5 persona must keep naming the tools the plugin actually registers.
186
+ {
187
+ name: 'v4-hint-stops-advertising-paper',
188
+ preset: 'vibe-math-v4', file: 'vibe-math-v4.js', nth: 'first', expect: 1,
189
+ guarantee: 'the /v4 hint must keep advertising the final-paper subcommand the handler implements',
190
+ from: "|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members|add|remove|set]'",
191
+ to: "|meeting|members|add|remove|set]'",
192
+ },
193
+ {
194
+ name: 'v5-paper-tool-renamed-in-plugin',
195
+ preset: 'vibe-math-v5', file: 'vibe-math-v5.js', nth: 'first', expect: 1,
196
+ guarantee: 'renaming the final-paper tool must be caught (the persona still advertises vibe_v5_paper)',
197
+ from: " registerTool('vibe_v5_paper',",
198
+ to: " registerTool('vibe_v5_paperX',",
199
+ },
200
+ // ── the persona↔hint list itself (the gap that hid `paper` in v2/v3/v4) ────────────────────
201
+ // The surface suite used to SKIP this comparison whenever the persona list contained `...`
202
+ // (v3's trailing ellipsis) or an argument placeholder with `...` (v4's `set <k=v>...`), and v2
203
+ // never enumerated at all. Each probe below drops ONE implemented subcommand from the persona
204
+ // list and requires the suite to go RED, so a future omission cannot hide behind an ellipsis.
205
+ {
206
+ name: 'v2-persona-slash-list-drops-paper',
207
+ preset: 'vibe-math-v2', file: 'agent.cordis.yml', nth: 'all', expect: 2,
208
+ guarantee: 'the v2 persona must name EVERY implemented /vibe subcommand explicitly (it never enumerated before)',
209
+ from: '|decisions|agents|paper [lang=zh|en] [format=both|md|tex] [force]).',
210
+ to: '|decisions|agents).',
211
+ },
212
+ {
213
+ name: 'v3-persona-slash-list-drops-paper',
214
+ preset: 'vibe-math-v3', file: 'agent.cordis.yml', nth: 'all', expect: 2,
215
+ guarantee: "the v3 persona's /vibe list must name every implemented subcommand (the old trailing '...' skipped the check)",
216
+ from: '|decisions|agents|paper [lang=zh|en] [format=both|md|tex] [force]).',
217
+ to: '|decisions|agents).',
218
+ },
219
+ {
220
+ name: 'v4-persona-slash-list-drops-paper',
221
+ preset: 'vibe-math-v4', file: 'agent.cordis.yml', nth: 'all', expect: 2,
222
+ guarantee: "a dropped v4 persona entry must be caught even though the list contains `set <k=v>...` (the old ellipsis escape)",
223
+ from: '|meeting|paper [lang=zh|en] [format=md|tex|both] [editor=office|resident:<id>] [force]|members',
224
+ to: '|meeting|members',
165
225
  },
166
226
  ]
167
227
 
@@ -143,7 +143,11 @@ const PRESETS = [
143
143
  js: 'vibe-math-v3.js',
144
144
  prefix: 'vibe_math_',
145
145
  tools: 33,
146
- undocumented: ['vibe_math_claim_write', 'vibe_math_release_write', 'vibe_math_sync_meta'],
146
+ // `vibe_math_sync_meta` is no longer undocumented: the promotion-contract line of the
147
+ // persona now names it as the place to report `lemmas[].价值/关键性` (v3 audit M1 —
148
+ // without a prompt-side source for that field the promotion main line is unreachable).
149
+ // The write-lock pair stays out: solver/method-keeper prompts carry it via kcWriteRules().
150
+ undocumented: ['vibe_math_claim_write', 'vibe_math_release_write'],
147
151
  lean: { tools: ['vibe_math_lean_run', 'vibe_math_lean_archive', 'vibe_math_lean_lib'], extra: [] },
148
152
  },
149
153
  {
@@ -166,9 +170,11 @@ const PRESETS = [
166
170
  dir: 'vibe-math-v5',
167
171
  js: 'vibe-math-v5.js',
168
172
  prefix: 'vibe_v5_',
169
- tools: 35,
173
+ tools: 37,
170
174
  // member- and academician-facing tools; the office (main agent) holds only the
171
- // institute-level controls plus the hiring authority.
175
+ // institute-level controls plus the hiring authority. The final-paper pair is an
176
+ // OFFICE control and IS named in the persona (vibe_v5_paper / vibe_v5_finalize_paper),
177
+ // so it must not appear in this snapshot.
172
178
  undocumented: [
173
179
  'vibe_v5_wait', 'vibe_v5_record_progress', 'vibe_v5_record_proposition',
174
180
  'vibe_v5_record_method', 'vibe_v5_record_subproblem', 'vibe_v5_read_library',
@@ -254,12 +260,19 @@ for (const P of PRESETS) {
254
260
  if (!hintOpen) eq(implemented.filter((c) => !hinted.includes(c)).sort(), [], `${P.dir}: implemented slash subcommand(s) are missing from the hint`)
255
261
  eq(used.sort(), implemented, `${P.dir}: the usage string and the implemented slash subcommands disagree`)
256
262
  const personaEnum = /slash command mirrors[^(]*\(([\s\S]*?)\)/.exec(blocks.prefix.join('\n') + '\n' + blocks.text.join('\n'))
257
- if (personaEnum && personaEnum[1].includes('|')) {
263
+ // An ellipsis must NOT switch this check off (that is how a dropped `paper` entry survived here),
264
+ // and `...` inside an argument placeholder (`set <k=v>...`) is not an abbreviation at all. So the
265
+ // persona is compared against the BRANCHES (the ground truth), and every implemented subcommand
266
+ // must be named EXPLICITLY. Only a HINT that is itself abbreviated makes the reverse direction
267
+ // (persona ⊆ hint) unprovable, so only that direction is relaxed then.
268
+ if (!personaEnum) {
269
+ ok(false, `${P.dir}: the persona never enumerates its /vN subcommands (list them so a human can see every one)`)
270
+ } else {
258
271
  const personaCmds = cmdsFrom(personaEnum[1].replace(/^\s*\/[a-z0-9]+\s+/, ''))
259
- if (personaCmds.length) {
260
- eq(personaCmds.filter((c) => !hinted.includes(c)).sort(), [], `${P.dir}: the persona enumerates slash subcommand(s) the hint does not know`)
261
- if (!/\.\.\./.test(personaEnum[1]) && !hintOpen) eq(personaCmds.slice().sort(), hinted.slice().sort(), `${P.dir}: the persona's /vN list and the command hint disagree`)
262
- }
272
+ ok(personaCmds.length > 0, `${P.dir}: no slash subcommand could be parsed from the persona's /vN list`)
273
+ eq(personaCmds.filter((c) => !implemented.includes(c)).sort(), [], `${P.dir}: the persona enumerates slash subcommand(s) the handler does not implement`)
274
+ eq(implemented.filter((c) => !personaCmds.includes(c)).sort(), [], `${P.dir}: the persona's /vN list omits subcommand(s) the handler implements — list them explicitly, do not hide behind "..."`)
275
+ if (!hintOpen) eq(personaCmds.slice().sort(), implemented.slice().sort(), `${P.dir}: the persona's /vN list and the command hint disagree`)
263
276
  }
264
277
 
265
278
  // ---- E. the Lean feature's prompt surface ----------------------------
@@ -31,6 +31,31 @@ const original = readFileSync(SRC, 'utf8')
31
31
  const REPO = fileURLToPath(new URL('..', import.meta.url))
32
32
  const dir = mkdtempSync(join(tmpdir(), 'v5-sens-'))
33
33
 
34
+ // `armNextVerify`'s two exclusion gates (a live MEETING and a live VERIFICATION), each written
35
+ // at the function head AND as a re-check inside the `while` loop with no `await` in between.
36
+ // `armGuards(true, true)` is the pristine source text; the probes switch ONE gate off in BOTH
37
+ // copies, which is the minimal edit that actually breaks the guarantee. Keeping the block in
38
+ // one place means the two mutations cannot drift apart from the source they mutate.
39
+ const armGuards = (meetingOn, verifyOn) => [
40
+ ...(meetingOn ? [' if (meeting) return'] : []),
41
+ ' // Only one begin may be in flight. Without this, two callers (a scheduling pass',
42
+ ' // and a fresh proposal) could both pass the `currentVerify()` check before either',
43
+ ' // has published its verdict record and would start the SAME object twice.',
44
+ ' if (beginLock) return',
45
+ ...(verifyOn ? [' if (currentVerify()) return'] : []),
46
+ ' beginLock = true',
47
+ ' try {',
48
+ ' while (true) {',
49
+ ' // NEVER remove a proposal we are not about to run: the entry leaves the durable',
50
+ ' // queue only in the same step that starts it. The old code shifted it out and then',
51
+ ' // returned because a meeting was live, so the proposal existed in neither the queue',
52
+ ' // nor `verdicts` until some later pass happened to re-arm it (audit M2). Re-check',
53
+ ' // the exclusion guards here too: `await putQueue` can let a meeting open.',
54
+ ' const q = inst().queue.slice()',
55
+ ' if (!q.length) return',
56
+ ' if (' + (meetingOn && verifyOn ? 'meeting || currentVerify()' : meetingOn ? 'meeting' : 'currentVerify()') + ') return',
57
+ ].join('\n')
58
+
34
59
  // Each probe: { name, ref, guarantee, from, to }
35
60
  // `from` must occur exactly once, so a mutation can never quietly hit the wrong site.
36
61
  const probes = [
@@ -141,9 +166,19 @@ const probes = [
141
166
  name: 'begin-not-exclusive',
142
167
  ref: 'prompt-v5-integrity.test.mjs',
143
168
  guarantee: '⑱ a second proposal must QUEUE, never start a concurrent ballot',
144
- from: " if (beginLock) return\n if (currentVerify()) return\n beginLock = true",
145
- to: " beginLock = true",
146
- },
169
+ from: armGuards(true, true),
170
+ to: armGuards(true, false),
171
+ },
172
+ // NOTE — the mutual-exclusion gates of `armNextVerify` are written TWICE: once at the
173
+ // function head and once as a re-check inside the `while (true)` loop, and there is NO
174
+ // `await` between them. The head copies are therefore redundant mirrors of the in-loop
175
+ // ones, and a probe that deletes only a head copy is SEMANTICALLY INERT — it can never go
176
+ // red, which would masquerade as a "blind spot" forever (this was the state of both of the
177
+ // ⑱/㉝ probes: the in-loop re-check kept the invariant alive and
178
+ // `prompt-v5-integrity.test.mjs` stayed green). The probes for ⑱ and ㉝ therefore remove
179
+ // BOTH copies of the ONE gate they test, which is the minimal edit that actually lets a
180
+ // second ballot start — and the suite's case 9b ("a second proposal QUEUES") and case 10b
181
+ // ("a verification proposed DURING a meeting must queue") do detect exactly that.
147
182
  // NOTE — a probe for `continueMeetingRound`'s `if (finalizeLock) { armHeartbeat(); return }`
148
183
  // re-arm was REMOVED, not because the re-arm is unnecessary but because the state it
149
184
  // guards is UNREACHABLE, so no black-box probe can detect its removal:
@@ -269,8 +304,10 @@ const probes = [
269
304
  name: 'verify-preempts-a-live-meeting',
270
305
  ref: 'prompt-v5-integrity.test.mjs',
271
306
  guarantee: '㉝ a verification proposed during a meeting must QUEUE, never start concurrently (the two are mutually exclusive)',
272
- from: " if (meeting) return\n // Only one begin may be in flight.",
273
- to: " // Only one begin may be in flight.",
307
+ // Removes BOTH `meeting` gates (see the note above `meeting-never-finalized`): deleting
308
+ // only the head copy is inert because the in-loop re-check runs before any `await`.
309
+ from: armGuards(true, true),
310
+ to: armGuards(false, true),
274
311
  },
275
312
 
276
313
  // ── LEAN FORMAL VERIFICATION PROBES (docs/formal-verification.md) ─────────
@@ -362,7 +399,11 @@ for (const p of probes) {
362
399
  continue
363
400
  }
364
401
  const r = spawnSync(process.execPath, [testPath], {
365
- env: Object.assign({}, process.env, { V5_PLUGIN: file }),
402
+ // V5_CORPUS_DIR: a MUTATED run must never rewrite the shipped `prompt-corpus-v5/`. The
403
+ // corpus is the mandatory human-review artifact (AUDIT-CHECKLIST §0.3/§2.4); letting a
404
+ // deliberately broken copy dump its prompts there would leave the working tree holding
405
+ // mutant text until someone re-ran the healthy suite.
406
+ env: Object.assign({}, process.env, { V5_PLUGIN: file, V5_CORPUS_DIR: join(dir, 'corpus') }),
366
407
  encoding: 'utf8',
367
408
  cwd: REPO,
368
409
  })