dsh-vibe-math 2.6.0 → 2.7.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 (59) hide show
  1. package/README.en.md +161 -389
  2. package/README.md +161 -390
  3. package/cordis.patch.yml +62 -14
  4. package/docs/AUDIT-CHECKLIST.md +2 -2
  5. package/docs/final-paper.md +1 -0
  6. package/docs/formal-verification.md +184 -29
  7. package/docs/math-computation.md +163 -0
  8. package/docs/release-notes/RELEASE-NOTES-2.7.0.en.md +151 -0
  9. package/docs/release-notes/RELEASE-NOTES-2.7.0.md +115 -0
  10. package/docs/release-notes/RELEASE-NOTES-2.7.1.en.md +86 -0
  11. package/docs/release-notes/RELEASE-NOTES-2.7.1.md +86 -0
  12. package/docs/test-timing.md +24 -12
  13. package/installer.js +7 -4
  14. package/package.json +21 -3
  15. package/prompt-corpus-persona/persona-corpus.json +12 -12
  16. package/prompt-corpus-persona/persona-corpus.md +128 -32
  17. package/prompt-corpus-v2/formal-verify-v2.json +182 -92
  18. package/prompt-corpus-v2/formal-verify-v2.md +2379 -55
  19. package/prompt-corpus-v3/formal-verify-v3.json +215 -53
  20. package/prompt-corpus-v3/formal-verify-v3.md +2147 -22
  21. package/prompt-corpus-v4/formal-verify-v4.json +14 -14
  22. package/prompt-corpus-v4/formal-verify-v4.md +267 -14
  23. package/prompt-corpus-v5/prompt-corpus-v5.json +62 -62
  24. package/prompt-corpus-v5/prompt-corpus-v5.md +631 -3
  25. package/tests/audit-math-computation-contract.mjs +204 -0
  26. package/tests/audit-math-computation-parity.mjs +198 -0
  27. package/tests/audit-math-computation-sensitivity.mjs +231 -0
  28. package/tests/audit-persona-sensitivity.mjs +4 -4
  29. package/tests/audit-persona-surface.test.mjs +38 -10
  30. package/tests/audit-v5-integrity.mjs +14 -0
  31. package/tests/e2e-v5-round2.test.mjs +459 -2
  32. package/tests/formal-verify-v2.test.mjs +362 -8
  33. package/tests/formal-verify-v3.test.mjs +355 -6
  34. package/tests/formal-verify-v4.test.mjs +295 -6
  35. package/tests/formal-verify-v5.test.mjs +9 -1
  36. package/tests/helpers/math-computation-fake-seam.mjs +163 -0
  37. package/tests/math-computation-shared.test.mjs +806 -0
  38. package/tests/prompt-v5-integrity.test.mjs +5 -5
  39. package/vibe-math-v2/agent.cordis.yml +22 -8
  40. package/vibe-math-v2/math-computation.js +1279 -0
  41. package/vibe-math-v2/math-engines.js +197 -0
  42. package/vibe-math-v2/vibe-math-v2.js +853 -40
  43. package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
  44. package/vibe-math-v3/agent.cordis.yml +22 -8
  45. package/vibe-math-v3/math-computation.js +1279 -0
  46. package/vibe-math-v3/math-engines.js +197 -0
  47. package/vibe-math-v3/vibe-math-v3.js +844 -47
  48. package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
  49. package/vibe-math-v4/agent.cordis.yml +38 -6
  50. package/vibe-math-v4/math-computation.js +1279 -0
  51. package/vibe-math-v4/math-engines.js +197 -0
  52. package/vibe-math-v4/vibe-math-v4.js +816 -31
  53. package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +6 -0
  54. package/vibe-math-v5/agent.cordis.yml +42 -6
  55. package/vibe-math-v5/math-computation.js +1279 -0
  56. package/vibe-math-v5/math-engines.js +197 -0
  57. package/vibe-math-v5/vibe-math-v5.js +1132 -37
  58. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +83 -3
  59. package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +251 -0
@@ -0,0 +1,163 @@
1
+ # 数学计算工具(`math_computation`)—— 契约与用法
2
+
3
+ > 四个预设(v2/v3/v4/v5)共用同一个工具。规格与冻结件在开发检出里:`_oneoff/math-computation-spec-v3.md`(rev-3)、`_oneoff/mc-P1-ready/INTERFACE-FREEZE.md`、`mc-P1-ready/{engines.md,tool-schema.json,receipt.md,prompts.md,guards.md}`。
4
+ > 本文件只讲**已实现的行为**与**明确不能保证的东西**,不含性能或效果承诺。
5
+
6
+ ## 1. 它是什么
7
+
8
+ `math_computation` 让成员先在**运行时探测**本机可用的计算引擎与包,再以统一接口执行计算,并把每次执行落成**可复核回执**:脚本原件、完整输出、实际 argv、解释器/引擎版本、退出码与耗时。它比"随手用宿主 shell 跑一条命令"多出来的只有三件事——**探测**、**统一接口**、**归档**。
9
+
10
+ **P1 引擎集合**:`python`、`r`、`octave`、`julia`(自由软件,全功能)+ `matlab`、`maple`、`wolfram`(商业:只探测与许可检查,**永不安装**,有证即可用)+ `cli`(通用逃生口,**默认开启**)。SageMath 属 P2,P1 不注册。
11
+
12
+ ## 2. 参数(六个,名字已冻结)
13
+
14
+ | 参数 | 取值 | 默认 | 说明 |
15
+ |---|---|---|---|
16
+ | `mathComputation` | `off` \| `auto` \| `on` | `auto` | `off` 是**真 no-op**:不探测、不执行、不在提示词里出现;`auto` 探测到引擎才工作 |
17
+ | `mathMode` | `typed` \| `typed+shell` | `typed+shell` | **只是提示词策略**(见 §6):允许在工具不可用时用宿主 shell 兜底,但结论必须标注"未经工具归档";`typed` 不提 shell,并禁用 `engine:'cli'` |
18
+ | `mathEngines` | 引擎名数组 | `[python, r, octave, julia, matlab, maple, wolfram, cli]` | 允许的引擎**及其探测顺序**;去掉 `cli` 即关闭逃生口 |
19
+ | `mathTimeoutMs` | 正整数(≥1000) | `60000` | 单次执行上限;超时后**主动终止**进程 |
20
+ | `mathPackages` | 字符串数组 | `[]` | 默认要求存在的包/工具箱(缺包只报告,不安装) |
21
+ | `mathInstallScope` | `user` \| `system` | `user` | **作用域只对当次调用生效,永不记忆**;多数包管理器没有 system 模板,此时 system 请求会被拒绝 |
22
+
23
+ 非法值一律回落默认(枚举非白名单 → 默认;`mathTimeoutMs` 非正 → 默认并抬到 ≥1000;`mathEngines` 过滤未知项、去重、空则回落默认)。
24
+
25
+ ## 3. 工具接口
26
+
27
+ ```
28
+ math_computation {
29
+ op: 'probe' | 'run' | 'receipt' | 'install',
30
+ engine?, mode?: 'code'|'file'|'expr', code?|file?|expr?,
31
+ packages?, timeoutMs?, captureFiles?, record?,
32
+ cli?: { command, argv?, stdin? }, // engine='cli' 必填
33
+ scope?: 'user'|'system', dryRun?, confirm? // op='install'
34
+ }
35
+ ```
36
+
37
+ - **`probe`**:只探测引擎/版本/许可与(可选的)包,**不执行用户代码**。没有可用引擎时返回 `MATH_ENGINE_NOT_FOUND` + 逐 OS 的用户安装指引。
38
+ - **`run`**:`mode:'code'` 由工具把源码落成 `script.<ext>` 再执行;`mode:'file'` 只接受**项目内**路径(复制进回执目录后执行);`mode:'expr'` 走该引擎的 eval 模板(表达式只占**一个** argv 元素)。`engine:'cli'` 时 argv = `cli.command` + `cli.argv`,`mode:'expr'` 被拒绝。
39
+ - **`receipt`**:对既有 `Computation/<id>/` **幂等**重渲染 `receipt.md`、补写缺失产物,**不重跑**。
40
+ - **`install`**:两步。先 `dryRun`(默认)返回计划与 `planToken`;执行必须把该 token 原样回传 `confirm`,token 不符即拒绝。**商业引擎一律拒绝**。
41
+
42
+ 失败码(11 个,全部 `MATH_` 前缀):`MATH_NOT_AVAILABLE`、`MATH_ENGINE_NOT_FOUND`、`MATH_ENGINE_LICENSE_REQUIRED`、`MATH_ENGINE_UNUSABLE`、`MATH_MISSING_PACKAGES`、`MATH_TIMEOUT`、`MATH_NONZERO_EXIT`、`MATH_ENGINE_BAD_ARGV`、`MATH_REFUSED`、`MATH_INVALID_ARGUMENT`、`MATH_NO_SUBPROCESS`。每个"环境缺失"类失败都带 `next`(自装指引 / 代装计划 / 厂商链接 / 启用建议)。
43
+
44
+ **"没有 subprocess 服务"与"没有引擎"是两件事**(避免误导用户去装 python):
45
+ - 宿主若能声明自己没有 subprocess 服务(预设可选提供 `hasSubprocess:()=>false`),`run` 与 `probe` 一律返回 `MATH_NO_SUBPROCESS` + `next.kind:'note'`;
46
+ - 宿主不提供该声明时:命令**解析不到** ⇒ `MATH_ENGINE_NOT_FOUND`(附逐 OS 安装指引);能解析但 `spawn` 拿不到句柄(返回 `null`)⇒ `MATH_NO_SUBPROCESS`。
47
+ - **机读词汇表(穷举;权威副本在 `_oneoff/mc-P1-ready/tool-schema.json#refusalVocabulary`,由 parity 守卫盯着,改名即红)**:
48
+ - `code`(11 个):`MATH_NOT_AVAILABLE` `MATH_ENGINE_NOT_FOUND` `MATH_ENGINE_LICENSE_REQUIRED` `MATH_ENGINE_UNUSABLE` `MATH_MISSING_PACKAGES` `MATH_TIMEOUT` `MATH_NONZERO_EXIT` `MATH_ENGINE_BAD_ARGV` `MATH_REFUSED` `MATH_INVALID_ARGUMENT` `MATH_NO_SUBPROCESS`;
49
+ - `next.kind`(7 个):`user-install`(用户自装指引)、`agent-install`(代装计划/计划就绪)、`vendor`(商业引擎厂商指引)、`enable`(`mathComputation:'off'` 的启用建议)、`engine-override`(bad-argv ⇒ 用 `mathEngineOverride` 覆盖模板)、`reason`(机读拒绝原因)、`note`(补充说明,如无 subprocess);
50
+ - `next.reason`(**13** 个,仅当 `next.kind==='reason'`):`policy`、`engine-not-allowed`、`missing-cli-command`、`expr-not-supported`、`mode-not-supported`、`path-outside-project`、`file-not-found`、`system-scope-unsupported`、`plan-token-mismatch`、`archive-missing`、`receipt-unparsable`、`no-subprocess`、**`unsupported-version-syntax`**(包规格写法不被支持:版本求解交给包管理器,见 §5)。
51
+ 调用方**不必解析文案**:按 `code` + `next.kind`(必要时 `next.reason`)分支即可;新增或改名必须同时更新这里与 `tool-schema.json`,否则守卫变红(守卫还会校验每个 `reason:` 取值都是**字符串字面量**,避免以常量绕开"穷举"检查)。
52
+
53
+ ## 4. 回执:可复核的支撑材料
54
+
55
+ ```
56
+ <项目或研究所根>/Computation/<runId>/
57
+ script.<ext> # 唯一权威源码(先落盘再执行)
58
+ stdout.txt / stderr.txt # 完整输出(返回体只给 ≤64KB 截断版)
59
+ receipt.json / receipt.md # 机器可读 + 人读
60
+ captured/… # captureFiles 的项目内副本
61
+ ```
62
+
63
+ - `runId = <preset>-<projectSlug>-<sha256(engine|mode|规范化的脚本|排序后的包需求)[0:12]>`——**不含墙钟**:同引擎、同脚本、同包要求在同一项目下命中同一目录。
64
+ - `receipt.json` 记录:引擎与版本、脚本路径与哈希、**实际 argv**、cwd、退出码/超时/耗时、截断标记、产物哈希、`network:"not-enforced-by-plugin"`。
65
+ - **复跑**:`math_computation { op:'run', engine:'<e>', mode:'file', file:'Computation/<id>/script.<ext>' }`,或直接照 `receipt.json` 的 `argv` + `cwd` 重放。
66
+ - 报告里"我算过"要给 `Computation/<id>/receipt.json`;**回执是经验证据,不是证明**——对象是否已验证仍由各预设既有的验证/共识路径决定。
67
+
68
+ ### 4.1 归档 → 编辑 → 重跑(可发现性与完整性)
69
+
70
+ 回执与返回体都带 **`scriptPath`**(归档脚本周相对路径)与 **`scriptHash`**(脚本字节的 sha256),所以"这次跑的到底是哪份代码"是可查的:
71
+
72
+ 1. `mode:'code'` 把源码落成 `Computation/<id>/script.<ext>`;agent **可以用普通文件工具打开并编辑它**。
73
+ 2. 编辑后用 `mode:'file'` 指向该脚本重跑,会写出**一份新回执**(新 attempt、新 `scriptHash`)。
74
+ 3. **旧回执对修改后的代码无效**。`mode:'file'` 的回执按**路径**归属同一个 archive id(不是按内容),所以"同一文件编辑后重跑"落在同一 id 的**新 attempt**,并显式给出:
75
+ - `scriptChanged:true`:该文件当前哈希与**上一份回执**记录的 `scriptHash` 不一致(旧回执不再代表当前代码);
76
+ - `scriptChangedDuringRun:true`:文件在**本次运行期间**被改动(典型是另一个成员同时在编辑)——运行前后各取一次哈希才可能发现;
77
+ - 两者都同时进入 `warnings[]`(`SCRIPT_CHANGED_SINCE_LAST_RECEIPT` / `SCRIPT_CHANGED_DURING_RUN`)与 `receipt.md`,**绝不静默**。
78
+ 4. `op:'receipt'` 会重新核对归档脚本的当前哈希与回执记录,返回 `scriptChanged` / `currentScriptHash`;它**只读**返回既有数据,仅补写**缺失**的 `receipt.md`,从不改写已有回执文件。**归档目录不存在**时返回 `MATH_REFUSED` + `next:{kind:'reason', reason:'archive-missing'}`(显式拒绝,不是"参数错误");`receipt.json` 存在但**无法解析**时仍是 `MATH_INVALID_ARGUMENT`(`reason:'receipt-unparsable'`,那确实是坏掉的产物)。
79
+ - **哈希的能力边界(按设计,不是缺陷)**:`scriptHash`/`scriptChanged` 是**内容**哈希——"内容相同但换了 inode/mtime(删了重建、复制覆盖)"与"改了又改回原样"**无法区分**,因此也不会被告警;需要区分"文件被动过"请用宿主的版本守卫(`ctx.fs` 的 `expected.version`)。**每会话模块实例的生命周期**跟随该会话对象(v4 原有生命周期,非本轮引入)。
80
+ - **手改状态文件里的未知键不会被静默丢弃(round-4)**:v5 从研究所状态文件恢复参数时,白名单之外的键会被忽略,但**会记入 `vibe_v5_status` 的 `diagnostics`**(`{kind:'state-dropped-keys', where, keys}`)并打印警告;同一归一化器也服务 `vibe_v5_set` / `vibe_v5_configure`,所以未知键在任何入口都不会无声消失。
81
+ 5. **归档是追加式的**:同一 id 的**首次**运行落在 `Computation/<id>/`,之后每次运行落在 `Computation/<id>/attempts/<n>/`(n 从 2 开始,取第一个空位);已有文件**永不覆盖**。**并发**下同一 id 的两个运行由模块按 id 串行分配,因此一定分别拿到 attempt 1 / attempt 2,不会共用目录(跨进程并发见 §6 的限制说明)。
82
+ 6. **失败关闭(fail-closed)**:源文件在运行**期间消失** ⇒ `scriptChangedDuringRun:true`;`op:'receipt'` 读不到归档脚本(被删/移走)⇒ `scriptChanged:true` + `currentScriptHash:null`。任何"读不到/对不上"都按"旧回执不可用"处理,绝不静默当真。
83
+ 7. **保留上限**:每个 id 最多 `MATH_ARCHIVE_MAX_ATTEMPTS_PER_RUN`=20 个 attempt、每个项目最多 `MATH_ARCHIVE_MAX_RUNS`=200 个 run 目录(后者仅在宿主提供可选 `listDir` 时检查)。**超过只告警**(`ARCHIVE_RETENTION_EXCEEDED`),**永不自动删除**。
84
+
85
+ **验证者(或复核代理)要查的六件事**:① 打开 `receipt.json` 确认 `scriptPath`;② 重新计算该脚本 sha256,与 `scriptHash` **逐字相等**;③ 确认 `scriptChanged` 与 `scriptChangedDuringRun` 均为 `false`(否则该回执不足以支撑结论);④ 确认 `sourceFile`(若有)正是被验证的源码,且 `attempt`/`attemptDir` 是当前代码对应的最新 attempt;⑤ 按 `argv` + `cwd` 重放,结果一致;⑥ 一旦改代码,**必须重跑并引用新回执**,报告里的旧回执撤下。
86
+
87
+ ## 5. 安装:两条路径
88
+
89
+ 1. **用户自己装**:工具在缺引擎时返回逐 OS 命令(Windows/macOS/Linux)与官方链接;商业引擎只给厂商安装包与激活说明。
90
+ 2. **代理代装**:`plan → confirm`(token 绑定该计划,内容变了就失效);默认 **user 作用域**(`pip --user`、用户 depot、`R_LIBS_USER`、Octave 用户包目录…);**system 作用域必须每次显式指定且不被记住**;每次执行写审计 `Computation/installs/<planToken>.json`(计划、作用域、管理器、命令、退出码、before/after、卸载命令模板)。
91
+
92
+ **诚实的回滚说明**:包安装**没有通用回滚**。工具保证的是——记下确切命令与 before/after 版本(卸载**可验证**)、默认 user 作用域限制影响面、提供各管理器的卸载模板;**不做**自动回滚。
93
+
94
+ ### 5.1 Python 的管理器分派(检测,不猜)
95
+
96
+ `python` 的安装计划按**解释器自身环境**选择包管理器,而不是固定 pip:
97
+
98
+ | 检测到的环境 | 计划里的命令 | 依据 / 说明 |
99
+ |---|---|---|
100
+ | conda 环境(解释器路径含 `/envs/`、`/conda`、`/miniconda*`、`/anaconda*`、`/mambaforge`、`/miniforge`)且 `conda` 可解析 | `conda install -y -c conda-forge <pkg>`(卸载 `conda remove -y <pkg>`) | 路径标记 + 可解析确认 |
101
+ | 同上但只有 `mamba` | `mamba install -y -c conda-forge <pkg>`(卸载 `mamba remove -y <pkg>`) | 同上 |
102
+ | `uv` 与解释器**同目录**(uv 管理的 venv) | `uv pip install <pkg>`(卸载 `uv pip uninstall <pkg>`) | 同目录才算,PATH 上别处的 uv 不算 |
103
+ | 其它 | `python -m pip install --user <pkg>`(卸载 `python -m pip uninstall -y <pkg>`) | **文档化回退**,且计划里标 `managerAssumed:true` |
104
+
105
+ 计划、返回消息与审计 JSON 都带 `manager` / `managerAssumed` / `managerWhy`——**歧义时回退 pip 并明说"计划假设用 pip"**,绝不猜。
106
+
107
+ ### 5.2 R / Octave / Julia:只做用户级(并说明为什么)
108
+
109
+ 这三个引擎**没有系统级模板**,`scope:'system'` 一律返回 `MATH_REFUSED` + `next.reason='system-scope-unsupported'`,消息里带**每引擎的理由**:
110
+
111
+ - **R**:用户库是 `R_LIBS_USER`;系统级要写发行版包目录(需 root 或发行版包管理器)。
112
+ - **Octave**:`pkg install` 装进用户包目录;系统级要写 Octave 的 `share/packages`(需 root)。
113
+ - **Julia**:`Pkg.add` 装进当前活动环境 / 用户 depot(`JULIA_DEPOT_PATH`);"系统级"不是 Julia 的概念。
114
+
115
+ python 走 pip 时仍有真实的系统模板(不带 `--user`);conda/mamba/uv **没有**系统模板,各自带理由。
116
+
117
+ ### 5.3 版本约束:交给包管理器,工具只查存在
118
+
119
+ - **策略**:**版本求解由包管理器负责**,本工具**只检查包是否存在**(按 **base name** 探测与报缺),安装时把规格**原样透传**。计划里带 `versionPolicy` 说明。
120
+ - **接受的写法**:`名称`、`名称[extras]`、`名称<op>版本`,其中 `<op>` ∈ `==` `>=` `<=` `~=` `!=` `>` `<` `=`(例如 pip 的 `numpy==1.2`、conda 的 `numpy=1.2`)。
121
+ - **拒绝的写法**:空格、`;`、`|`、`&`、`$`、反引号、`@`、括号等 shell 危险或管理器不认识的语法 ⇒ `MATH_INVALID_ARGUMENT` + `next.reason='unsupported-version-syntax'`(**不会**把可疑字符串丢给 shell)。
122
+
123
+ ### 5.4 引擎发现顺序、`op:'probe'` 的语义与 `mathEngineOverride` 的边界
124
+
125
+ - **发现顺序(PATH 永远优先)**:① PATH 上的描述符候选名(`python3`→`python`→`py`、`Rscript`→`R`、`octave`→`octave-cli`、`julia`);② 以上都失败、且宿主声明了可选字段 `runtimeRoots` + `listDirAbs` 时,**最后**扫描 **DSH 自带运行时** `<root>/dsh-runtimes/*/dependencies/<engine>/<候选名>{,.exe,.cmd}`——**树名通配**(不写死 `dsh-primary-runtime`),且只接受描述符自己的候选名(不会塞进无关二进制)。`<root>` 由宿主给出(当前四套预设:**若 `DSH_HOME` 已设置则以它为唯一根**,否则用用户主目录下的 `.dsh`——显式设置即隔离发现,测试与定制部署都靠这个)。
126
+ - **找到可用引擎就报它的真实版本**(`engineInfo.path` + `version`),**不再**给安装指引;**只有**一个可用引擎都找不到时,才按 OS 给安装指引——而且是对**请求的那个引擎**(不是"第一个允许的引擎")。`op:'probe'` 的 `engine`/`engineInfo` 就是**请求的**引擎;请求的引擎缺失时,`code`/`message`/`next` 全部指向它(历史上这里会回显 `python`)。
127
+ - **`mathEngineOverride` **不是**发现手段**:它只按引擎名覆盖 **argv 模板**(如 `{python:{versionArgv,scriptArgv,evalArgv,packageProbe}}`),**不能指定一个可执行文件**。所以 `MATH_ENGINE_NOT_FOUND` 的正解是:装上引擎、把解释器放进 PATH、让上面的 DSH 运行时被扫到,或用 `engine:'cli'` + `cli.command` 显式给命令。
128
+ - **`engine:'cli'` 的三条行为**:① **包预检按 `cli.command` 的族**执行(`python*`/`Rscript`/`octave`/`julia` 用对应描述符的探测);命令族**不可识别** ⇒ **跳过**预检并给出 `PACKAGE_PRECHECK_SKIPPED` 警告(**不误报缺包、不阻塞**逃生口)。② `mode:'code'`/`'file'` 时归档脚本的**绝对路径会自动追加到 argv 末尾**(回执里 `cli.scriptAppended: true`;`cli.argv` 仍是调用方给的 argv)——否则会掉进 REPL、`exit=0` 而 stdout 为空,读起来像成功。**但若调用方 argv 自带程序槽(`-c`/`-m`/`-e`/`--eval`/`--command`),则调用方 argv 优先、不追加**(否则脚本会被当作多余参数,例如 `python -c 'print(1)' script.txt` 会把脚本喂给 `sys.argv[1]`);此时回执给 `cli.scriptAppended:false` + `cli.scriptSkipped` 说明原因。③ `engineInfo.version` 取自**真实的版本探测**(族可识别时),不再是 `"unknown"`。
129
+ - **真实宿主上的"空 spawn"与失败可见性(round-7 实测)**:真实会话里宿主的 `subprocess` 通路**在会话早期可能对一次 spawn 返回空**(`exit=null`)。因此:版本探测**最多尝试 3 次**(20s / 45s / 45s + 退避),且只在**看起来像冷启动/空返回**(`timedOut`、`exit===null`、`spawned=false`)时重试——确定性失败(非零退出、输出无法解析)**不重试**。仍然失败时返回 `MATH_ENGINE_UNUSABLE` 并携带**机读诊断** `probe: {argv, exit, timedOut, ms, spawned, stderrTail, attempts, retried, retryDiag}`;**引擎已找到就绝不再给"去装引擎"的指引**(那会误导),改为 `next.kind:'note'`。包预检同样重试;若预检**始终跑不起来**,**不判缺包、不阻塞**,给出 `PACKAGE_PRECHECK_UNKNOWN` 警告(附 attempts/argv)——"探测失败"永远不会被读成"包没装"。
130
+ - **包存在性探测的 argv 形态**:python 的探针代码遍历 `sys.argv[1:]`,所以包名按**每个一个 argv 项**传入(`<pkgs...>`);R/Julia/Octave 的模板把逗号串插进代码(`<pkgs>`/`__PKGS__`)。真机上曾经把 `numpy,pandas` 当成**一个**包名,导致"已装的包也报缺"。
131
+ - **探针 argv 里不得出现 shell 元字符(round-7 实测)**:真实宿主的 subprocess 通路会**拒绝**含 `|`(以及 `%`)的 argv——表现为一次**空 spawn**(`exit=null`),而不是报错。因此所有探测/许可证代码都改成**不含 `|`/`%`** 的形式(每包一行 `名称 ok|missing`),解析器两种形态都接受(`名称:状态` 与 `名称 状态`)。**新增/修改任何探针模板时都要保持这条**。
132
+ - **响应必须永远可 JSON 序列化(round-7 回归)**:诊断对象曾被写成自引用(`retryDiag` 指向父对象),导致 `JSON.stringify(response)` 抛 `Converting circular structure to JSON`,调用方**看不到任何结果**。现在诊断经 `jsonSafeDiag()` 投影为纯字段(`argv/exit/timedOut/ms/spawned/stderrTail/attempts/retried/retryDiag`),共享套件有一条**横切不变量**:`probe`/`run`/`receipt`/`install` 的 13 种响应(成功与失败)都必须能 `JSON.stringify`。**新增任何响应字段都要过这条。**
133
+ - **探测的 cwd 必须"一定存在"(round-7 真实根因)**:版本探测/包预检/许可探测曾用 `projectRoot()` 作 cwd;**全新会话里该目录还不存在**,而宿主对"cwd 不存在"的 spawn 返回 `spawned:true` + `exit:null`(**重试无用**——同 cwd 同结果),于是新会话第一次 `probe` 会得到"引擎存在但不可用"。现在这三类探测统一走 `probeCwd(H)`:优先宿主可选字段 `probeCwd`,否则 **OS 临时目录**(`TEMP`/`TMPDIR`/`TMP`),最后才回退到项目根;**真正的 `run` 仍用项目根**(run 会先写回执,写盘本身会创建目录树,所以它一直是好的)。
134
+
135
+ ## 6. 插件**能**与**不能**强制的东西
136
+
137
+ **能强制**:入参 schema(闭合;未知键拒绝)、**词法级**项目内路径守卫(见下)、cwd、超时并终止进程、输出上限、回执落盘、插件自身的写域(只写 `Computation/`)、安装两步与作用域默认、同一 archive id 的**串行分配**(并发运行不会共用 attempt 目录)。
138
+
139
+ **不能强制**(如实声明,勿当成承诺):
140
+ - **路径守卫是字符串级的**:`projectRel` 只做词法规范化(拒绝绝对路径、盘符、`..` 越界),**不做 realpath、不解析符号链接/junction/硬链接**。项目内一个指向外部的链接可以绕过该守卫拿到 `mode:'file'` 的读取与执行;真正的容器化由宿主 `ctx.fs` 后端负责(`resolve`/`contains`),插件不重复实现。审计 A 的用例把这一**限制**钉成了断言(`tests/math-computation-shared.test.mjs` §16c)。
141
+ - 脚本内部的网络访问与文件访问——宿主 `subprocess.spawn` **没有 policy 槽**(`env` 层只做凭据/`DSH_*` 清洗与显式合并,不能限制网络或写盘;参见 `dsh-subprocess-local` 的 `runner-launch-*.js` 与 `vibe-math-v5.js` 中 audit M8 注释),任何被执行的计算进程都在宿主 fs 策略之外;
142
+ - `mathMode` 只是提示词策略:**模型仍可能直接调用宿主 shell**,插件无法阻止,也无法为 shell 路径生成回执。因此默认策略要求:走 shell 得出的结论必须标注"**未经工具归档(shell 路径)**",**只有工具路径的计算才算可复核支撑材料**;
143
+ - **替代必须声明(提示词规则,同样不是强制)**:当替代方案改变了**精确性或结论强度**(精确符号解 → 数值近似、闭式解 → 采样/求积、改了精度/容差/假设、换了算法类),结论里**必须写明**,不得读起来像得到了原本(精确/所要求的)结果;拿不到精确结果就直说。规则行由 `MATH_SUBSTITUTION_RULE_LINE`/`_EN` 提供(已并入 `MATH_RULE_LINES`/`_EN` 与四套 persona 两个文本块),但**能否照做取决于模型**——插件只能要求,不能验证。
144
+ - 商业引擎的 CLI 模板随版本变化(Maple 尤甚):描述符带 `VERIFY` 标记,**每次运行都回显实际 argv**;若引擎以用法/选项错退出,返回 `MATH_ENGINE_BAD_ARGV` + `next.kind='engine-override'`(指向 `mathEngineOverride` 覆盖模板),而不是裸 `MATH_NONZERO_EXIT`。有许可的机器应验证这三个模板。
145
+ - 真强制(禁网/限权)需要宿主 sandbox/policy 支持——**列为待上游需求**,不在本轮范围。
146
+
147
+ **验证边界(诚实声明)**:引擎执行本身、各引擎的真实 argv/版本/许可路径与安装器命令**只通过假 subprocess seam 验证**(`tests/helpers/math-computation-fake-seam.mjs`),**没有**在真实 python/R/octave/julia/matlab/maple/wolfram 上跑过;真机验证需要一台装有这些引擎的机器(安装计划、模板与许可探测的**静态**面已在 `guards.md` 与 parity 守卫里冻结)。
148
+
149
+ **并发下的完整性**:同一 archive id 的分配与"占用"(receipt.json 落盘)在**一个插件实例内**是串行的,因此两个并发同 id 运行会分别拿到 attempt 1 / attempt 2,绝不会共用目录。**跨进程**并发仍依赖宿主的独占创建原语(当前 fs 接口没有暴露,列为已知限制)。
150
+
151
+ **父目录由宿主创建(round-3 记录,勿再当缺陷)**:宿主的文本写会**自动创建缺失的父目录**——`dsh-fs-local/lib/index.js` 的 `writeFileAtomic` 在落盘前执行 `mkdir(dirname(absolutePath), { recursive: true })`。因此 `Computation/<runId>/`、`Computation/<runId>/attempts/<n>/` 以及异步作业镜像 `Formal/Jobs/` **不需要预先存在**;`ensureDirs()` 仍然列出它们只是为了空项目里的可发现性(v2/v3/v4/v5 现已都含 `Formal/Jobs` 与 `Computation`)。真机上"严格 fs 拒绝创建父目录"的实验与真实宿主行为矛盾,不能作为失败证据。
152
+
153
+ ## 7. `cli` 逃生口为什么默认开启
154
+
155
+ 模型本来就有宿主 shell 可用(默认 `mathMode='typed+shell'` 就允许把它当兜底),所以 `cli` **不扩大爆炸半径**;区别在于 `cli` 仍在工具内:照样有 cwd、超时、输出上限、路径守卫与回执(`engine` 记为 `cli:<command>`)。要收紧:把 `mathMode` 设为 `typed`,或从 `mathEngines` 去掉 `'cli'`——两种情况下 `engine:'cli'` 都返回 `MATH_REFUSED`。
156
+
157
+ ## 8. 与形式化验证(Lean)的关系
158
+
159
+ `math_computation` 产出**经验证据**(回执可复核);形式化证明仍是**可选**的 Lean 路径(`formalVerify: off|encourage|require`,默认 `off`)。计算回执**不能**顶替 Lean 的 `passed`/`blocked`,也不能把对象标为"已验证"。
160
+
161
+ ## 9. 参数配置在哪改
162
+
163
+ `vibe_math_set_params`(v2/v3)、`vibe_v4_set`(v4)、`vibe_v5_set`(v5)以及各自的控制面:v2/v3 是 `vibe_math_setup`(交互式 schema)/`vibe_math_status`/`vibe_math_template`/`vibe_math_save_settings`,v4 是 `vibe_v4_configure`/`vibe_v4_status`(**没有** `template`/`setup`),v5 是 `vibe_v5_configure`/`vibe_v5_status`(同样没有 `template`/`setup`);六个参数名在四套、README 双语参数表与本文中拼写一致(由 `tests/audit-math-computation-parity.mjs` 与 `audit-math-computation-contract.mjs` 盯着)。
@@ -0,0 +1,151 @@
1
+ # dsh-vibe-math 2.7.0 — Release Notes (English)
2
+
3
+ > Previous release: 2.6.0. This release adds two capabilities: **incremental Lean formalisation
4
+ > (async compilation + `import` reuse)** and the **`math_computation` tool** (one shared
5
+ > implementation for all four presets). Existing parameter defaults, data formats and directory
6
+ > structure are **unchanged** (the release only adds parameters and directories), the DSH support
7
+ > window is unchanged, and no migration is needed.
8
+
9
+ ---
10
+
11
+ ## Overview
12
+
13
+ - **Incremental Lean formalisation**: `leanAsync` (default `true`) compiles on a per-session
14
+ background queue and returns as soon as the job is queued; new `leanJobsMaxParallel`
15
+ (default `1`), `leanInitiative` (`off|normal|eager`, default `normal`) and `leanSearchPaths`
16
+ (default `[]`). New read-only tools `lean_read` / `lean_job`; jobs are mirrored to
17
+ `Formal/Jobs/<jobId>.json`.
18
+ - **`math_computation` (new tool)**: `op:'probe'` pre-checks engines/packages/licences, `op:'run'`
19
+ computes, and every run is archived as a reviewable receipt; `op:'install'` is a two-step
20
+ plan-then-confirm flow and `op:'receipt'` re-checks a receipt read-only.
21
+ - **Engines**: `python` / `r` / `octave` / `julia` / `matlab` / `maple` / `wolfram` / `cli`.
22
+ The three commercial ones are **detected and licence-checked only and are never installed**;
23
+ `cli` is on by default (two documented ways to disable it).
24
+ - **Archive → edit → re-run**: receipts carry `scriptPath`/`scriptHash`; after editing a script you
25
+ must re-run it with `mode:'file'`, because an old receipt is not evidence for edited code
26
+ (`scriptChanged` / `scriptChangedDuringRun` warn explicitly).
27
+ - **Six new parameters**: `mathComputation` / `mathMode` / `mathEngines` / `mathTimeoutMs` /
28
+ `mathPackages` / `mathInstallScope` (the bilingual README table and `docs/math-computation.md`
29
+ are authoritative).
30
+ - This release went through **four independent audit rounds** plus a **real-host packaging/boot
31
+ certification on the frozen commit**; the gate is `TOTAL 65 PASS 65 FAIL 0`
32
+ (44 suites + 21 probes).
33
+
34
+ ## Changes
35
+
36
+ ### 1. Incremental Lean formalisation + async compilation + `import` reuse
37
+
38
+ - **New parameters** (identical in all four presets; every default preserves existing behaviour):
39
+ - `leanAsync`: default `true`. `true` = compile on a **per-session background queue** and return
40
+ as soon as it is queued; `false` = synchronous `await` (the old semantics).
41
+ - `leanJobsMaxParallel`: default `1` (serial). Concurrency cap for background compilations.
42
+ - `leanInitiative`: `off | normal | eager`, default `normal`. **How eager the staff are about
43
+ formalising during normal work.**
44
+ - `leanSearchPaths`: default `[]`. Extra `--search-path` roots; when non-empty they are injected
45
+ **first** and the automatic root is injected **after** them.
46
+ - **Queue state machine**: jobs move through explicit states, and **only `settled(ok)` counts as
47
+ passed** - "queued" or "compiling" never counts as proved.
48
+ - **`import` reuse**: the compile command automatically injects `--search-path <VibeMath root>`, so
49
+ `import Formal.Lib.<name>` / `Formal.Proved.<name>` work directly (cross-project reuse of archived
50
+ definitions and lemmas).
51
+ - **New read-only tools**: `lean_read` (read archive/library content) and `lean_job` (job status).
52
+ Both are **read-only** and never trigger a compilation.
53
+ - **Job mirror**: every job keeps a mirror at `Formal/Jobs/<jobId>.json`, so a restart can re-queue
54
+ or explicitly interrupt it.
55
+ - **Honest note**: `leanInitiative` and `formalVerify` are **two independent axes**.
56
+ `formalVerify:'off'` disables the **verification stage**; it does not swallow the initiative axis:
57
+ `off + normal` ⇒ no Lean text at all; `off + eager` ⇒ **only** the daily line (the proactivity
58
+ prompt) appears, while the verification stage stays off.
59
+
60
+ ### 2. The new `math_computation` tool
61
+
62
+ - **Six frozen parameters** (the names cannot change; identical spelling across the four presets,
63
+ the bilingual README table and the contract doc):
64
+ | Parameter | Default | Meaning |
65
+ |---|---|---|
66
+ | `mathComputation` | `auto` | `off` (a true no-op) / `auto` (works once an engine is detected) / `on` |
67
+ | `mathMode` | `typed+shell` | **Prompt policy**: `typed` never mentions the shell and disables `engine:'cli'` |
68
+ | `mathEngines` | `[python,r,octave,julia,matlab,maple,wolfram,cli]` | Allowed set and probe order; removing `cli` closes the escape hatch |
69
+ | `mathTimeoutMs` | `60000` | Per-run budget (minimum 1000); on timeout the process is **actively terminated** |
70
+ | `mathPackages` | `[]` | Packages required by default; a missing one is reported with a plan, **never auto-installed** |
71
+ | `mathInstallScope` | `user` | Install scope; `system` must be passed explicitly every time and is **never remembered** |
72
+ - **`op`**: `probe` (engines/versions/packages/licences), `run` (`mode: code|file|expr`),
73
+ `receipt` (read-only re-check), `install` (two-step).
74
+ - **Failure codes (11, frozen)**: `MATH_NOT_AVAILABLE`, `MATH_ENGINE_NOT_FOUND`,
75
+ `MATH_ENGINE_LICENSE_REQUIRED`, `MATH_ENGINE_UNUSABLE`, `MATH_MISSING_PACKAGES`, `MATH_TIMEOUT`,
76
+ `MATH_NONZERO_EXIT`, `MATH_ENGINE_BAD_ARGV`, `MATH_REFUSED`, `MATH_INVALID_ARGUMENT`,
77
+ `MATH_NO_SUBPROCESS`; each carries a machine-readable `next` (`user-install` / `agent-install` /
78
+ `vendor` / `enable` / `engine-override` / `reason` / `note`).
79
+ - **Deterministic receipts**: `runId` is derived only from preset + project + engine + mode + key +
80
+ sorted packages and contains **no wall clock**; `argv` is echoed **both** in the receipt and in the
81
+ return value (copy it to re-run); full `stdout`/`stderr` land on disk while the return value is
82
+ capped at 64 KB.
83
+ - **Engines**: `python`/`r`/`octave`/`julia` are free; `matlab`/`maple`/`wolfram` are **commercial** -
84
+ detected and licence-checked only, **never installed** (vendor guidance only); `cli` runs the
85
+ command you name, still under the tool's timeout, output caps, cwd and receipt.
86
+ - **Two installation routes**: ① user self-install (per-OS commands + official links); ② agent
87
+ install via `plan → planToken → confirm` (the token binds the plan content, so a changed plan
88
+ invalidates it). Scope defaults to **user**; `system` must be confirmed **every time**; each
89
+ execution writes an audit file `Computation/installs/<planToken>.json` (exact commands, exit codes,
90
+ before/after versions and **uninstall templates**). Package installation has **no universal
91
+ rollback** - the tool guarantees verifiable commands/versions plus uninstall templates, not
92
+ automatic rollback.
93
+ - **`cli` is on by default** (it is no broader than the host shell the model already has, but it adds
94
+ receipts and limits); two documented ways to disable it: `mathMode:'typed'`, or removing `'cli'`
95
+ from `mathEngines` - both return `MATH_REFUSED`.
96
+ - **Archive → edit → re-run (integrity rules)**:
97
+ - the return value and the receipt both carry `scriptPath`/`scriptHash`; the script original can be
98
+ opened and edited;
99
+ - re-run it with `mode:'file'` after editing ⇒ a **new receipt** (new attempt, new hash); **an old
100
+ receipt is not evidence for edited code**;
101
+ - `scriptChanged` (the current file differs from the previous receipt) and
102
+ `scriptChangedDuringRun` (the file changed while the run was in flight) **warn explicitly** and
103
+ are recorded in the receipt;
104
+ - archives are **append-only** (`Computation/<id>/`, attempt n≥2 under `attempts/<n>/`) and
105
+ existing files are **never overwritten**;
106
+ - the retention caps (20 attempts / 200 runs) **warn only and never delete**; unknown parameter
107
+ keys in a hand-edited state file are **reported** (`diagnostics`) instead of being dropped
108
+ silently.
109
+
110
+ ### 3. Honest boundaries (please read)
111
+
112
+ - **Engine execution is verified only through a fake subprocess seam**: no real python/R/octave/julia/
113
+ matlab/maple/wolfram was run on this machine; real-engine verification needs a machine with those
114
+ engines installed (the **static** surface - templates, licence probes, install plans - is pinned by
115
+ guards).
116
+ - **The plugin cannot enforce network or write restrictions**: the host's `subprocess.spawn` has
117
+ **no policy slot** (its `env` layer only scrubs credentials/`DSH_*` names and cannot restrict
118
+ network or disk access); real enforcement needs host sandbox support and is **recorded as a pending
119
+ upstream requirement**.
120
+ - **Commercial templates are marked `VERIFY`**: the CLI templates for Maple (especially), MATLAB and
121
+ Wolfram change between versions, so their descriptors carry a `VERIFY` flag; every run echoes the
122
+ real argv, and a usage/option error returns `MATH_ENGINE_BAD_ARGV` with a `mathEngineOverride`
123
+ hint instead of a bare `MATH_NONZERO_EXIT`. Machines with licences should verify those three
124
+ templates themselves.
125
+ - **SageMath is P2**: this release only has the descriptor placeholder; it is not wired in.
126
+ - **The two comparison experiments are given as run-books** (sync vs async compilation; without vs
127
+ with `import` reuse): these notes contain **no fabricated numbers** and do not claim that a
128
+ real-Lean-corpus comparison was completed.
129
+
130
+ ### 4. Review and verification
131
+
132
+ This release went through **four independent audit rounds** (adversarial re-falsification, cross
133
+ audit, user on-ramp, fresh-lens review) and a **real-host packaging/boot certification on the frozen
134
+ commit**; the gate is `TOTAL 65 PASS 65 FAIL 0` (**44 suites + 21 probes**), plus 22 module-level
135
+ mutation probes that each require a *named* assertion to turn red.
136
+
137
+ ## Compatibility
138
+
139
+ - Existing parameter defaults, data formats and directory structure are **unchanged**; every new
140
+ parameter defaults to "does not change existing behaviour" (`leanAsync=true` merely moves
141
+ compilation to a background queue, `leanInitiative='normal'` keeps the previous proactivity).
142
+ - New directories `Computation/` and `Formal/Jobs/`; the host's text write **creates missing parent
143
+ directories**, so nothing has to be pre-created by hand.
144
+ - The DSH support window is unchanged (the `>=0.1.7` patch row and the `<=0.1.6` directory row both
145
+ ship the shared module).
146
+
147
+ ## Upgrade
148
+
149
+ - Upgrade directly: **no migration** - no parameter was renamed and no state-file format changed.
150
+ - To turn the new capabilities off: `mathComputation:'off'` (a true no-op), `leanAsync:false` (back to
151
+ synchronous compilation), `leanInitiative:'off'` (no proactive formalisation).
@@ -0,0 +1,115 @@
1
+ # dsh-vibe-math 2.7.0 — 发布说明(中文)
2
+
3
+ > 上一版:2.6.0。本版新增两块能力:**Lean 增量形式化(异步编译 + `import` 复用)** 与
4
+ > **`math_computation` 数学计算工具**(四套预设共享同一实现)。既有参数默认值、数据格式与目录结构
5
+ > **未变**(只是新增参数与目录),DSH 支撑窗口不变,无需迁移。
6
+
7
+ ---
8
+
9
+ ## 概览
10
+
11
+ - **Lean 增量形式化**:`leanAsync`(默认 `true`)把编译放后台队列,入队即返回;新增并发上限
12
+ `leanJobsMaxParallel`(默认 `1`)、日常主动性 `leanInitiative`(`off|normal|eager`,默认 `normal`)
13
+ 与额外搜索路径 `leanSearchPaths`(默认 `[]`)。新增只读工具 `lean_read` / `lean_job`,
14
+ 作业镜像写 `Formal/Jobs/<jobId>.json`。
15
+ - **`math_computation`(新工具)**:先用 `op:'probe'` 预检引擎/包/许可,再 `op:'run'` 计算,
16
+ 每次执行都归档成可复核回执;`op:'install'` 走"计划 → 确认"两段式,`op:'receipt'` 只读复核。
17
+ - **引擎**:`python` / `r` / `octave` / `julia` / `matlab` / `maple` / `wolfram` / `cli`。
18
+ 商业三家**只探测与查许可,永不代装**;`cli` 默认开启(两个文档化的关闭路径)。
19
+ - **归档→编辑→重跑**:回执带 `scriptPath`/`scriptHash`;改动脚本后必须用 `mode:'file'` 重跑,
20
+ 旧回执对修改后的代码无效(`scriptChanged` / `scriptChangedDuringRun` 显式告警)。
21
+ - **六个新参数**:`mathComputation` / `mathMode` / `mathEngines` / `mathTimeoutMs` / `mathPackages` /
22
+ `mathInstallScope`(README 双语参数表与 `docs/math-computation.md` 为准)。
23
+ - 本版经过**四轮独立审查** + 冻结提交上的**真机打包/启动认证**;门禁 `TOTAL 65 PASS 65 FAIL 0`
24
+ (44 套件 + 21 探针)。
25
+
26
+ ## 变更
27
+
28
+ ### 1. Lean 增量形式化 + 异步编译 + `import` 复用
29
+
30
+ - **新参数**(四套一致,默认值即"安全且不改变既有行为"):
31
+ - `leanAsync`:默认 `true`。`true` = 编译进**每会话后台队列**,入队即返回;`false` = 同步 `await`(旧语义)。
32
+ - `leanJobsMaxParallel`:默认 `1`(串行)。后台编译的并发上限。
33
+ - `leanInitiative`:`off | normal | eager`,默认 `normal`。**日常流程中的形式化主动性**。
34
+ - `leanSearchPaths`:默认 `[]`。额外的 `--search-path` 根;非空时**先**注入它们,**再**注入自动根。
35
+ - **队列状态机**:作业有明确的状态迁移;**只有 `settled(ok)` 才算 passed**——"入队成功/正在编译"
36
+ 一律不计入通过,也不会被当成已证。
37
+ - **`import` 复用**:编译命令自动注入 `--search-path <VibeMath 根>`,因此
38
+ `import Formal.Lib.<name>` 与 `Formal.Proved.<name>` 可以直接工作(跨项目复用已归档的定义/引理)。
39
+ - **新只读工具**:`lean_read`(读归档/库内容)与 `lean_job`(查作业状态),都是**只读**,不会触发编译。
40
+ - **作业镜像**:每个作业在 `Formal/Jobs/<jobId>.json` 留一份镜像,进程重启后可恢复或显式中断。
41
+ - **诚实说明**:`leanInitiative` 与 `formalVerify` 是**两条独立的轴**。
42
+ `formalVerify:'off'` 关闭的是**验证阶段**,不会吞掉主动性轴:
43
+ `off + normal` ⇒ 一个字都不提 Lean;`off + eager` ⇒ **只**出现日线(主动性提示),验证段仍然关闭。
44
+
45
+ ### 2. 新工具 `math_computation`
46
+
47
+ - **六个冻结参数**(名字不可改;四套、README 双语表、契约文档拼写一致):
48
+ | 参数 | 默认 | 说明 |
49
+ |---|---|---|
50
+ | `mathComputation` | `auto` | `off`(真 no-op)/ `auto`(探测到才工作)/ `on` |
51
+ | `mathMode` | `typed+shell` | **提示词策略**:`typed` 不提 shell 且禁用 `engine:'cli'` |
52
+ | `mathEngines` | `[python,r,octave,julia,matlab,maple,wolfram,cli]` | 允许/探测顺序;去掉 `cli` 即关闭逃生口 |
53
+ | `mathTimeoutMs` | `60000` | 单次上限(最小 1000);超时**主动终止**进程 |
54
+ | `mathPackages` | `[]` | 默认要求的包;缺包只报告 + 给计划,**不自动装** |
55
+ | `mathInstallScope` | `user` | 安装作用域;`system` 必须每次显式指定、**不被记住** |
56
+ - **`op`**:`probe`(预检引擎/版本/包/许可)、`run`(`mode: code|file|expr`)、`receipt`(只读复核)、
57
+ `install`(两段式安装)。
58
+ - **失败码(11 个,冻结)**:`MATH_NOT_AVAILABLE`、`MATH_ENGINE_NOT_FOUND`、`MATH_ENGINE_LICENSE_REQUIRED`、
59
+ `MATH_ENGINE_UNUSABLE`、`MATH_MISSING_PACKAGES`、`MATH_TIMEOUT`、`MATH_NONZERO_EXIT`、
60
+ `MATH_ENGINE_BAD_ARGV`、`MATH_REFUSED`、`MATH_INVALID_ARGUMENT`、`MATH_NO_SUBPROCESS`;
61
+ 每个都带机读 `next`(`user-install` / `agent-install` / `vendor` / `enable` / `engine-override` /
62
+ `reason` / `note`)。
63
+ - **确定性回执**:`runId` 只由"预设 + 项目 + 引擎 + 模式 + 键 + 排序后的包"决定,**不含墙钟**;
64
+ `argv` 在回执与返回体里**双回显**(可照抄复跑);`stdout`/`stderr` 全量落盘、返回体截 64KB。
65
+ - **引擎**:`python`/`r`/`octave`/`julia` 为自由引擎;`matlab`/`maple`/`wolfram` 为**商业**——
66
+ 只做探测与许可检查,**永不代装**(只给厂商指引);`cli` 用你指定的命令执行,仍受本工具的超时、
67
+ 输出上限、cwd 与回执约束。
68
+ - **两条安装路径**:① 用户自装(逐 OS 命令 + 官方链接);② 代理代装 `plan → planToken → confirm`
69
+ (token 绑定计划内容,内容变了即失效)。默认 **user 作用域**,`system` **每次都要显式确认**;
70
+ 每次执行写审计 `Computation/installs/<planToken>.json`(含确切命令、退出码、before/after
71
+ 与**卸载命令模板**)。包安装**没有通用回滚**——工具保证的是"命令与版本可核验 + 卸载模板",
72
+ 不做自动回滚。
73
+ - **`cli` 默认开启**(它不比模型本来就有的宿主 shell 更大,但多了回执与上限);两个关闭路径:
74
+ `mathMode:'typed'`,或把 `'cli'` 从 `mathEngines` 移除——两者都返回 `MATH_REFUSED`。
75
+ - **归档→编辑→重跑(完整性保证)**:
76
+ - 返回体与回执都带 `scriptPath`/`scriptHash`;脚本原件可以打开/编辑;
77
+ - 编辑后用 `mode:'file'` 重跑 ⇒ **新回执**(新 attempt、新哈希);**旧回执不代表修改后的代码**;
78
+ - `scriptChanged`(与上一份回执的哈希不一致)、`scriptChangedDuringRun`(运行期间被改动)都会
79
+ **显式告警**并写进回执;
80
+ - 归档是**追加式**的(`Computation/<id>/`,第 n≥2 次进 `attempts/<n>/`),已有文件**永不覆盖**;
81
+ - 保留上限(20 attempt / 200 run)**只告警、永不自动删除**;手改状态文件里的未知参数键会被
82
+ **报告**(`diagnostics`),不静默丢弃。
83
+
84
+ ### 3. 诚实边界(务必读)
85
+
86
+ - **引擎执行只用假 subprocess seam 验证**:本机**没有**跑过真实的 python/R/octave/julia/matlab/
87
+ maple/wolfram;真机验证需要一台装有这些引擎的机器(模板、许可探测与安装计划的**静态面**已被守卫冻结)。
88
+ - **插件不能强制禁网/限权**:宿主 `subprocess.spawn` **没有 policy 槽**(`env` 层只做凭据/`DSH_*`
89
+ 清洗,不能限制网络或写盘);真强制需要宿主 sandbox 支持,**列为待上游需求**。
90
+ - **商业模板标 `VERIFY`**:Maple(尤甚)、MATLAB、Wolfram 的 CLI 模板随版本变化,描述符带 `VERIFY`
91
+ 标记;每次运行都回显实际 argv,用法/选项错会返回 `MATH_ENGINE_BAD_ARGV` 并提示用
92
+ `mathEngineOverride` 覆盖模板(而不是裸 `MATH_NONZERO_EXIT`)。有许可的机器应自行验证这三个模板。
93
+ - **SageMath 属于 P2**:本版只有描述符占位,不接入。
94
+ - **两个对照实验以 run-book 形式给出**(同步 vs 异步编译;不使用 vs 使用 `import` 复用):
95
+ 本说明**不含**任何编造的实测数字,也不声称已完成真实 Lean 语料的对比。
96
+
97
+ ### 4. 审查与验证
98
+
99
+ 本版经过 **四轮独立审查**(对抗性再证伪、交叉审计、用户上手路径、新视角复审),
100
+ 并在**冻结提交上**完成**真机打包/启动认证**;门禁为 `TOTAL 65 PASS 65 FAIL 0`
101
+ (**44 套件 + 21 探针**),另有 22 个模块级变异探针要求"具名断言变红"。
102
+
103
+ ## 兼容性
104
+
105
+ - 既有参数默认值、数据格式与目录结构**未变**;新增参数都取"不改变既有行为"的默认值
106
+ (`leanAsync=true` 只是把编译改为后台队列,`leanInitiative='normal'` 保持原有主动性)。
107
+ - 新增目录 `Computation/` 与 `Formal/Jobs/`;宿主的文本写会**自动创建缺失的父目录**,
108
+ 无需预先手工建目录。
109
+ - DSH 支撑窗口不变(`>=0.1.7` 的 patch 行 + `<=0.1.6` 的目录行两条安装线都随包发布共享模块)。
110
+
111
+ ## 升级
112
+
113
+ - 直接升级即可,**无需迁移**:没有改名的参数、没有改格式的状态文件。
114
+ - 想关闭新能力:`mathComputation:'off'`(真 no-op)、`leanAsync:false`(回到同步编译)、
115
+ `leanInitiative:'off'`(不主动形式化)。
@@ -0,0 +1,86 @@
1
+ # dsh-vibe-math 2.7.1 — Release Notes (English)
2
+
3
+ > Previous release: 2.7.0. This is a PATCH release that ships two batches together. **Batch 1 (honesty and install policy)**: a substitute that changes exactness or conclusion strength must be declared; python installs dispatch to conda/mamba/uv/pip from the **detected environment**; R/Octave/Julia system-scope refusals state the **per-engine reason**; **version solving is delegated to the package manager** while the tool only checks presence. **Batch 2 (seven defects found by the first real end-to-end run)**: the probe now reports the requested engine, engine discovery also finds the runtimes **DSH ships itself**, the `cli` escape hatch prechecks against `cli.command`'s own interpreter, `mode:'code'` really passes the script (caller argv wins when it already runs code), versions are real, probe argv carries no shell metacharacters, and discovery probes no longer depend on the project directory existing. Existing parameter defaults, data formats and directory structure are **unchanged**, the DSH support window is unchanged, and no migration is needed.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ - **Declare substitutions**: a new rule line (zh/en) requires that a substitute changing exactness/conclusion strength be stated explicitly; it lands in **both persona blocks of all four presets**, in the rule block and in the tool description. Missing engines/packages explicitly lead to an **alternative or an install plan**, and installation always goes `plan → planToken → confirm`.
10
+ - **Python package-manager dispatch**: `conda` / `mamba` (`-y -c conda-forge`) / `uv` (`uv pip …`) / the **pip fallback** (`-m pip install --user`); detection reads the interpreter's **own environment** (detect, don't guess) and an ambiguous case falls back to pip with the **assumption** recorded in plan/message/audit.
11
+ - **System scope now explains itself**: `system` still returns `MATH_REFUSED` + `next.reason:'system-scope-unsupported'`, but the message states **why per engine** (R→`R_LIBS_USER`, Octave→`share/packages`, Julia→`JULIA_DEPOT_PATH`, plus conda/mamba/uv reasons).
12
+ - **Version constraints**: `name` / `name[extras]` / `name<op>version` are accepted; anything else is refused with the new reason `unsupported-version-syntax`. **Version solving belongs to the package manager** - the tool probes presence by base name and passes the spec through verbatim.
13
+ - A real-host defect was fixed along the way: the install path resolved the interpreter by **engine name** (`python`) instead of the descriptor's candidate order (`python3`→`python`→`py`), which would have broken conda detection on a real machine.
14
+
15
+ - **Seven real-machine defects fixed** (all found by the first real end-to-end run, all closed on the same bytes): the probe echoed `python` for any requested engine; the bundled DSH runtime was invisible; the `cli` precheck ignored `cli.command`; `mode:'code'` never passed the archived script; `engine.version` was `"unknown"`; probe argv contained `|`/`%` (which a real host answers with a null spawn); and probes ran with a cwd that does not exist on a brand-new workspace (the single root cause behind "found but unusable" and the false "missing package").
16
+ - **Two permanent guards added**: §20 the JSON-serialisability invariant over 13 response shapes, and §21 the fresh-root simulation (a spawn whose cwd does not exist answers `exit:null`).
17
+
18
+ ## Changes
19
+
20
+ ### 1. Honesty about substitutions
21
+
22
+ - **The rule**: when an alternative changes **exactness or conclusion strength** - exact symbolic solution → numerical approximation, closed form → sampling/quadrature, changed precision/tolerances/assumptions, a different algorithm class - the conclusion **MUST say so** and must not read as if the original (exact/requested) result had been obtained; if the exact result is unavailable, say so plainly.
23
+ - **Where it lives** (one constant per language): `MATH_SUBSTITUTION_RULE_LINE` / `MATH_SUBSTITUTION_RULE_LINE_EN`, folded into `MATH_RULE_LINES`/`_EN` (so `mathAvailabilityLine()` carries them), with a paraphrased sentence in the tool description; **both persona text blocks of all four presets append them verbatim** (append, never replace an existing rule line).
24
+ - **Missing engines/packages**: still report-only, and now explicitly offer an **alternative or an agent install plan**; installation always issues a plan first and requires **`planToken` confirmation** (the token binds the plan content, so a changed plan invalidates it), scope defaults to user, and `system` must be given explicitly every time and is never remembered.
25
+
26
+ ### 2. Python package-manager dispatch
27
+
28
+ The `python` install plan picks the manager from the **interpreter's own environment** instead of hard-coding pip:
29
+
30
+ | Detected environment | Commands in the plan | Basis |
31
+ |---|---|---|
32
+ | the interpreter path carries a conda marker (`/envs/`, `/conda`, `/miniconda*`, `/anaconda*`, `/mambaforge`, `/miniforge`) and `conda` resolves | `conda install -y -c conda-forge <pkg>` (uninstall `conda remove -y <pkg>`) | path marker + resolvability |
33
+ | same, but only `mamba` resolves | `mamba install -y -c conda-forge <pkg>` (uninstall `mamba remove -y <pkg>`) | as above |
34
+ | `uv` sits **next to** the interpreter (a uv-managed venv) | `uv pip install <pkg>` (uninstall `uv pip uninstall <pkg>`) | same directory only |
35
+ | anything else | `python -m pip install --user <pkg>` (uninstall `python -m pip uninstall -y <pkg>`) | **documented fallback**, plan flagged `managerAssumed` |
36
+
37
+ - **Detect, don't guess**: the plan, the returned message and the audit JSON all carry `manager` / `managerAssumed` / `managerWhy`; an ambiguous case falls back to pip and says "the plan assumes pip".
38
+ - **Install and uninstall stay paired**: the uninstall command comes from the same chosen manager (the audit carries the uninstall template; there is still **no universal automatic rollback**).
39
+ - **A real defect fixed on the way**: the install path used to resolve the interpreter by **engine name** (`python`) rather than the descriptor's **candidate order** (`python3`→`python`→`py`) - on a real host the conda `python3` *is* that interpreter, so dispatch would not have seen the environment markers. It now resolves exactly like the run path.
40
+
41
+ ### 3. Per-engine system-scope policy
42
+
43
+ - R / Octave / Julia are **user-scope only**; `scope:'system'` returns `MATH_REFUSED` + `next.reason:'system-scope-unsupported'`, and the message gives the **engine-specific reason**:
44
+ - **R**: the user library is `R_LIBS_USER`; a system install would write into the distribution's package tree (root or the distro package manager).
45
+ - **Octave**: `pkg install` writes to the user package directory; system-wide would write Octave's `share/packages` (root).
46
+ - **Julia**: `Pkg.add` installs into the active environment / user depot (`JULIA_DEPOT_PATH`); "system scope" is not a Julia concept.
47
+ - **conda / mamba / uv** likewise have no system template and each carries its own reason; **python on pip keeps a real system template** (without `--user`), confirmed explicitly on every call.
48
+
49
+ ### 4. Version-constraint policy
50
+
51
+ - **Accepted**: `name`, `name[extras]`, `name<op>version` where `<op>` ∈ `==` `>=` `<=` `~=` `!=` `>` `<` `=` (pip-style `numpy==1.2`, conda-style `numpy=1.2`).
52
+ - **Refused**: spaces, `;`, `|`, `&`, `$`, backticks, `@`, parentheses - anything shell-unsafe or unknown to the manager ⇒ `MATH_INVALID_ARGUMENT` + `next.reason:'unsupported-version-syntax'` (a suspicious string is **never** handed to a shell).
53
+ - **Division of labour**: **version solving belongs to the package manager** - the tool probes and reports missing packages by **base name**, passes the spec through **verbatim** in the install plan, and states this in the plan as `versionPolicy`.
54
+ - **Vocabulary**: `unsupported-version-syntax` is the new machine-readable reason; the vocabulary is now **13 reasons / 7 `next.kind` values / 11 failure codes**, kept in **set-equality** across `tool-schema.json#refusalVocabulary`, `docs/math-computation.md` §3 and the parity guard (which also requires every `reason:` value to be a string literal).
55
+
56
+ ### 5. Seven real-machine defects (one root cause, three symptoms)
57
+
58
+ 1. **`op:'probe'` ignored the requested engine.** The response carried `engine: null` (or the first *allowed* engine), and the failure path always blamed `params.mathEngines[0]` (python), so `probe{engine:'r'}` echoed python and told the user to install python. It now resolves the **requested** engine and reports its name/path/version; a requested engine that resolves (including `cli`) counts as available, and a missing requested engine is reported — and guided — as *that* engine.
59
+ 2. **Engine discovery missed the runtimes DSH ships itself.** PATH is still tried first; when nothing resolves and the host declares the optional `runtimeRoots` + `listDirAbs` fields, the tool finally scans `<root>/dsh-runtimes/*/dependencies/<engine>/` — the **tree name is globbed** (never a hard-coded `dsh-primary-runtime`) and only the descriptor's own candidate names are accepted. A found engine now reports its **real version** and **no install guidance is emitted** (a winget hint for an engine that is already installed is exactly what misled the user).
60
+ 3. **The `cli` package precheck ignored `cli.command`.** The generic `cli` descriptor has no package probe, so every package looked missing and the escape hatch blocked itself. The precheck now runs for the **family** of the resolved command (`python*`/`Rscript`/`octave`/`julia`), against that very interpreter; an unrecognisable family **skips** the precheck with an explicit warning instead of reporting a false "missing".
61
+ 4. **`mode:'code'` never passed the script.** `cli` received only the command, so python dropped into a REPL (`exit=0`, empty stdout — which reads as success). For `mode:'code'`/`'file'` the archived script path is now appended to argv; **when the caller's argv already provides a program slot (`-c`/`-m`/`-e`/`--eval`/`--command`), the caller's argv wins** and nothing is appended (otherwise the script would be fed as an extra argument). The receipt records `cli.scriptAppended` and, when skipped, `cli.scriptSkipped` with the reason.
62
+ 5. **`engine.version` was `"unknown"`.** For `cli` the version is now obtained with the family descriptor's real version probe (unknown families remain `"unknown"`).
63
+ 6. **Probe argv contained shell metacharacters.** A real host answers argv containing `|` (and `%`) with a **null spawn**. All probe codes were rewritten to be metacharacter-free (one line per package: `name ok|missing`) and the parser accepts both the old `name:state` and the new `name state` shapes; python still passes **one argv item per package**.
64
+ 7. **The root cause: probe cwd did not exist on a brand-new workspace.** Version probes, the package precheck and the licence probe spawned with `projectRoot()` as cwd — but on a fresh session that directory does not exist yet, and the host answers such a spawn with `spawned: true, exit: null` (retries cannot help: same cwd, same result). That single cause produced "found but unusable", the false "missing package", and `version: "unknown"`. Probes now use a cwd that is guaranteed to exist (`probeCwd`: optional host field → OS temp dir → project root); real runs keep the project root, which they create by writing the receipt first.
65
+
66
+ ### 6. Two new permanent guards
67
+
68
+ - **§20 JSON-serialisability invariant.** The probe diagnostics had a self-referential `retryDiag`, so `JSON.stringify(response)` threw `Converting circular structure to JSON` and the caller saw **no result at all**. Diagnostics are now projected to plain fields (`jsonSafeDiag`), and the shared suite asserts that **13 response shapes** (`probe`/`run`/`receipt`/`install`, success and failure, including the retried-failure `retryDiag` case) serialise.
69
+ - **§21 fresh-root simulation.** The fake seam models the host ("a spawn whose cwd does not exist answers `exit:null`", and `writeText` marks the directory as created), asserting that a probe on a fresh root is still `ok:true` with a real version, that no probe reports `exit:null`, and that the precheck still runs.
70
+
71
+ ### 7. Review and verification
72
+
73
+ - **Same discipline**: the four shared-module copies are **byte-identical** (`install-copies --check` ⇒ `COPIES OK`); `math-computation-shared` **278/0**, the four preset suites **114/116/153/117**, `audit-math-computation-contract` **155/0**, `audit-math-computation-parity` **92/0**, `audit-math-computation-sensitivity` **22 probes / 0 problems**, the mutation proofs **16 proved / 0 problems** (each turning a named assertion red); the gate is `TOTAL 65 PASS 65 FAIL 0` (**44 suites + 21 probes**).
74
+ - **Real-session evidence (fresh empty workspace, first tool call)**: `probe{engine:'python'}` ⇒ `ok:true`, `version 3.12.14`, path `…/dsh-runtimes/dsh-primary-runtime/dependencies/python/python.exe`, no `exit:null`; `run` via `cli` with the bundled interpreter and `packages:['numpy']` ⇒ `exit:0`, `stdout:"1.643935 -2.0"` (Σ1/k² for k=1..1000 and det([[1,2],[3,4]])), `packages.found.numpy:"present"`, `warnings:[]`, six receipt files.
75
+ - **Honest boundaries that still stand**: engine execution is now verified **via the `cli` escape hatch on the DSH-bundled runtime** (that is the only real-engine path proven here — the built-in engines are still covered only through the fake subprocess seam); `session/follow` token-level deltas, multi-client use and mid-turn `cancel` remain **unverified**; the host's `subprocess.spawn` has **no policy slot**, so the plugin cannot enforce network/write restrictions (that needs host sandbox support); the Maple/MATLAB/Wolfram templates are marked `VERIFY` with `mathEngineOverride` available; **SageMath is P2**; the two comparison experiments are **run-books** and these notes contain **no fabricated numbers**.
76
+
77
+ ## Compatibility
78
+
79
+ - Existing parameter defaults, data formats and directory structure are **unchanged**; this release only adds rule text, install-plan dispatch and one machine-readable reason - **no migration**.
80
+ - **The only behaviour change is inside install plans**: machines without conda/uv markers still use pip (as before); **a conda/mamba/uv environment now gets that manager's commands** (which is the point of this release), and the uninstall command comes from the same manager.
81
+ - The `system`-scope return code and `next.reason` are **unchanged** (only the message carries more information); the python+pip system template is still there.
82
+
83
+ ## Upgrade
84
+
85
+ - Upgrade directly; no migration.
86
+ - To keep install plans on **pip** always: run on an interpreter without conda/uv markers (or point at the system python). The tool never "guesses" a manager - it only dispatches when the interpreter's own environment says so.