dsh-vibe-math 2.5.0 → 2.7.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.
- package/README.en.md +175 -369
- package/README.md +175 -370
- package/cordis.patch.yml +78 -22
- package/docs/AUDIT-CHECKLIST.md +3 -3
- package/docs/final-paper.md +172 -0
- package/docs/formal-verification.md +184 -29
- package/docs/math-computation.md +121 -0
- package/docs/release-notes/RELEASE-NOTES-2.6.0.md +268 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.0.en.md +151 -0
- package/docs/release-notes/RELEASE-NOTES-2.7.0.md +115 -0
- package/docs/test-timing.md +25 -9
- package/installer.js +7 -4
- package/package.json +21 -3
- package/prompt-corpus-persona/persona-corpus.json +16 -16
- package/prompt-corpus-persona/persona-corpus.md +164 -52
- package/prompt-corpus-v2/formal-verify-v2.json +128 -92
- package/prompt-corpus-v2/formal-verify-v2.md +1574 -108
- package/prompt-corpus-v3/formal-verify-v3.json +137 -53
- package/prompt-corpus-v3/formal-verify-v3.md +1406 -100
- package/prompt-corpus-v4/formal-verify-v4.json +14 -14
- package/prompt-corpus-v4/formal-verify-v4.md +247 -14
- package/prompt-corpus-v5/prompt-corpus-v5.json +62 -62
- package/prompt-corpus-v5/prompt-corpus-v5.md +570 -3
- package/tests/audit-math-computation-contract.mjs +191 -0
- package/tests/audit-math-computation-parity.mjs +169 -0
- package/tests/audit-math-computation-sensitivity.mjs +231 -0
- package/tests/audit-persona-sensitivity.mjs +64 -11
- package/tests/audit-persona-surface.test.mjs +53 -16
- package/tests/audit-v5-integrity.mjs +14 -0
- package/tests/e2e-v5-round2.test.mjs +923 -3
- package/tests/formal-verify-v2.test.mjs +295 -6
- package/tests/formal-verify-v3.test.mjs +275 -4
- package/tests/formal-verify-v4.test.mjs +295 -6
- package/tests/formal-verify-v5.test.mjs +9 -1
- package/tests/helpers/math-computation-fake-seam.mjs +135 -0
- package/tests/math-computation-shared.test.mjs +504 -0
- package/tests/prompt-v5-integrity.test.mjs +14 -8
- package/tests/selfdrive-v5.mjs +55 -4
- package/vibe-math-v2/agent.cordis.yml +36 -14
- package/vibe-math-v2/math-computation.js +936 -0
- package/vibe-math-v2/math-engines.js +156 -0
- package/vibe-math-v2/vibe-math-v2.js +1653 -48
- package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
- package/vibe-math-v3/agent.cordis.yml +30 -12
- package/vibe-math-v3/math-computation.js +936 -0
- package/vibe-math-v3/math-engines.js +156 -0
- package/vibe-math-v3/vibe-math-v3.js +1623 -53
- package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
- package/vibe-math-v4/agent.cordis.yml +44 -8
- package/vibe-math-v4/math-computation.js +936 -0
- package/vibe-math-v4/math-engines.js +156 -0
- package/vibe-math-v4/vibe-math-v4.js +1592 -37
- package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
- package/vibe-math-v5/agent.cordis.yml +46 -10
- package/vibe-math-v5/math-computation.js +936 -0
- package/vibe-math-v5/math-engines.js +156 -0
- package/vibe-math-v5/vibe-math-v5.js +2233 -42
- package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +153 -4
- package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +251 -0
|
@@ -83,16 +83,16 @@ const probes = [
|
|
|
83
83
|
{
|
|
84
84
|
name: 'v2-lean-tools-line-removed',
|
|
85
85
|
preset: 'vibe-math-v2', file: 'agent.cordis.yml', nth: 'all', expect: 2,
|
|
86
|
-
guarantee: 'the
|
|
87
|
-
from: ' - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib — Lean formal
|
|
86
|
+
guarantee: 'the five Lean tools must be named in the persona the main agent receives',
|
|
87
|
+
from: ' - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib / vibe_math_lean_read /\n vibe_math_lean_job — Lean formal verification (execute / archive / list + read the reuse\n library / inspect or wait for a background compile job). leanAsync=true (default) queues the\n compile and returns a jobId; nothing counts as proved until that job settles ok (so "有把握"\n is not the same as "已验证"). The scheduler\'s child agents use them too; they work in every\n mode.\n',
|
|
88
88
|
to: '',
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
name: 'v2-phantom-tool-mentioned',
|
|
92
92
|
preset: 'vibe-math-v2', file: 'agent.cordis.yml', nth: 'all', expect: 2,
|
|
93
93
|
guarantee: 'the persona must not advertise a tool that is not registered (the agent would call it and fail)',
|
|
94
|
-
from: ' - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib
|
|
95
|
-
to: ' - vibe_math_lean_exec / vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib
|
|
94
|
+
from: ' - vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib / vibe_math_lean_read /',
|
|
95
|
+
to: ' - vibe_math_lean_exec / vibe_math_lean_run / vibe_math_lean_archive / vibe_math_lean_lib / vibe_math_lean_read /',
|
|
96
96
|
},
|
|
97
97
|
|
|
98
98
|
// ── v3 ─────────────────────────────────────────────────────────────────────
|
|
@@ -148,27 +148,80 @@ const probes = [
|
|
|
148
148
|
name: 'v4-usage-advertises-phantom-subcommand',
|
|
149
149
|
preset: 'vibe-math-v4', file: 'vibe-math-v4.js', nth: 'first', expect: 1,
|
|
150
150
|
guarantee: 'the unknown-subcommand usage string must not advertise a subcommand that no branch implements',
|
|
151
|
-
// The anchor must be the CURRENT literal
|
|
151
|
+
// The anchor must be the CURRENT literal. Round A changed both the usage string and the
|
|
152
152
|
// hint to advertise `start [problem]` / `set <k=v>...` (vibe-math-v4.js:2492). While the
|
|
153
153
|
// anchor kept the pre-fix literal it matched 0 times, so this probe reported SETUP-FAIL
|
|
154
154
|
// every run and the F-section agreement between usage and branches had NO sensitivity
|
|
155
155
|
// proof at all (the hint probe at :155 was updated, this one was not).
|
|
156
|
-
|
|
157
|
-
|
|
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>...'",
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
162
|
name: 'v4-hint-advertises-unimplemented-subcommand',
|
|
161
163
|
preset: 'vibe-math-v4', file: 'vibe-math-v4.js', nth: 'first', expect: 1,
|
|
162
164
|
guarantee: 'the typing hint must not advertise a subcommand the handler does not implement',
|
|
163
|
-
|
|
164
|
-
|
|
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]'",
|
|
165
170
|
},
|
|
166
171
|
{
|
|
167
172
|
name: 'v5-persona-slash-list-drops-add-remove',
|
|
168
173
|
preset: 'vibe-math-v5', file: 'agent.cordis.yml', nth: 'all', expect: 2,
|
|
169
174
|
guarantee: "the persona's /v5 list must match the command hint (a human reading the persona must see every subcommand)",
|
|
170
|
-
|
|
171
|
-
|
|
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',
|
|
172
225
|
},
|
|
173
226
|
]
|
|
174
227
|
|
|
@@ -132,29 +132,35 @@ const PRESETS = [
|
|
|
132
132
|
dir: 'vibe-math-v2',
|
|
133
133
|
js: 'vibe-math-v2.js',
|
|
134
134
|
prefix: 'vibe_math_',
|
|
135
|
-
tools:
|
|
135
|
+
tools: 28,
|
|
136
136
|
// member-facing write-lock / scheduler-metadata tools; the v2 coordinator never
|
|
137
137
|
// writes Markdown itself, so they stay out of its persona.
|
|
138
138
|
undocumented: [],
|
|
139
|
-
lean: { tools: ['vibe_math_lean_run', 'vibe_math_lean_archive', 'vibe_math_lean_lib'], extra: [] },
|
|
139
|
+
lean: { tools: ['vibe_math_lean_run', 'vibe_math_lean_archive', 'vibe_math_lean_lib', 'vibe_math_lean_read', 'vibe_math_lean_job'], extra: [] },
|
|
140
|
+
// the shared math-computation tool (docs/math-computation.md) is prefix-less by
|
|
141
|
+
// design (INTERFACE-FREEZE.md §3), so it is pinned explicitly here.
|
|
142
|
+
math: { tools: ['math_computation'] },
|
|
140
143
|
},
|
|
141
144
|
{
|
|
142
145
|
dir: 'vibe-math-v3',
|
|
143
146
|
js: 'vibe-math-v3.js',
|
|
144
147
|
prefix: 'vibe_math_',
|
|
145
|
-
tools:
|
|
148
|
+
tools: 36,
|
|
146
149
|
// `vibe_math_sync_meta` is no longer undocumented: the promotion-contract line of the
|
|
147
150
|
// persona now names it as the place to report `lemmas[].价值/关键性` (v3 audit M1 —
|
|
148
151
|
// without a prompt-side source for that field the promotion main line is unreachable).
|
|
149
152
|
// The write-lock pair stays out: solver/method-keeper prompts carry it via kcWriteRules().
|
|
150
153
|
undocumented: ['vibe_math_claim_write', 'vibe_math_release_write'],
|
|
151
|
-
lean: { tools: ['vibe_math_lean_run', 'vibe_math_lean_archive', 'vibe_math_lean_lib'], extra: [] },
|
|
154
|
+
lean: { tools: ['vibe_math_lean_run', 'vibe_math_lean_archive', 'vibe_math_lean_lib', 'vibe_math_lean_read', 'vibe_math_lean_job'], extra: [] },
|
|
155
|
+
// the shared math-computation tool (docs/math-computation.md) is prefix-less by
|
|
156
|
+
// design (INTERFACE-FREEZE.md §3), so it is pinned explicitly here.
|
|
157
|
+
math: { tools: ['math_computation'] },
|
|
152
158
|
},
|
|
153
159
|
{
|
|
154
160
|
dir: 'vibe-math-v4',
|
|
155
161
|
js: 'vibe-math-v4.js',
|
|
156
162
|
prefix: 'vibe_v4_',
|
|
157
|
-
tools:
|
|
163
|
+
tools: 35,
|
|
158
164
|
// resident-facing tools (mail, library cards, task board, write lock). The
|
|
159
165
|
// coordinator drives residents through vibe_v4_message / _meeting / _add_member.
|
|
160
166
|
undocumented: [
|
|
@@ -164,15 +170,20 @@ const PRESETS = [
|
|
|
164
170
|
'vibe_v4_task_done', 'vibe_v4_list_tasks', 'vibe_v4_report_context',
|
|
165
171
|
'vibe_v4_claim_write', 'vibe_v4_release_write',
|
|
166
172
|
],
|
|
167
|
-
lean: { tools: ['vibe_v4_lean_run', 'vibe_v4_lean_archive', 'vibe_v4_lean_lib'], extra: ['vibe_v4_formal_report'] },
|
|
173
|
+
lean: { tools: ['vibe_v4_lean_run', 'vibe_v4_lean_archive', 'vibe_v4_lean_lib', 'vibe_v4_lean_read', 'vibe_v4_lean_job'], extra: ['vibe_v4_formal_report'] },
|
|
174
|
+
// the shared math-computation tool (docs/math-computation.md) is prefix-less by
|
|
175
|
+
// design (INTERFACE-FREEZE.md §3), so it is pinned explicitly here.
|
|
176
|
+
math: { tools: ['math_computation'] },
|
|
168
177
|
},
|
|
169
178
|
{
|
|
170
179
|
dir: 'vibe-math-v5',
|
|
171
180
|
js: 'vibe-math-v5.js',
|
|
172
181
|
prefix: 'vibe_v5_',
|
|
173
|
-
tools:
|
|
182
|
+
tools: 40,
|
|
174
183
|
// member- and academician-facing tools; the office (main agent) holds only the
|
|
175
|
-
// institute-level controls plus the hiring authority.
|
|
184
|
+
// institute-level controls plus the hiring authority. The final-paper pair is an
|
|
185
|
+
// OFFICE control and IS named in the persona (vibe_v5_paper / vibe_v5_finalize_paper),
|
|
186
|
+
// so it must not appear in this snapshot.
|
|
176
187
|
undocumented: [
|
|
177
188
|
'vibe_v5_wait', 'vibe_v5_record_progress', 'vibe_v5_record_proposition',
|
|
178
189
|
'vibe_v5_record_method', 'vibe_v5_record_subproblem', 'vibe_v5_read_library',
|
|
@@ -180,11 +191,14 @@ const PRESETS = [
|
|
|
180
191
|
'vibe_v5_task_list', 'vibe_v5_task_get', 'vibe_v5_task_update',
|
|
181
192
|
'vibe_v5_overview', 'vibe_v5_assign', 'vibe_v5_prioritize', 'vibe_v5_nudge',
|
|
182
193
|
],
|
|
183
|
-
lean: { tools: ['vibe_v5_lean_run', 'vibe_v5_lean_archive', 'vibe_v5_lean_lib'], extra: [] },
|
|
194
|
+
lean: { tools: ['vibe_v5_lean_run', 'vibe_v5_lean_archive', 'vibe_v5_lean_lib', 'vibe_v5_lean_read', 'vibe_v5_lean_job'], extra: [] },
|
|
195
|
+
// the shared math-computation tool (docs/math-computation.md) is prefix-less by
|
|
196
|
+
// design (INTERFACE-FREEZE.md §3), so it is pinned explicitly here.
|
|
197
|
+
math: { tools: ['math_computation'] },
|
|
184
198
|
},
|
|
185
199
|
]
|
|
186
200
|
|
|
187
|
-
const LEAN_PARAMS = ['formalVerify', 'leanCommand', 'leanArgs', 'leanTimeoutMs']
|
|
201
|
+
const LEAN_PARAMS = ['formalVerify', 'leanCommand', 'leanArgs', 'leanTimeoutMs', 'leanAsync', 'leanJobsMaxParallel', 'leanInitiative', 'leanSearchPaths']
|
|
188
202
|
|
|
189
203
|
for (const P of PRESETS) {
|
|
190
204
|
const ymlPath = join(BASE, P.dir, 'agent.cordis.yml')
|
|
@@ -193,9 +207,19 @@ for (const P of PRESETS) {
|
|
|
193
207
|
const src = readFileSync(jsPath, 'utf8')
|
|
194
208
|
|
|
195
209
|
// ---- registered tools -------------------------------------------------
|
|
210
|
+
// Two registration shapes exist now:
|
|
211
|
+
// 1. a literal `registerTool('<name>', …)` (most tools, including the shared math tool in
|
|
212
|
+
// v2/v3, which register it by its frozen prefix-less name);
|
|
213
|
+
// 2. the shared math tool in v4/v5, where the plugin only wires a host callback and the module
|
|
214
|
+
// calls `host.register(MATH_TOOL_NAME, …)`. The module's name is FROZEN
|
|
215
|
+
// (MATH_TOOL_NAME = 'math_computation', INTERFACE-FREEZE.md §3), so a source that wires
|
|
216
|
+
// `registerMathComputation(` contributes exactly that one tool. Without this the shared tool
|
|
217
|
+
// would be invisible to this guard on v4/v5 - a silent coverage hole.
|
|
196
218
|
const registered = new Set()
|
|
197
219
|
for (const m of src.matchAll(/registerTool\(\s*'([A-Za-z0-9_]+)'/g)) registered.add(m[1])
|
|
220
|
+
if (/registerMathComputation\s*\(/.test(src)) registered.add('math_computation')
|
|
198
221
|
eq(registered.size, P.tools, `${P.dir}: registered tool count changed (update this snapshot deliberately)`)
|
|
222
|
+
for (const tool of (P.math ? P.math.tools : [])) ok(registered.has(tool), `${P.dir}: the shared math tool ${tool} is not registered`)
|
|
199
223
|
|
|
200
224
|
// ---- A. both literal blocks exist and are non-empty -------------------
|
|
201
225
|
const blocks = literalBlocks(yml)
|
|
@@ -224,7 +248,13 @@ for (const P of PRESETS) {
|
|
|
224
248
|
// Uppercase is part of the token class on purpose: a drifted name such as
|
|
225
249
|
// `vibe_v4_formal_reportX` must be captured whole and judged unregistered, instead of
|
|
226
250
|
// being read as a mention of the registered `vibe_v4_formal_report`.
|
|
227
|
-
|
|
251
|
+
// Names that do NOT carry this preset's prefix (the shared `math_computation`) are added to the
|
|
252
|
+
// alternation from the registry, so "every registered tool must be mentioned" stays true for
|
|
253
|
+
// them too instead of forcing them into the `undocumented` snapshot.
|
|
254
|
+
const prefixRoot = P.prefix.replace(/_$/, '')
|
|
255
|
+
const prefixless = [...registered].filter((n) => !new RegExp('^' + prefixRoot + '_').test(n))
|
|
256
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
257
|
+
const re = new RegExp('\\b' + prefixRoot + '_[A-Za-z0-9_]+' + (prefixless.length ? '|\\b' + prefixless.map(escapeRe).join('\\b|\\b') + '\\b' : ''), 'g')
|
|
228
258
|
for (const m of body.matchAll(re)) {
|
|
229
259
|
if (body[m.index + m[0].length] === '*') { wildcards.add(m[0]); continue }
|
|
230
260
|
mentioned.add(m[0])
|
|
@@ -258,12 +288,19 @@ for (const P of PRESETS) {
|
|
|
258
288
|
if (!hintOpen) eq(implemented.filter((c) => !hinted.includes(c)).sort(), [], `${P.dir}: implemented slash subcommand(s) are missing from the hint`)
|
|
259
289
|
eq(used.sort(), implemented, `${P.dir}: the usage string and the implemented slash subcommands disagree`)
|
|
260
290
|
const personaEnum = /slash command mirrors[^(]*\(([\s\S]*?)\)/.exec(blocks.prefix.join('\n') + '\n' + blocks.text.join('\n'))
|
|
261
|
-
|
|
291
|
+
// An ellipsis must NOT switch this check off (that is how a dropped `paper` entry survived here),
|
|
292
|
+
// and `...` inside an argument placeholder (`set <k=v>...`) is not an abbreviation at all. So the
|
|
293
|
+
// persona is compared against the BRANCHES (the ground truth), and every implemented subcommand
|
|
294
|
+
// must be named EXPLICITLY. Only a HINT that is itself abbreviated makes the reverse direction
|
|
295
|
+
// (persona ⊆ hint) unprovable, so only that direction is relaxed then.
|
|
296
|
+
if (!personaEnum) {
|
|
297
|
+
ok(false, `${P.dir}: the persona never enumerates its /vN subcommands (list them so a human can see every one)`)
|
|
298
|
+
} else {
|
|
262
299
|
const personaCmds = cmdsFrom(personaEnum[1].replace(/^\s*\/[a-z0-9]+\s+/, ''))
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
300
|
+
ok(personaCmds.length > 0, `${P.dir}: no slash subcommand could be parsed from the persona's /vN list`)
|
|
301
|
+
eq(personaCmds.filter((c) => !implemented.includes(c)).sort(), [], `${P.dir}: the persona enumerates slash subcommand(s) the handler does not implement`)
|
|
302
|
+
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 "..."`)
|
|
303
|
+
if (!hintOpen) eq(personaCmds.slice().sort(), implemented.slice().sort(), `${P.dir}: the persona's /vN list and the command hint disagree`)
|
|
267
304
|
}
|
|
268
305
|
|
|
269
306
|
// ---- E. the Lean feature's prompt surface ----------------------------
|
|
@@ -90,6 +90,20 @@ for (const m of src.matchAll(/(?:const|let|var)\s+\{([^}]+)\}\s*=/g)) {
|
|
|
90
90
|
for (const part of m[1].split(',')) { const n = part.split(':').pop().trim(); if (n) defined.add(n) }
|
|
91
91
|
}
|
|
92
92
|
for (const m of src.matchAll(/catch\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/g)) defined.add(m[1])
|
|
93
|
+
// ESM imports are DEFINITIONS too: the preset imports the shared `math_computation` module
|
|
94
|
+
// (INTERFACE-FREEZE §5.1), so calling an imported function is not a ReferenceError. Without
|
|
95
|
+
// this every imported symbol was reported as "called but never defined"; a call to a name that
|
|
96
|
+
// is neither declared nor imported is still flagged.
|
|
97
|
+
for (const m of src.matchAll(/import\s*(?:\*\s*as\s+([A-Za-z_$][\w$]*)|([A-Za-z_$][\w$]*)\s*,?\s*)?(?:\{([^}]*)\}\s*)?from\s*['"][^'"]*['"]/g)) {
|
|
98
|
+
if (m[1]) defined.add(m[1])
|
|
99
|
+
if (m[2]) defined.add(m[2])
|
|
100
|
+
if (m[3]) {
|
|
101
|
+
for (const part of m[3].split(',')) {
|
|
102
|
+
const name = part.split(/\s+as\s+/).pop().trim()
|
|
103
|
+
if (name) defined.add(name)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
93
107
|
// Function/method PARAMETERS are locally bound identifiers too, not free calls.
|
|
94
108
|
for (const m of src.matchAll(/function\s*[A-Za-z_$]*\s*\(([^)]*)\)/g)) {
|
|
95
109
|
for (const raw of m[1].split(',')) {
|