R 线交付记录(R0–R8.5)
⚠️ 这是归档材料,不作为设计参考。 原文从「内部路线图(未公开)」的 §11.x 整段搬出(2026-10-08):路线图是规划文档,堆着已交付的每一步只会让它继续膨胀 (那份文件已经 1384 行)。路线图里只留「R 线现在在哪、下一步做什么」。 原文未做删改。R 线是什么、为什么做 → 「内部架构迁移评估(未公开)」。 R8.3–R8.5 的明细见文末「补记」(R8.3 起的原文仍在路线图 §11.5–§11.10)。
11.1 ✅ R0 已交付(2026-09-30):一致性夹具
做了什么:把 Python 前端现在的行为逐字冻结成结构化基线 —— tokens / ast / bytecode / diagnostics 四个阶段,73 个语料。 工具 tools/conformance.py(freeze / check / list)、基线 tests/conformance/、守门测试 tests/test_m57_conformance.py(17 项)、 说明 docs/一致性夹具.md(内部)。
| 项 | 数字 |
|---|---|
| 语料 | 73 个(语言 45 / 示例 24 / 基石库层 4) |
| 四阶段全通过 | 58 个;15 个在编译期报错(词法 5 / 语法 10) |
| 冻结规模 | tokens 12,623 · AST 节点 7,444 · 指令 9,047 · 指令集 50 条 |
| 耗时 / 体积 | freeze、check 各约 1.4 秒;产物 4.2 MB(gzip 后 0.3 MB) |
| Python 3.10.9 vs 3.13.14 | 产物逐项一致(73/73) |
🔴 立规矩时立刻抓到两件事(都写进夹具文档):
- 差点把 bug 冻成标准:我把
serialize.dump_json写成dumps_json,
73 个语料的 bytecode 全变成「编译阶段:<内部错误:ImportError>」。 于是加了硬闸门:前端抛非基石错误一律拒绝冻结(那是实现 bug,不是语言行为)。 —— 自动化工具分不清「事实」与「事故」,所以「把当前状态升格为标准」的动作 必须有这道闸门。
- 同一课在 M49 也上过:
fetch_release_mirror.py拿上一版的SHA256SUMS
校验新包(文件名不随版本变 + 「大小一致就跳过」),四条校验全被静默跳过。 已改成硬错误:清单里没有这个资产名 → 拒绝发布。 (「跳过校验」与「校验通过」在 CI 里长得一模一样。)
📌 3.10 与 3.13 产物一致这条对 CI 排障有直接价值:它说明 CI 里 Python 3.10 的失败不是前端语义差异。
🔴 顺着这条线索查到了 CI 3.10 一直红的根因(2026-09-30 当天修掉): tools/build_home.py 里有 12 处 {code('…\n…')} —— PEP 701 是 Python 3.12+ 才允许 f-string 表达式含反斜杠,3.10/3.11 直接 SyntaxError(import build_home 失败 → 连带 7 个用例全红)。 本地是 3.13,永远看不到。 修法:含反斜杠的字符串提到 f-string 外面; 并加了一条 AST 扫描测试(任何 Python 版本下都能发现)。 ⚠️ 顺带记一条口径:本地有 Rust 二进制、CI 不编译 Rust —— 本地结果不能 完全代表 CI。
11.2 ✅ R1 已交付(2026-10-01):前端契约固化
做了什么:把「前端每个阶段要产出什么」写成带版本号的契约,并给出一把 能定位的尺子。R1 的定义就是「不改行为,只定义接口」。
| 交付 | 说明 |
|---|---|
conformance.py compare --from <目录> | 外地产物 vs 基线(R2 起 Rust 每写一步都用它验收) |
--stage tokens | 只比某一阶段(R2 只做分词时用) |
| 差异明细带「这是什么元素」 | 从「tokens[2].col 5 vs 6」升级成「tokens[2].col —— OP「=」(第 2 行第 5 列) 基线 5 / 外部 6」 |
docs/前端契约.md(内部) | 顶层结构 / 四阶段字段 / 确定性要求 / Rust 侧要产出什么(含最小示例)/ 四条已知风险 |
tests/test_m58_frontend_contract.py | 13 项:能定位 / 只比指定阶段 / 缺阶段要报 / 缺语料要报 / 读不动要报 / 文档与版本号一致 |
验收演示(R1 的验收标准正是「改坏一处能定位」):
❌ 外部产物 vs 基线:72/73 个语料一致;共 1 处问题
· 产物不一致:tests/cases/01_算术.jsh 在 `tokens[2].col`
差异明细:
「tokens[2].col」 —— OP「=」(第 2 行第 5 列)
基线:5
外部:6
📌 一条设计上的坚持:外部只提供部分阶段也行(R2 只做分词),但 没提供的阶段必须如实报「未提供」,默认(不带 --stage)时那就是没过 —— 「没测」与「测过了」必须能分开。同一个道理今天在 CI 上又验证了一遍。
11.3 ✅ R2 已交付(2026-10-01):分词器换 Rust
做了什么:新建 core/(Rust crate,pyo3 编成 Python 扩展), tokenize() 在 JISHI_FRONTEND=rust 时走 Rust、默认仍是 Python(有开关可回退)。
| 项 | 结果 |
|---|---|
| 逐 token 一致 | 85/85 语料 ✅(另 2 万组随机源码对拍 0 分歧;40 个脚本端到端跑出来也一样) |
| 端到端吞吐(2.07 万行 / 734 KB) | 490.8 → 132.0 ms(3.72x) |
| 纯分词核心 | 490.8 → 69.6 ms(7.05x) |
| ⚠️ 端到端天花板 | 4.23x |
| 构建 | python core/build.py(产物 jishi/_core.pyd,按 Python ABI 编,不进仓库) |
| 守门测试 | tests/test_m59_tokenizer_rust.py(78 项) |
| 工具 | tools/bench_frontend.py(口径同机同时段交错 A/B + 把天花板推导印出来) |
🔴 最重要的一条:「≥5 倍」够不着,是接口决定的
端到端 = Rust 纯核心(69.6 ms) + 把 Token 交回 Python(62.4 ms)
其中 46.4 ms 是「纯造 11.7 万个 Token」的地板
天花板 = 490.8 ÷(69.6 + 46.4) = 4.23x ← 与实现好坏无关
建议(待作者确认):R2 的验收线改成「纯核心 ≥5x + 逐 token 一致」, 把「端到端 ≥5x」挪到 R5 —— 那时下游(parser/compiler)才不吃 Python 对象。 ✅ 2026-10-01 已拍板采纳:R2/R3 这一层认「纯核心 ≥5x + 产物逐项一致」, 「端到端 ≥5x」移到 R5 验收(那时下游不再要 Python 对象)。
🔴 顺手抓到的(都不是写错,是「本来就没测」)
- R0 的 73 个语料里一个 emoji 都没有(=没测位置),而列号口径是这次迁移的
头号风险。补 12 个语料:emoji 码点列号 / 多行注释三形态 / 全角归一化 44 项 / 三引号独占行 / 数字写法 / 插值分段表 / 5 个词法错误; 并新增语料类别 tests/frontend_cases/(超 i64 整数 ⇒ C VM 有意报 E2005, 放 tests/cases/ 会破坏「三执行器都能跑」那条约定)。 ⚠️ CRLF / 孤立 \r / BOM 进不了语料目录(read_text 会归一化)→ 字符串级测试。
tokenizer.TOKEN_TYPES漏了FSTRING(M7 没补)。R2 的kind_name_index
用 unreachable! 兜底 → 当场 panic(宁可不跑也别给错值)。
emit没清空输出目录 ⇒ 中途失败后compare拿旧产物比出一个绿
(「没跑成功」伪装成「通过」,第三次遇到同一个坑)。已改成「先删再写」。
11.4 ✅ R3 已交付(2026-10-01):语法 → AST 换 Rust
做了什么:core/src/parser.rs(2600 行)—— 把 1968 行的 jishi/parser.py 逐行移植,含全部解析期脱糖(条件表达式 / 超() / 匹配 / 枚举 / 推导式 / f-string),脱糖发生在与 Python 同一位置。
| 项 | 结果 |
|---|---|
| AST 逐项一致 | 88/88 语料 ✅(另 5 万组随机源码对拍 0 分歧;40 个脚本端到端一致) |
| 语法阶段诊断一致 | 88/88 ✅(码 / 行列 / 文案 / 提示 / ^ 下划线逐字) |
| 端到端吞吐(2.07 万行 / 735 KB) | 823.3 → 270.6 ms(3.04x) |
| 纯语法核心 | 823.3 → 145.9 ms(5.64x) |
| ⚠️ 端到端天花板 | 4.73x(同 R2 的道理:交回 Python 的对象本身有地板) |
| 守门测试 | tests/test_m60_parser_rust.py(111 项)+ 3 个新错误语料 |
🔴 最有价值的一条不是性能,是「接口原本就是错的」
原计划让 Rust 从 source_lines 反拼源码。做到一半发现:source_lines 是 有损载体 —— incremental.split_lines 会去掉末尾空行,反拼回去 报错行号整体少一行(实测 9 例错 4 例,全在「块首后跟空行」这种形状上)。 而 LSP 那条路拿到的正是 inc.lines。
改成:Rust parse 吃源码文本;新增 parse_source(源码) 作生产入口 (顺带省掉一次重复分词);旧入口留给 LSP 增量分词,并加精确守卫 (拿 token 流末尾 EOF 的行号当标尺)—— 丢了内容当场报错,不猜。
📌 手法可复用:「反拼」这件事本身就是信号 —— 需要把 A 反拼成 B 才能让下游 干活时,多半是接口给错了;把原始输入直接递下去,比在两侧维持两条「口径」 的隐性约定可靠得多。
🔴 顺手抓到的两处:① 插值字符串的 value 是分段表,直接插进中文报错会把 Python 的 repr 泄给用户 → 两侧统一说「插值字符串」;② 类外 超() 那条错漏了 with_source 与 ^ 下划线(它是解析之后补的检查,不走 _err)→ 报错的补全要认「一个出口」,别按「谁抛的」分岔。
下一步:R4 前端报错 —— ⚠️ 范围已缩小:R3 顺手把语法阶段的 diagnostics 一起比过了,所以 R4 只剩「把比较面补全」这半边(执行期/编译期留给 R5/R6)。
11.5 ✅ R4 已交付(2026-10-01):前端报错(契约升 v2)
做了什么
| 项 | 内容 |
|---|---|
| 契约 v2 | diagnostics 补 下划线 / 修正 / 渲染(用户真正看到的那段文本) |
| 错误码体检 | 新 tools/probe_errcodes.py:「表里有这个码」≠「这个码真的会出现」 |
| 统一还原出口 | errors.from_extension(词法/语法不再各抄一份字段清单) |
| 语料 88 → 92 | 补 E0204 的两个形状 + E0205 的两个形状 |
🔴 渲染 为什么进契约:它是「事实」的下游产物,看着冗余,其实是兜底 —— 哪天给错误加了一个事实字段却忘了列进契约,两个实现的渲染立刻不一样;而 「字段没进契约」本身是看不出来的(R3 就栽在「漏了一个字段、比对还是绿的」)。 挑那个能连坐整个类的不变量,是一次就够的检查。
🔴 错误码体检第一次跑就抓到两笔(以前从来没人查过):
- E0205「括号没有闭合」是死码 —— 定义在、说明在、
jishi 错误 E0205查得到,
但全仓库没有触发点(括号没闭合其实报 E0201)。一份永远兑现不了的承诺。 → ✅ 已拍板接上:期望右括号而已到文件末尾 ⇒ E0205,位置指向 那个没闭合的开括号(EOF 的位置是「末行第 1 列」,指着它没用)。 ⚠️ 只认「走到末尾都没等到」:中途撞上别的 token 仍报 E0201 并说出「看到了什么」—— 别为了把码归拢而把信息丢掉。
- E0204 可达却一个语料都没有 —— 八个形状能触发它。
🔴 词法那条还原路径漏读「修正」:词法/语法各抄一份字段清单,词法那份少了 fix —— 而当时恰好没有一个词法错误带 fix,所以怎么测都是绿的。 → 收成一个出口 + 字段表往返测试把它们钉死。 「两份清单并存 + 恰好没用到」= 测不出来,但问题在。
下一步:R5 编译器 → 字节码 —— 见 §11.6。
11.6 ✅ R5 已交付(2026-10-02):编译器 → 字节码换 Rust
做了什么:core/src/compiler.rs(1151 行 Python 编译器的逐行移植) + jishilib.rs(基石库层展开)+ serialize.rs(字节码 JSON + Python 的 repr(float))+ opcodes.rs + sha256.rs。
| 项 | 结果 |
|---|---|
| 字节码逐字节一致 | 92/92 语料 ✓(连 checksum 都相同 —— 它是 payload 的 SHA-256);3 万组随机源码对拍 0 分歧 |
| 另三阶段 | tokens / ast / diagnostics 也都 92/92 ✓ |
| 编译端到端(2.07 万行) | 2035.0 → 378.9 ms(5.37x) |
| (对照)也做一份对象产物 | 400.2 ms(5.08x)—— VM/CVM 那条路,仍要付地板 |
✅ 前端整条链(源码 → 词法 → 语法 → 字节码)现在都能在 Rust 里原生跑完。
🔴 兑现「端到端 ≥5x」靠的是换产物形态:从「Python 对象」换成 「字节码 JSON 文本」。地板(造对象)在文本这条路上不存在 —— 不是实现更快了,是接口换了。⚠️ VM/CVM 那条路仍要对象,别拿它判 R5。
🔴 移植时抓到四个既有 bug(与 R5 无关) —— R5 逐字复刻,R5.1 已全修 (编译器漏 Try 分支 / Python VM 撞掉帧栈基址 / 树遍历「捕获 为」开子作用域 / 调用链时机不一)。见 docs/前端契约.md §12.4、docs/设计决策.md D59。
下一步:R6 VM + 标准库(Rust 原生) —— 验收 = 426 项对拍全绿; 实测后定 C VM 去向。
11.7 ✅ R6 已交付(2026-10-04):VM + 标准库(Rust 原生)
一句话:树遍历参考执行器六步 + 四项主体性能全交付,C VM 去向按「先量再定」 实测后拍板保留。细节不在这里重复:数据与结论见 docs/设计决策.md D76 (长任务 C VM 净 489.0ms vs Rust VM 净 649.0ms,C 快 1.33x;维护面 2408 行 / 近 30 天 12/187 次提交),时间线见 PROGRESS.md。
📌 教训(R6 全期最值钱的一条):Val 的「32 字节」在文档里躺了很久、 被反复引用,却从没量过 —— 一量是 128 字节(撑大它的是内联的 Class)。 没有量过的数,不许当事实写。
11.8 🔎 R7.0 侦察与度量(2026-10-05):工具链迁移面台账(待拍板)
R7 的验收是「编辑器不再依赖 Python;增量指标不倒退」。开工前先把面摸清 ——「搬得动/搬不动」都要有依据,不能靠感觉。
一、好消息:边界已经是「进程 + 协议」,不是「import 我们的模块」
- 编辑器扩展一直是 spawn 子进程:
server.command默认jishi、
server.args 默认 ["lsp"](stdio JSON-RPC,自带最小传输层 lsp-transport.js);调试走 VS Code 的 DebugAdapterExecutable spawn jishi dap。 → R7 换的是协议那头是谁,不是发明新接口。
- 工具链模块没有一个依赖第三方 Python 库(只有
json/sys/pathlib/
argparse/threading/zipfile/hashlib 这类标准库)。 → 「不再依赖 Python」的实际含义 = 把 R2–R5 的 Rust 前端 + R6 的 Rust 宿主 接到同样的协议上,再补 Rust 侧还缺的几块。
二、迁移面台账(逐项)
| 工具 | 规模 | 入口 / 协议 | 依赖的 Python 面 | Rust 侧现状 | 判断 |
|---|---|---|---|---|---|
| 格式化 | formatter.py 422 | jishi 格式化 <文件>、LSP textDocument/formatting、MCP format_source | 只要 parser/AST + 自己的排版规则 | 有 parser ✅,无 formatter | ★ 低(纯函数,最容易先做) |
| 静态检查 | checker.py 432 + scope.py 167 | jishi 检查 <路径> --json、MCP check_source | 四条 lint 共用 scope.py(与 LSP 未定义名同一份规则) | 编译器里有作用域收集,四条 lint 都没有 | ★ 低 |
| 错误码查询 | errcodes.py 224 | jishi 错误 <码>、MCP lookup_error、LSP codeAction | 自带说明表 | 有「类型名 → 码」表(M56),没有码 → 说明 | ★ 低 |
| 语言元数据 | ai.py 393 + langdata.py 57 | --ai-card/--lang-spec、MCP describe_language/lookup_stdlib、LSP 补全与悬停 | 🔴 用 import ast 解析自己的 Python 源码收集元数据(Python 特有手法) | 有 --dump-stdlib/--dump-methods/--dump-builtins,但形状与用途不同 | ★★ 中(要拍板「唯一来源」,见 §五-1) |
| LSP | lsp.py 1666 | stdio JSON-RPC,18 个方法(补全/悬停/定义/符号/格式化/重命名/代码动作/签名帮助/工作区符号…) | tokenizer/parser/scope/langdata/formatter/errcodes/incremental;不执行代码 | tokenizer ✅ / parser ✅ / diagnostics ✅,其余缺 | ★★ 中(体量在协议细节,不在算法) |
| MCP | mcp_server.py 443 | stdio JSON-RPC,7 个工具(run_script / eval_expr / describe_language / check_source / lookup_error / lookup_stdlib / format_source) | 上面那些的薄壳;run_script 靠 subprocess 隔离 + 真超时 kill | Rust CLI 只能跑字节码 JSON,无 stdin / 超时 / JSON 报错 | ★★ 中(取决于 CLI 那件) |
| CLI | cli.py 1268 | jishi <文件>、-i REPL、--ast/--json-errors/--ai-card/--lang-spec/--sandbox/--stdin/--dump-bytecode/--load-bytecode/--执行器 + 10 个子命令 | 全部 | jishi-rs 只有 <字节码.json> + 4 个 --dump-* | ★★ 中高(面最宽,但每项都不难) |
| REPL | repl.py 253 + lineedit.py 507 | jishi -i | 需要能反复执行/求值的宿主 + 行编辑 | 无 | ★★ 中 |
| DAP | dap.py 676 + debugger.py 1135 | stdio JSON-RPC(initialize/launch/setBreakpoints/stackTrace/scopes/variables/continue/next/stepIn/stepOut/evaluate…) | 需要一个能单步、能看变量与帧的执行器 | 🔴 宿主没有单步/断点/语句钩子(只有 Frame.locals 结构与 --walk) | ★★★ 高(要新做「可调试执行器」) |
| 包管理 | packages.py 597 | 安装/卸载/列表/发布/索引/检查 | zipfile/hashlib/shutil + 索引格式 | 无 | ★★ 中高(与「自主分发」是同一件事的两半) |
三、搬不动的(显式登记,与既有「有意边界」同一条口径)
从 python 导入/ 本地包:单二进制里没有 Python —— 与跨语言宿主已有
的那条登记同源(tests/error_cases/13_从python导入.jsh 等)。
jishi 医生里的「Python 环境」检查项(venv / 编码 / C VM DLL 路径):
单二进制里没有意义,换成 Rust 版自己的检查项(二进制位置 / 标准库自检 / 包索引可达性)。这是改动,不是缺失 —— 要在 R7.4 明说。
- 元数据的「反射」做法:
ai.py用 Python 的ast读自己的源码抓
docstring 与结构;Rust 里没有等价物。必须换做法(见 §五-1 拍板项)。
四、建议的迁移顺序(依赖少的先来)与每项验收判据
| 步 | 内容 | 验收判据(可度量) |
|---|---|---|
| R7.1 | 格式化 + 静态检查 + 错误码查询(纯前端,不碰执行器) | ① 格式化:对全语料逐字节一致(与 Python 版同输入同输出)且幂等(格式化两次 = 一次);② 静态检查:检查 --json 逐字段一致(码/行列/消息/severity);③ 错误 <码> 文本逐字一致 + 码表体检(无死码 / 无漏码,照 R4 那套) |
| R7.2 | 语言元数据(唯一来源定型) | Rust 侧产出的 language card / lang spec 与 Python ai.build_lang_spec() JSON 逐字节一致(漂移检测,同 test_m51_consistency 那套:两边各自 --dump 自报家底来比) |
| R7.3 | LSP(把 R7.1/R7.2 组装起来) | 用编辑器自带的同一份自检(editors/vscode-jishi/verify-lsp.js)对 jishi lsp 与 Rust 版各跑一遍 → 报文逐字一致;且机器上不装 Python 也能用(这才是验收原文) |
| R7.4 | CLI + MCP | ① jishi-rs 补齐 <文件>/--stdin/--json-errors/--ast/--sandbox(超时真 kill);② MCP 7 个工具报文逐字一致;③ run_script 的隔离与超时语义一致(不是「差不多」,是「真 kill」) |
| R7.5 | DAP | VS Code 里能设断点 / 单步 / 看变量与调用栈;与 Python DAP 的响应序列一致(同输入同断点集) |
| R7.6 | 包管理 | 与「自主分发」合并做;安装/卸载/列表 的索引格式与错误文案与前代一致 |
⚠️ 「增量指标不倒退」要在 R7.3 验:基线是同机同时段测的 「编辑一次 → 诊断出来」的耗时(按文件行数分档)。incremental.py 有历史数据 (见 §六),但历史数据只能当量级参考,不能当判据 —— 按本项目纪律, R7.3 开工首日要在同一台机器上重测一份基线再比。
五、搬第一件之前要拍板的事
- 🔴 语言元数据的唯一来源放哪?现在它长在 Python 里(而且是用反射自己源码
的方式长出来的)。三条候选:(a) Rust 侧显式表(可读、可测,但要防与 Python 侧漂移);(b) 一份共享数据文件(.json/.toml),两侧都读它 ——漂移在结构上不可能;(c) 生成(从一个来源生成两侧)。 📌 这条决定 R7.2 的形态;本项目为「两个地方各自解释同一份元数据」付过代价 (AGENTS §2 有记录)。倾向 (b),但需要你点头。
- 🔴 Rust 二进制的入口名与分发形态:编辑器 spawn 的是
jishi
(现在是 Python 控制台脚本)。R7 之后是 ① 叫 jishi-rs、扩展里配置切换; 还是 ② 直接叫 jishi(Rust 版顶上),Python 版退成一个别名? 这直接决定「编辑器不再依赖 Python」怎么验(① 是「可切换」,② 才是 「默认不依赖」)。
- 🔴 DAP 放不放 R7:它要新做「可调试执行器」(单步 / 断点 / 语句钩子 +
变量快照)。要选落在哪个执行器上:树遍历参考执行器(已经是「规范」, 加单步可能最省)还是 Rust VM(快,但要动指令循环)。 若 R7 只想收「编辑器不再依赖 Python」,DAP 可以挪到 R8 之后。
- 包管理放不放 R7:它和「自主分发」是同一件事的两半。若 R7 目标是
「编辑器不再依赖 Python」,包管理可以不动。
- 旧 Python 工具链的处置:R7 完成后留不留?(D76 的教训:C VM 那次是
先量维护面再拍板)——建议同样处理:先量「留着的维护代价」与 「退掉的风险」,再定,不预设。
六、R7.0 已经拿到的基线(增量)
jishi/incremental.py 里记着一份历史实测(编辑一次的代价,Python 侧):
| 文件行数 | tokenize | parse | 合计 |
|---|---|---|---|
| 253 | 6.1 ms | 3.8 ms | 9.9 ms |
| 1253 | 25.6 ms | 18.6 ms | 44 ms |
| 5003 | 103.6 ms | 77.9 ms | 182 ms |
| 20003 | 430 ms | 328 ms | 759 ms |
📌 现状:只有分词是增量的,parse 仍是全量(1000 行以上有可感卡顿)。 ⚠️ 这份表是历史数据(当时机器 / 当时版本)—— R7.3 开工首日同机重测 一份,才是「不倒退」的判据。
11.9 🚧 R7 已拍板(2026-10-05):四条决定 + R7.1 进度
§11.8 那份台账把「搬第一件之前要拍板的事」列了 5 条,用户当场拍了 4 条:
| # | 问题 | 拍板 |
|---|---|---|
| 1 | 语言元数据的唯一来源 | Rust 侧显式表 + 「两侧各自 --dump 自报家底来比」的漂移检测(同 test_m51_consistency 那套)。 |
| 2 | Rust 二进制的入口名与分发形态 | Rust 顶上叫 jishi(Python 版过渡期退成 jishi-py)。这样「不装 Python 也能用」是字面可验的 —— 卸载 Python 后编辑器照常工作。 |
| 3 | DAP 放不放 R7 | 放,且落在树遍历参考执行器上(它已经是「规范」、按节点走,加单步比动 VM 指令循环便宜)。 |
| 4 | 下一步 | 进 R7.1(格式化 + 静态检查 + 错误码查询)。包管理与旧 Python 工具链的处置暂缓(后者照 D76:先量维护面再定)。 |
🔴 开工时核实出的前置条件(§11.8 台账没写清,值得单记)
前端本体本来住在 core/src/,而 core 是编成 Python 扩展(pyo3)的 crate —— 于是「能解析源码的东西只有 Python 拿得到」,独立宿主 jishi-rs 只能吃 已经编译好的字节码 / AST JSON(R6 的 --walk 也是吃 AST JSON)。 而 LSP / 格式化 / 静态检查都必须自己拿到 AST ⇒ 前端必须先解耦。
📌 核实的结果是好消息:这个搬家是干净的 —— 前端那五个文件代码级零 pyo3 用法 (翻遍 tokenizer / parser / serialize / jishilib / opcodes,命中的 "pyo3/Python" 全是注释里的「与 Python 侧对应关系」说明),而且跨模块引用本来就写 crate::tokenizer::… —— 整体搬到新 crate 根目录后一行引用都不用改。
📌 教训:「谁拿得到这个能力」要在侦察时就查清,不能只看「这个能力在不在 Rust 里」。台账当时写「tokenizer ✅ / parser ✅」是对的,但没说「它们住在 pyo3 扩展里、二进制用不上」—— 差一点就按「已经齐了」排期。
进度
| 步 | 内容 | 状态 |
|---|---|---|
| R7.1-a | 抽 jishi-frontend crate(纯 Rust、零依赖),core(pyo3 壳)与 rust(二进制)共用 | ✅ 已交付(aee3e52):夹具 103/103(四阶段)+ Rust 前端 emit 后四阶段各 103/103 一致 |
| R7.1-b | 错误码查询搬进 Rust(rust/src/errcodes.rs + jishi-rs 错误 [码] + --dump-errcodes) | ✅ 已交付:32 个码逐个「与 jishi 错误 逐字对拍」+ 总览 + 「没有这个码」(stderr + 退码 2)+ 别名写法(301/E301/0301/小写/带空格);两张表(码+标题 ← errors.py,说明 ← errcodes.py)由 tests/test_m85_errcodes_rust.py 钉住不许漂 |
| R7.1-c | 静态检查搬进 Rust(checker.py + scope.py 移植) | ✅ 已交付:jishi-rs 源码静态检查 [--json](⚠️ 当年叫 检查;R7.6 起让回给包管理,见下行)与 jishi 源码静态检查 逐字一致(四条规则 + 语法错 + 退出码);真语料四个目录 × 两种形态对拍;符号表漂移检测(当场抓到 测试 模块只有 Python 有) |
| R7.1-c+ | 测试 模块补进 Rust 宿主(用户指出:Rust 要替代 Python,这个也得有) | ✅ 已交付:stdlib/testmod.rs(11 个函数)+ 给 BuiltinHost 加了第 6 个能力「往 stderr 写」(测试.汇总 的失败明细走 stderr、汇总行走 stdout);m51 的豁免改成按宿主(测试 从「两宿主都没有」变成「只有 Node 没有」) |
| R7.1-d | 格式化搬进 Rust(formatter.py 移植) | ✅ 已交付:jishi-rs 格式化 [文件] [--stdin] [--check] [--write] [--indent N](含目录模式)与 jishi 格式化 全语料逐字节一致(103 个 .jsh × 单文件/目录/各模式)+ 幂等。🔑 前置是把 frontend/src/tokenizer.rs 里私有的 tokenize_line 暴露成公开 API(喂原始行、列号落原文坐标 → 格式化器能按列切片取回原文)。顺带补 JishiError.underline(多字符下划线 ~^,以前宿主渲染不出来) |
| R7.1-收尾 | R7.1 四件(错误码 / 静态检查 / 测试 模块 / 格式化)都已完成 | ✅ |
| R7.2 | 语言元数据搬进 Rust(jishi/ai.py) | ✅ 已交付:jishi-rs --lang-spec / --ai-card 与 jishi --lang-spec / --ai-card 逐字节一致(56081 字节 JSON / 整份语言卡)。拍板①落地:rust/src/langdata.rs 是 tools/gen_langdata_rs.py 一次性搬运的显式表(关键字 36 / 运算符 / 内建 30 / 标准库 25 模块 229 函数带说明 / 错误码 35 / 静态检查码 5 / 等价 Python 写法),rust/src/langspec.rs 自带保序 JSON + 两个序列化器(缩进版对齐 json.dumps(indent=2),排序紧凑版算 content_hash)。判据 test_m90(逐字节 ×2 + 不经 CLI 的结构化对比 + 独立判据)。✅ |
| R7.3 | LSP 搬进 Rust(jishi/lsp.py,1666 行 / 18 个方法) | ✅ 已交付:jishi-rs lsp 与 jishi lsp 同一串 33 条报文(覆盖 18 方法 + 增量编辑 + 版本乱序)逐字节一致(40418 字节);编辑器自带的 verify-lsp.js 两边各跑一遍都是 25/25 全过。⚠️ 顺带抽出 scope.rs(作用域分析,checker 与 LSP 共用一份)与 jsonw.rs(保序 JSON 写出器,--lang-spec 与 LSP 共用)。🔴 照抄了一条 Python 的怪行为:词法错的 didOpen 会让整个 on_did_open 抛出、文档根本不注册(IncrementalDocument 在构造里就分词)。📌 性能(同机同时段,R7.3 首日重测):每次编辑 Python 13.0/70.9/289.2/1224.3ms → Rust 2.4/11.1/49.8/207.1ms(253/1253/5003/20003 行,5.3~6.4x)—— Rust 用全量重分词+重解析就赢了 Python 的增量,所以没有搬 incremental.py。判据 test_m91(逐字节 + verify-lsp.js 双跑 + 独立判据 + 计时 性能护栏)。⏳ 下一步 R7.4 CLI+MCP |
| R7.4 | CLI + MCP 搬进 Rust(cli.py 1268 行 / mcp_server.py 443 行 · 7 个工具) | ✅ 已交付: |
① CLI 主入口:jishi-rs <文件.jsh> / --stdin / --ast / --json-errors / --sandbox / --json-result / --timeout / --load-bytecode / --dump-bytecode。tools/check_cli.py(新闸门)跑 93 个语料 × 3 种模式 × 两个引擎,不一致 0;--json-errors 连 fix(print→打印)都对上。 ② MCP:jishi-rs mcp 与 python -m jishi.mcp_server 同一串 40 条报文逐字节一致(7 个工具全过;只有 duration_ms 会因为它是墙上时间而归一)。 ③ run_script 到点真 kill:子进程隔离 + 到点 kill();宿主自己那条沙箱超时(线程 + 有超时的收结果)1 秒内带着协议 JSON 退出。 🔴 位置参数的语义改了:<文件> 从「字节码 JSON」变成源码(与 jishi <文件> 对齐),字节码走 --load-bytecode —— tests/ + tools/ 里 25 个文件、35 处调用点跟着补了开关。 🔴 顺带修掉三个真 bug(旧判据没覆盖到,R7.4 的对拍照出来的):① 宿主把 值错误("x") 当立刻抛而不是构造异常(三个 Python 执行器都不是这样,令 e = 值错误("x") 在宿主上直接崩);② TraceFrame 少带 col,调用链里那一项恒为 null;③ 树遍历的「找不到名字」hint 给 "" 而另两个执行器给 null。 🔴 本机环境坑:Windows 上给子进程 stdin 方向 CreatePipe 稳定失败(ERROR_PIPE_BUSY / os error 231,兄弟症状是 test_m31 那条已登记的 spawnSync node.exe EBUSY)。jishi-rs --mcp-spawn-probe 是复现入口;解法是用临时文件当 stdin(--stdin 语义不变,只是不走管道)。 ⚠️ Python 侧两处如实登记(不是宿主的问题):① CLI 的沙箱超时会丢输出(跑死循环时 stdout 全空、0.5 秒的超时要跑十几秒 —— 泄漏的守护线程抢 GIL);② MCP 的 stdout 以前没像 CLI 那样 reconfigure(newline="\n"),Windows 上吐 CRLF(已修,两侧统一 LF)。 判据 test_m92(CLI:对拍 + 独立 + 计时 超时护栏)/ test_m93(MCP:报文逐字节 + 独立 + 计时 真 kill 护栏)。⏳ 下一步 R7.5 DAP | | R7.5 | DAP 搬进 Rust(可调试执行器 + jishi-rs dap) | ✅ 已交付: ① 给树遍历参考执行器 (rust/src/walk.rs) 加调试钩子(DebugHooks:before_stmt / push_frame / pop_frame)—— 只读、不改语义;为了让「带调试器跑」与「不带」逐字节相同,钩子只读语句与作用域。 ② 会话(rust/src/debugger.rs,对应 debugger.py 的 _Session):断点表 / 单步模式判定 / 变量与调用栈 / 就地求值 / 出错现场快照。 ③ DAP 适配器(rust/src/dap.rs,20 个请求 + 事件 + 60 余条报文)。 🔑 Rust 版与 Python 版的线程模型不一样、报文一样:Python 用「主线程跑协议 + 脚本线程跑程序」,Rust 的 Walker 不是 Send,所以改成「读线程只做分帧 + 主线程既跑协议又跑程序」—— 暂停时由 PauseDriver 串行地泵消息,顺手把 stackTrace / variables / evaluate 答掉。对客户端而言报文序列逐条对应。 ⚠️ 一处如实说明的时序差异:程序自由跑时收到的读请求,Python 当场就答、Rust 在下一个语句边界才答(对 pause / disconnect 无影响 —— 本来就是「下一条语句生效」)。判据用同步客户端(等应答再发下一条)把两边卡在同一点。 🔴 判据:三种场景(普通单步 / 就地求值 / 出错现场)与 jishi dap 报文逐字节一致(13327 / 13930 / 10655 字节)+ 不依赖 Python 的独立判据(能力声明必须在 body.capabilities 里、停不住的行要标 verified:false、只支持树遍历要明确拒绝、写死的变量与栈期望、清空 PATH 也能跑)。 ⚠️ 没搬的部分(如实登记):字节码执行器的调试(--执行器 vm/cvm 明确拒绝)。 ✅ R7.5 收尾已交付:jishi-rs 调试(交互式命令行)—— 补上命令表 (断点/取消断点/清空断点/变量/查看/栈/源码/帮助 + gdb 风格别名)与「读一行」的驱动 LineDriver(PauseDriver 当初就是为它留的口子)。判据 tests/test_m95_debug_cli_rust.py (20 场景逐字节对拍 + stdin 路径 + 独立判据)。🔴 顺带修掉一个宿主真 bug: 内建渲染成 <内建函数 打印>、异常类型渲染成裸的 值错误(Python/Node 是 <内建 打印> / <异常类型 值错误>)—— 旧测试只比 Python VM 与 Node、没算上 Rust, 是 变量 全部 的对拍照出来的;已补进 test_m32_boundaries.py。 | | R7.6 | 包管理搬进 Rust(packages.py 597 行 + cli.py 的 _run_package_cmd) | ✅ 已交付: rust/src/packages.rs + jishi-rs 安装/卸载/列表/发布/索引/检查。判据 tests/test_m96_packages_rust.py (六子命令逐字节对拍 + 独立判据 + file:// 本地镜像走完整条安装链)。 🔴 检查 换回了包管理的语义:Python 的 检查 是包依赖冲突、源码检查叫 源码静态检查(别名 静态检查);R7.1-c 当初把源码 lint 挂在 检查 上是错的, R7.6 让回包管理(否则 Rust 顶上 jishi 时脚本会静默走错),调用点改了 test_m86/test_m87。 🔴 一处刻意保留的差异:打包 zip 的字节不同(Python ZIP_DEFLATED、宿主 stored 写出器)→ 发布 / 索引 --checksum 的体积与 sha256 对不上(测试归一化后比); 内容一样、stored 合规(拿 Python zipfile 当裁判验过),不影响安装。 ⚠️ 另两处已登记文案差异:缺参数走 argparse(宿主给中文用法,只对齐退码 2); 网络错误的具体文案(外层结构逐字一致)。📌 复用了 zip.rs/crypto.rs/http.rs (http::client::request 返回类型从 String 改成原始字节,新增 fetch_bytes)。 ✅ R7 六件(R7.1–R7.6)全部完成。 |
📌 R7.1-b 顺带打通了一条流水线(后面两件照抄):**Rust 侧显式表 + 「自报家底」 --dump-* + Python 侧逐字段比 —— 这是拍板第 1 条说的那条漂移检测,先从「纯数据」 的错误码表上跑通。首次的表由 tools/gen_errcodes_rs.py 一次性搬运**(跑完看 diff), 之后两侧由测试钉住。
11.10 🚧 R8 侦察与度量(2026-10-07)—— 单二进制 + wasm + 退役 Python/JS
R7 六件收口(jishi-rs 已覆盖 编辑器 / agent / 脚本 / 包管理 四条路)之后进 R8。 R8 的定义(docs/架构迁移路径.md §三):单二进制 + wasm,退役 Python 与 JS → 架构变革 收尾;验收 = 三平台包可用 + playground 跑通 + 安装/升级链路不受影响, 体积 ≤ 5 MB、启动 ≤ 100 ms(达不到就如实公布)。
一、开工实测(本机,2026-10-07)
| 项 | 实测 | 达标线 | 判断 |
|---|---|---|---|
| 宿主二进制体积 | 4,827,095 B = 4.60 MB | ≤ 5 MB | ✅ 擦线(余量 0.4 MB) |
启动(--version,7 次中位) | 92.3 ms | ≤ 100 ms | ✅ 擦线(余量 8 ms,且含父进程 fork/exec 开销) |
跑一句(--stdin,7 次中位) | 91.4 ms | — | 与空跑同量级 ⇒ 启动成本就是全部 |
| 现有 Python 发行包 | jishi-0.2.1-windows-x64.zip = 20.0 MB | — | Rust 单二进制 小 4.3 倍 |
| 第三方依赖 | 0(只有 jishi-frontend 路径依赖) | — | R0 起的核心资产,R8 不许破 |
📌 两条验收线都擦着过,所以 R8 的「体积/启动」不是「顺手就过」,而是 要看着别退:后续任何一件(wasm 胶水、沙箱步数预算、改名)都可能把它推过线。
二、🔴 本次已完成的高风险单点验证:wasm 真能跑
docs/架构迁移路径.md 的风险表写着「wasm 上跑不通(playground)→ R8 前先做单点验证 (一个 hello 走通 wasm),不把它拖到最后」。本次当场做了(一次性探针, 源码留在 build/_wasm_probe/,不进仓库):
rustup target add wasm32-unknown-unknown(本机原先只有x86_64-pc-windows-gnu);- 零改动下
jishi-frontend与jishi-ffi都能编到 wasm(0 error)——
比预想的好得多(原以为要按 cfg 拆 crate);
- 探针(cdylib + 3 个
extern "C"导出 + 手写 30 行 JS shim)真跑起来了:
| 输入 | wasm 里的输出 |
|---|---|
令 甲=1 / 令 乙=2 / 打印(甲+乙) | 3 |
斐(15)(递归 + 函数调用) | 610 |
导入 数学 / 打印(数学.开方(16.0)) | 4.0(原生标准库可用) |
打印(甲 +)(语法错) | 正确的中文报错 |
wasm 模块 1,518,284 B = 1.45 MB(release,未做 opt-level="z"/LTO 调优)。
📌 结论:R8 最大的一处未知已排除 —— 整条链(前端 + 编译器 + 字节码 VM + 原生 标准库)在 wasm 上零依赖跑得通,不需要 wasm-bindgen 那类框架。 ⚠️ 「编得过」≠「跑得对」:探针同时证明了必须避开的两处 —— std::thread(sandbox.rs 的超时靠它,wasm 上会 panic)与 std::fs/net/process。
三、现状台账:还差什么
| 件 | 现状 | 差什么 |
|---|---|---|
| 单二进制 | ✅ 已经就是(零第三方依赖、单 exe) | 只是名字还叫 jishi-rs;jishi 这个名字现在被 Python 控制台脚本占着 |
| 三平台发行包 | ⚠️ release.yml 走的是 PyInstaller(tools/build_release.py) | 改成 cargo build --release + 打三平台包;体积/启动在 CI 上复测 |
| wasm(playground) | ✅ 单点验证通过(见上) | ① 正式 ABI + JS shim;② 非线程版执行器(超时那套在 wasm 上没有);③ site/playground.html 从 Pyodide 换成 wasm |
| 退役 Python | ⚠️ Python 是判据的基准(109 个测试文件里 42 个会 spawn sys.executable) | 按 R8 计划搬进 oracle/(只给测试用),改这 42 个测试 |
| 退役 Node 宿主 | ⚠️ node/ 只被测试用(站点与编辑器都不引用它) | 一批 test_m66–test_m74 一类的宿主对拍要重定基准 |
| 收尾 | — | README 技术栈重写、改名、文档、进 0.3.x |
四、建议的推进顺序(依赖少的先来)
| 步 | 内容 | 验收 |
|---|---|---|
| R8.1 | wasm 正式化:ABI + JS shim + 非线程执行器(步数预算) | 一组语料在 wasm 上的输出与宿主逐字节一致 |
| R8.2 | playground 切 wasm(退役 Pyodide) | 站点 playground 能跑示例;死循环不冻页面 |
| R8.3 | 三平台发行包改走 cargo | 三平台包可用 + CI 上实测体积/启动 |
| R8.4 | 改名 jishi(Python 版退 jishi-py) | 编辑器/脚本/agent 四条路都不再依赖 Python |
| R8.5 | 退役 Python + Node → oracle/ | 42 个测试改完,全量回归绿 |
| R8.6 | 收尾:README 技术栈重写 + 文档 + 进 0.3.x | —— |
五、待拍板(4 条)→ ✅ 2026-10-07 已拍板
| # | 问题 | 拍板 |
|---|---|---|
| 1 | wasm 的胶水方案 | 裸 extern "C" ABI + 手写 JS shim(保住「零第三方依赖」这条核心资产;探针已证可行,1.45 MB)。不上 wasm-bindgen。 |
| 2 | playground 怎么防死循环 | 给执行器加「步数预算」(超了抛「执行太久」)—— 与 Python 侧「到点真 kill」同一精神。⚠️ 要盯着别把性能/体积推过线。 |
| 3 | 退役的顺序 | 单二进制 → wasm → 退役(照 docs/架构迁移路径.md §三 原文)。 |
| 4 | 改名时机 | 与退役同批做(R8.5):那时 Python 版退成 jishi-py、Rust 顶上 jishi,改动集中在一处,避免两版同名的过渡期混乱。 |
⇒ 于是 R8 的步序定为:R8.1 wasm 正式化 → R8.2 playground 切 wasm → R8.3 三平台发行包 → R8.4 退役 Python/Node + 改名 jishi → R8.5 收尾(进 0.3.x)。
11.11 ✅ R8.1 已交付(2026-10-07):wasm 引擎 + 裸 ABI + 手写 shim
按 §11.10 的拍板(裸 ABI、步数预算、单二进制→wasm→退役、改名与退役同批)开工。
交付物
| 件 | 位置 | 说明 |
|---|---|---|
| wasm 入口 | wasm/src/lib.rs(crate jishi-wasm) | 裸 C ABI:jishi_alloc/free/run/eval/out_len/out_copy/set_budget/get_budget/version;crate-type=["cdylib"] |
| JS shim | site/jishi-wasm.js | 手写 ~150 行:内存往返、ABI 自检、trap 兜底、performance.now() 计时 |
| 构建 | tools/build_wasm.py | 构建 + 拷到 site/jishi_wasm.wasm + 报体积;--check 比新旧 |
| 判据 | tests/test_m97_wasm_playground.py | 6 条:12 个语料逐字节对拍 + 5 条独立判据 |
实测
- wasm 模块 1,544,334 B = 1.47 MB(未做
opt-level="z"/LTO 瘦身)。 - 12 个语料(含 error_cases)在 wasm 里的结果 JSON 与
jishi-rs --sandbox --json-result 逐字节一致(只归一 duration_ms)。
🔴 三条要记住的
std::time::Instant::now()在 wasm 上 panic(`time not implemented on this
platform)。panic="abort" 下它表现成 RuntimeError: unreachable —— 看着完全 不像时钟问题,我在这上面绕了一圈(先怀疑 profile、又怀疑 LTO)。 ⇒ sandbox.rs 加了 Clock(wasm 上恒 0),真实耗时由 shim 用 performance.now()` 量。
- 步数预算关进 cargo 特征
budget,只有wasm/开。这是量出来的:
- 判断放指令循环顶(每条指令过一遍)→ 1.068x;
- 只在回跳处判 → 1.058x;
- 同轮「同一份二进制的两份拷贝」→ 1.005x(所以 5~6% 不是噪声);
- 特征关掉 → 1.000x(与加这个功能之前逐位同效)。
⇒ 原生宿主一个字节的钱都不用付;wasm 付 6% 换「页面不冻住」。
[profile.release](opt-level="z"+ LTO + strip)暂时没写:当时配上它
wasm 只剩 16KB 且一跑就 trap —— 后来查明根因是第 1 条(时钟),不是 profile。 但它现在没被验证过,所以先不写(1.47MB 能上网页);要瘦身得单独量过再开。
⚠️ 一处如实说明:wasm 上判「跑飞了」的单位是循环圈数 ⇒ 兜不住没有回跳的失控(无限递归)—— 那种撞 wasm 栈上限,由 shim 接住并翻成 「程序跑飞了」。引擎给的错只有「执行步数超过上限」那一条。
⏳ 下一步 R8.2:site/playground.html 从 Pyodide 换成就是这份 wasm (现在跑的是 Python 实现,页面 432KB、还要从 CDN 拉 Pyodide)。
11.12 ✅ R8.2 已交付(2026-10-08):在线试玩页换成 wasm
按 §11.10 的顺序(单二进制 → wasm → 退役)做的第二件:把 playground 页面本身 从 Pyodide 换掉。
交付物
| 件 | 位置 | 说明 |
|---|---|---|
| 页面生成器 | tools/build_playground.py(重写) | 不再内嵌 Python 源码,只出 HTML |
| 生成的页面 | site/playground.html | 432 KB → 7 KB |
| 判据① | tests/test_m10_playground.py(重写) | 管生成出来的 HTML |
| 判据② | tests/test_m97_wasm_playground.py(加一条) | 端到端:DOM 桩里真跑页面 |
换掉了什么
| 以前(Pyodide) | 现在(wasm) | |
|---|---|---|
| 引擎 | 浏览器里的 Python 实现(树遍历解释器) | Rust 前端 + 字节码 VM + 原生标准库 |
| 页面体积 | 432 KB(内嵌 8 个 .py 模块的源码) | 7 KB |
| 网络 | 从 CDN 拉 Pyodide(约 10 MB) | 取本站 1.47 MB 的 .wasm(浏览器缓存) |
与 jishi / 编辑器的关系 | 另一条实现路径 | 同一份引擎 |
判据怎么写的(值得复用的一条)
test_m10 只管「HTML 里有什么」(无 Pyodide 残留 / 引用 wasm 三件 / 体积量级); 真正兜底的是 test_m97 新加的那条端到端:把生成出来的那个页面放进 Node + DOM 桩里跑起来(真 shim + 真 wasm),点「运行」,比输出文本 (示例程序写死期望、语法错比码与行列、运行期错比码与标题)。
📌 只断言「HTML 里含 JishiWasm.load 这个串」是糊不住错的 —— 渲染逻辑、 错误分支、事件绑定、textContent 写没写对,全都不在字符串里。这一条才是 「页面能用」的判据。
🔴 顺带修的:协议形状不统一
胶水与 wasm 自己兜的两种意外(wasm trap / 返回不是合法 JSON)以前给的是 errors 数组(元素带 type),而引擎给的失败是 error 对象 ({code,title,message,line,col,hint})—— 页面得判两支。
⇒ 统一成引擎那一份。改了三处:wasm/jishi-wasm.js 的两条兜底、wasm/src/lib.rs jishi_eval 的「值没法表示成 JSON」那条。「页面只该认一个协议」。
📌 实机验证(不是只在 Node 里跑)
用无头 Edge 起本地站点(python -m http.server → 127.0.0.1:8791):
playground.html/jishi-wasm.js/jishi_wasm.wasm(1,544,414 B)都是 200;--dump-dom里状态栏已从「正在加载引擎…」变成 「就绪」、运行按钮已启用
⇒ fetch 得到 + 实例化 + ABI 自检这条路通;
- 再截一张「自动点过运行」的图:输出区显示示例的正确结果。
⚠️ file:// 直接双击打不开(浏览器不许从本地磁盘读 wasm)—— 页面里已经写明 「改用本地服务器(例如 python -m http.server)」。这与以前不同:旧页面虽然是 自包含的,但它照样要联网拉 Pyodide。
⏳ 下一步 R8.3:三平台发行包从 PyInstaller 改走 cargo build --release。
补记:R8.3–R8.5(2026-10-08 ~ 10-09)
本节是摘要(R8.3 起的原文仍在内部路线图 §11.5–§11.10)。这一段把 0.2.x 「架构变革」收尾:单二进制发行 → 退役 Python/Node → 升 0.2.2。
- R8.3 发行包改走 cargo(单二进制):三平台包从 PyInstaller 换成
cargo build --release 的产物 —— 包内文件数 249 → 13、Windows zip 约 20MB → 2.91MB、启动 83ms。顺带修掉一个真 bug:老包的 bin/ 是 嵌套的(bin/jishi/jishi.exe),而 install.sh / install.ps1 的自检写的是 平铺 —— Linux/macOS 用 tar.gz 装完自检必失败,且 jishi 不在 PATH 上 (Windows 走 Inno Setup 才没暴露)。判据 test_m98。
- R8.4a:
医生/新项目/教程搬进 Rust(rust/src/tools.rs)——
发行包换单二进制后这三个不能再缺。新项目/教程 与 Python 逐字节对拍; ⚠️ 医生 有意重写(Python 版报「Python 版本 / C VM / MCP」,单二进制里 那三样根本不存在)。判据 test_m99。
- R8.4b-1:交互式 REPL(
-i)搬进 Rust(rust/src/repl.rs)——
10 个会话 stdout/stderr/退码逐字节一致;⚠️ 不改行编辑(那是独立一件事)。 判据 test_m100。
- R8.4b-2(改名 + 退役):
jishi让给单二进制(Python 退jishi-py,随后
随退役取消安装入口);jishi/ → oracle/jishi/、node/ → oracle/node/, 只作判据基准。改动面约 640 处、233 个文件。判据 test_m101。 🔴 收敛 142→0 的四类根因见路线图 §11.9(盲替换打坏 .jishi/-jishi; core/ 里硬编码模块名;editable 要重装;语料 glob + 基线重冻)。
- R8.5 收尾:版本升 0.2.2;
[profile.release]量完再开(LTO + strip,
exe 4.63→2.91MB、算力 0.97x;opt-level="z" +24% 慢 ⇒ 不用); check_engines 改每语料私有 cwd(判据 test_m102)。