版本说明:v1(
Vitro架构审阅报告20260921.md)是本次审阅的时序累积版,保留了每一轮对话的原文。本 v2 是分门别类的整合版。 修正批 b(2026-09-21 第二会话):对本文断言做了第二轮代码级复核——3 项修正(对外面口径 / JIT 形态 / 协议冻结现状)、5 项补遗、1 项归属修正,并落插件架构终局裁决。散点处标注〔修正 b〕/〔补遗 b〕,集中记录见第 8 部分。 v1 保留不删——按本项目"撤回记录本身即审计资产"的纪律,时序痕迹不应被清理。 口径声明:标注 〔实测〕 者为 2026-09-21 本机现场验证;〔推断〕 为基于结构证据的判断(附可复现检查方式);〔未验证〕 统一收在 §6。 覆盖度声明:凡在917251e档案中已有记载者,一律标注"档案已有",并下调其价值(从"新发现"降为"确认结论仍成立")。详见 §1.2。
摘要
本次审阅跨 12 轮对话,产出的技术结论与认知修正数量相当。后者是本次的真正特征:12 处判断被更正或撤回,其中 4 处源于我未核实前提、3 处源于判据选错、2 处源于因果链错误、3 处源于把旧结论沿用到已变更的问题形式上。 〔修正 b〕第二会话复核再增 3 处(#13–15,见第 5 部分)——共性归因新出一类:对自家已建资产的盘点不足(把"已冻结"当"待办"、把"形态受限"当"不可能")。
技术侧的最终收敛(§3):
一门语言做核心(机制 + 数据都在内,可冻结)+ 一个协议做契约(先冻结,LSP 式能力协商)+ 主进程掌控实例边界(引擎与插件分处不同执行单元,各自原地待命)+ 任意语言做插件(两档隔离)
尚未闭合的唯一开放项:S6 门。它同时决定机制层的语言与性能基线(判据为门 1 条件 A + 门 3 集成版)。
最紧要的三条待办(按"不依赖任何未决策"排序):
- 收窄对外面——实测 11 个包暴露 150 个公共符号,而总计划 §4 的硬约束是"只暴露三面"。该约束从未落地、无 CI 校验。必须先于协议冻结,否则冻结的是 150 个符号。
SLOT_STRATEGY_VERSION分档——A 级 code 段锚锁死 codegen,是temp_slot病灶(3 起同类 bug)至今未根除的真实原因。- C++ 语料处置——语料留存 ≠ 防线留存;F-2 已将 C++ 防线排退役,99 + 83 条语料将成无人校验资产。
第 0 部分 阅读指引
0.1 三级标注
| 级别 | 含义 | 要求 |
|---|---|---|
| 〔实测〕 | 本机一手验证 | 附可复现命令 |
| 〔推断〕 | 基于结构证据 | 附检查方式 |
| 〔未验证〕 | 未测 | 收于 §6,不混入结论 |
0.2 观点裁决四级
| 标记 | 含义 |
|---|---|
| ✅ 采纳 | 成立,已进入最终方案 |
| △ 部分采纳 | 结论可用但理由或范围需修正 |
| ✗ 不采纳 | 成立但不适用本项目的约束 |
| ⛔ 撤回 | 原判断错误,予以撤销并记录 |
0.3 台账编号
A 语言选型 · B 出口与分发 · C 分层与边界 · D 进程与插件 · E 验证与防线 · F 流程与方法论。
正文引用形如 见 A-04。
第 1 部分 实测基线与覆盖度核查
1.1 实测基线〔实测〕2026-09-21
| 检查项 | 命令 | 结果 |
|---|---|---|
| clippy(严格档) | cargo clippy --workspace --lib --all-features |
0 warning / 0 error |
| MoonBit 编译器 | moonc -v |
v0.10.13+cbb11c36f (2026-09-15) |
| MoonBit 工具链 | moon version |
moon 0.1.20260915 (2e1a46d 2026-09-15) |
| Rust 侧规模 | find native -name "*.rs" -not -path "*/target/*" |
258 文件 / 77,771 行 |
| MoonBit 侧规模 | find moonbit -name "*.mbt" -not -path "*/_build/*" |
235 文件 / 60,023 行 |
| 根 package 占比 | 同上(native/src/) |
57 文件 / 14,998 行(占 Rust 侧 19%) |
| 测试规模 | ls native/tests/*.rs |
51 个 .rs + 19 份 *_FAILURES.md;最大单文件 5,152 行 |
| 对外接口面 | 逐包统计 pkg.generated.mbti |
150 个公共符号(^pub 口径)〔修正 b:真实口径 200——漏计 pub(all) 35 + 抽象 type 4,见 §8.2〕 |
| 本机 OCaml 工具链 | command -v ocaml opam dune wasm_of_ocaml binaryen |
全部 NOT FOUND |
全量 clippy(
--all-targets)在本机沙箱下因写入native/target/被拒未跑完;--lib档完整通过。
1.2 档案覆盖度核查——本报告哪些是重发现
以 917251e 的 15 份档案(≈8,022 行)为语料做关键词命中统计〔实测〕:
| 本报告条目 | 档案命中 | 判定 |
|---|---|---|
C-02 死文件 src/compiler/ast.rs |
compiler/ast.rs 6 + 死文件 5 |
档案已有 → 价值下调为"确认登记项至今未执行" |
C-03 compute_type_size 单源 |
24 次 | 档案已有 → 价值下调为"确认结论仍成立" |
| C-07 槽位锁死重构 | slot 25 |
病灶已有;"版本常量分档"是新解(SLOT_STRATEGY 0) |
C-01 src/ 上帝包 |
src/ 250 · 包切分 19 |
素材已有;"按 L0–L9 重切 crate"是新结论(重构为 crate 0) |
| B-05 对外面 150 符号 | — | 新 |
| 其余 §2 / §3 条目 | 多为 0 命中 | 新 |
方法论沉淀:做架构审阅前,先 grep 项目自身的债务/审计文档语料,据此标注每条发现是"重发现"还是"新发现"。对已有 137 条发现台账 + M1–M9 机制解剖的项目尤为必要(见 F-08)。
第 2 部分 架构问题与建议
2.0 总判:不完美的不是"框架选错",是"边界没切干净"
现有框架已经是分层 + 多语言,且分工正确(§4.8)。缺的三样没有一样是换架构能解决的:① 边界切分(§2.1);② 生成层(§2.5);③ 冻结层(§2.4)。
2.1 【P1】native/src/ 是上帝包,且不参与依赖图
证据〔实测〕:10 个 crate 合计 45,791 行;src/ 独 14,998 行 / 57 文件,承载六种强度完全不同的职责(capi 承诺层 / session_api 语义中立层 / unified 重构区 / engine / compiler 分析算法 / diagnostics 内容数据 / bin)。根 package vitro_native 依赖全部 10 个 crate,而没有任何 crate 依赖它 ⇒ 无法独立版本化、无法独立发布、无法让消费者只依赖"编译管线"而不带教学层。
外部惯例:rust-analyzer(≈200k 行)用扁平 crates/*(32 crate);matklad《Large Rust Workspaces》明确"10k–1M 行项目扁平结构最合理",理由是 cargo 的 crate 命名空间本就是扁平的,嵌套树无收益。
反差点:MoonBit 侧已按 L0–L9 切好 10+ 包——活跃区比冻结区更规范,这本身就是改进信号。
建议:按 L0–L9 对齐切分,从最外圈往里(每步独立可验收):
| 序 | 新 crate | 来源 | 为何先切它 |
|---|---|---|---|
| 1 | vitro_diagnostics |
src/diagnostics/(含 error_catalog/) |
内容数据,单向依赖,最干净 |
| 2 | vitro_analysis |
src/compiler/{cfg,data_flow,intent,algorithm_detector}(2,900 行) |
纯消费者,只读 AST |
| 3 | vitro_unified |
src/unified/ |
已被自身审计列为重构区,独立才好动 |
| 4 | vitro_session |
src/session_api.rs(1,132 行) |
capi 与 serve 共用 |
| 5 | vitro_gateway |
src/capi/ + serve 协议 |
对外契约,应最少变动 |
判据:cargo tree -p vitro_native --depth 1 一眼可读;根 package 行数 < 1k。
前置:必须先做第 0 步——新增 CI 依赖方向断言(扫 use 出有向图,断言①无环②诊断/教学层不得被编译管线依赖)。纯读、零风险,但能避免切到一半才发现环。且必须晚于 §2.4 的对拍锚分档。
2.2 【P2·档案已有】死文件 src/compiler/ast.rs
src/compiler/mod.rs 共 12 行,用 pub use vitro_ast as ast;,无 mod ast; ⇒ src/compiler/ast.rs(116 行)永不编译。其头部自称 pub mod decl/expr/stmt/types;,而 src/compiler/ 下并无这些文件,进一步证明是历史遗留。
风险:内含 compute_type_size + _impl 的字节级镜像,而那是全项目最关键的布局函数(决定 1MB 内存映像,即 A 级锚点③)。读者极易误认为这是活的第二套 AST。
建议:删除(或移入 docs/archive/)。并在 工程债务维护方案.md 登记——它是"登记了但未执行"的流程漏洞实例。
2.3 【P2·档案已有·确认】compute_type_size 是单源 + 三处委托
与最坏预期相反,逐处核对如下〔实测〕:
| 位置 | 形态 |
|---|---|
crates/vitro_ast/src/lib.rs:33 (+_impl:46) |
唯一实现 |
crates/vitro_codegen/src/lib.rs:871 type_size() |
委托 compute_type_size(ty, &self.struct_defs, …) |
crates/vitro_typeck/src/context.rs:8 |
委托(use vitro_ast::{compute_type_size, …}) |
src/compiler/ast.rs:37 |
死文件中的历史副本(§2.2,应删) |
moonbit/ast/types_predicates.mbt:198 |
MoonBit 单源 |
moonbit/codegen/func.mbt:215 / typeck/context.mbt:115 |
均委托 @ast.compute_type_size |
moonbit/parser/decl.mbt:889 |
〔补遗 b〕第三消费点(static_assert 的 sizeof 折叠,E3 场景)——三表全空 + sz>0 失败回退 None,属防御性回退设计(非 §2.6 哨兵缺失对象),但单源清单必须收录,否则 C# 改尺寸语义时此处是盲点 |
两处文档注释自陈"与 bytecode_gen::type_size 和 compile_pipeline::type_size 保持同一语义"——说明历史上曾有 4 份,现已收敛。
建议:把"布局/尺寸类函数必须单源、其余位置只做委托"写成 AGENTS.md 可检查纪律,并加 CI grep 断言(pub fn compute_type_size 全仓只允许 1 处)。
2.4 【P1】对拍锚锁死了重构空间(解 A 级枷锁)
机制:A 级锚点之一是"字节码 code 段逐指令",前提是"槽位策略版本化",现行策略为 v1。任何改变 code 段输出的内部重构 ⇒ A 级对拍必然全红 ⇒ 无法区分"重构引入缺陷"与"预期形状变化"。
这解释了三个已登记但未根除的病灶为何悬置:temp_slot 固定 4 槽(3 起同类 bug,20260906 审查报告 §7.2-7 明确建议"一次性根除")、next_local_offset 只增不减、以及"槽位泄漏 × ARC 插桩密度"的共振风险。
⚠ 关键限定:A 级三条锚锁的不是同一侧
| 锚 | 锁的是 | 敏感维度 |
|---|---|---|
| code 段逐指令 | codegen | 形状(槽位一动即全红) |
| 最终 1MB 内存映像 | VM | 语义(本就应该守) |
⇒ RunCore(VM + unified + VmObserver)的内部重构撞不到 code 段锚,重构自由度显著高于 codegen。被锁死的是 codegen 一侧。
〔补遗 b〕两锚有一个交叉点:CompileOutput 13 字段(vitro_codegen/src/lib.rs:954-971)除 code 外还含 globals_init_32/64(数据模式)与 global_data_end(R1 布局量——运行层堆起点据此计算)⇒ 任何动堆起点布局的改动会同时打红 codegen 锚与 VM 映像锚。分档白名单必须显式列出该交叉点。
建议:
- Rust 侧补上
SLOT_STRATEGY_VERSION(MoonBit 侧已有 v1 常量),A 级判定改三态:同版本 → 逐位必等;跨版本 → 按显式形状白名单判语义等价;白名单外 → 红。 〔补遗 b〕实施面比原估更小,但多一道流程门:MoonBit 侧 dump 外层已带slot_strategy(cmd/dump_compile/main.mbt:99),codegen_diff只比内层 14 键 ⇒ 分档 = Go 脚本消费外层键 + Rustdump-compile补外层字段——后者是动冻结区(native 白名单 P1–P7/U1/U2),需走白名单裁定,非"无依赖现在就能做"(见 §8.1)。 - 必须带证红:注入一条形状变更,证明白名单能区分"预期"与"非预期"。一个从未红过的检查,与一个坏掉的检查,在观测上无法区分。
- 沿用既有"白名单为空时不得绿"纪律。
- 顺序:分档机制 → 再动
temp_slot(挂上 8 条事故回归,v2 常量分档)。
2.5 【P1·新发现】对外面偏大,且与硬约束相反
实测(逐包统计 pkg.generated.mbti):
| 包 | 行数 | 公共符号 |
|---|---|---|
ast |
338 | 44 |
typeck |
99 | 31 |
bytecode |
96 | 18 |
lexer |
46 | 16 |
diag |
192 | 15 |
names / opcode / source |
35 / 162 / 32 | 10 / 5 / 5 |
libc / codegen / parser |
27 / 27 / 40 | 3 / 2 / 1 |
| 合计 | 150 |
对照 MoonBit迁移总计划.md §4 硬约束:
.mbti只暴露protocol全量 /lexer.tokenize/typeck.check三面
判定:该硬约束从未落地,且无 CI 校验。 11 个包全部发布完整 .mbti;protocol 包尚未创建(排在 S7)。
为何必须现在管(三条叠加):
ast(44) +typeck(31) 占一半,而这两个恰是变更最频繁的包(Type 17 / Expr 26 / Stmt 16 三族持续增长)。- §12「制度随迁」刚把承诺载体改为"包版本 +
.mbti" ⇒ 承诺的维护成本正比于面的大小,现状等于承诺 150 个符号。 ast增删一个变体即构成破坏性变更 ⇒ 最常变的包暴露最大的面,是最坏组合。
建议(复用 gen_diag 幂等三件套模式):① 定义对外面白名单;② 以 moon info 产物加断言,超出即红;③ 优先收窄 ast(收益最大)。须先于协议冻结。
〔修正 b〕三点 sharpen(详见 §8.2):口径——^pub 漏计 pub(all) 与抽象 type,真实对外面 200(ast 实为 70、parser 实为 ≥4 而非 1);优先级——从"先于协议冻结"改为"未发布包先于各自首次发布"(已发布 5 包约 119 符号是 mooncakes 既成承诺须走版本化弃期;未发布 6 包约 81 符号在零成本窗口,parser 已排队"随下一 minor"——窗口正在关);收缩单位——不是包清单而是管线签名闭包(全管线消费者 cmd/dump_compile 实测只消费 7 个跨包符号;签名内类型保持名义可见/抽象化,逐包独立收会把下游入口收死)。根因:MoonBit 顶层类型默认对外可见(struct BytecodeGen 无修饰即入 .mbti)且 priv 自家代码零使用——面膨胀是默认态,一次性收缩不够,必须同立白名单 -check 闸。
2.6 【P2】MoonBit 侧硬编码空表(无哨兵)
moonbit/codegen/func.mbt:215 → @ast.compute_type_size(ty, …, {});moonbit/typeck/context.mbt:115 → Map([])。而 Rust 侧 vitro_codegen/src/lib.rs:871 传的是真实的 &self.class_sizes。
判断:C-only 阶段 class_sizes 恒空,当前语义正确(F-2 已裁定砍 C++)。但这是"永远为空"的隐含假设,无任何断言保护 ⇒ C# 引入类/容器尺寸后会静默给出错误尺寸,且 C-only 语料永远测不出。
〔补遗 b〕parser/decl.mbt:884-889 的第三调用点三表全空,但其"查不到即回退 None(放弃折叠)"的防御设计使它不落本条哨兵缺失之列;typeck/context.mbt:100 另自陈一笔 O2 债(每次调用重建 struct/union 定义表,照搬登记未处理)。
建议:传真实表,或保留空表但加哨兵(guard / fail),让"非空场景到此即红"。项目已有"预留位 tripwire"制度(总计划 §12),此处是标准适用场景。
2.7 【P2】测试防线自身的维护负担
51 个 .rs + 19 份 *_FAILURES.md;单文件最大 end_to_end_extra_test.rs 5,152 行、host_contract_tests.rs 1,744 行。用例目录四套并存(cases/、cases_golden/、cases_template_generated/、golden/)。
建议:① 5k 行 E2E 按域拆文件(单文件 < 800 行);② 19 份自由文本台账收敛为单一机器可读台账,由 CI 与测试结果双向对账——项目已有 reports/facts.json 与一致性报告基础设施,可直接承载。
2.8 【P2】Rust 侧缺"接口面快照"
MoonBit 侧 pkg.generated.mbti 入版本控制(已是纪律),Rust 侧无等价物 ⇒ crate 公共 API 变更不在任何 diff 里显现。
建议:引入 cargo public-api(或自写 emitter)生成 API 快照并入库,与 .mbti 对称。
2.9 【P2】绑定层厚度应靠生成解决,不靠换语言
关键洞察:项目的架构纪律本身削弱了 wasm-gc 的收益。README 规定"复杂结构过边界统一走 JSON 字符串"——而这恰恰放弃了 wasm-gc 的 externref 对象直通优势。即:薄绑定靠的是协议设计(少入口 + 整块搬运),不是语言特性。绑定层厚是设计问题。
建议:以 gen_diag / gen_host_route 的"落款源 sha256 + 生成流程内置格式化 + -check 幂等"三件套模式,新增 gen_capi_bindings:
- 先抽元数据:
vitro_capi.h从"手写声明"改为机器可读capi_manifest.json(函数名 / 参数 / 返回 / 所有权语义 / 线程模型 / ABI 版本),头文件降为生成物。为何"所有权语义"必须进元数据:
tests/capi_string_ownership_contract_test.rs的存在说明这类契约最易在抽卡时丢失。 - 生成 C 头 / Go / Python / .NET 绑定骨架 +
vitro_abi_version常量。 判据:手写 Go shadow 的绑定部分被生成物替换;版本变更时第三方消费者能只改版本号完成升级(这是"承诺"的可机判形态)。
2.10 性能与观测
| # | 级别 | 问题 | 证据 | 建议 |
|---|---|---|---|---|
| P-1 | P1 | JIT 决策被单出口假设掩盖〔修正 b:还须按 JIT 形态分〕 | 门 1 实测 512ms vs 55ms(8.8×);F-3"不搬"依据只对 wasm-gc 成立;且 55ms 真身是模板超级指令(函数指针表,非机器码、无 unsafe——jit_templates.rs:1-8 自陈),与宿主 V8 JIT 正交可叠加,MoonBit 一等函数可表达 |
按出口 × 形态分别裁定;门 1 加第三基线 wasm-gc + 模板 JIT(见 §8.3) |
| P-2 | P2 | 宿主资源域零观测(承接审计 L6) | 63.6GB 泄漏 + 复发 + target 33GB 堆积;防线对 RSS/磁盘零观测 |
CI 增 RSS 上界 + target 体积门禁 |
| P-3 | P3 | cast_possible_wrap 246 处未 deny |
native/Cargo.toml 注释自陈 |
先清"算术→索引/容量"子类,再升级 deny |
| P-4 | P3 | 深结构递归栈预算未在 Rust 侧标定 | MoonBit 侧有 F4 实测(wasm-gc 854 / js 538 / native 1005),Rust 侧仅 MAX_PARSE_DEPTH=64 单点 |
深度上限做成按后端标定的常量表,两端共享同一口径 |
2.11 已销项
native/src/flutter_bridge.rs 命名债已解决〔实测〕:该文件不存在,会话包装层现为 src/session_api.rs(1,132 行)。此项从债务清单销账。
(待核:src/shared/source_loc.rs 与 crates/vitro_shared 是否存在两条 SourceLoc 路径——src/compiler/ast.rs 里 pub use crate::shared::source_loc::SourceLoc; 有此暗示。)
第 3 部分 目标架构(收敛结果)
3.1 总体形态
主进程(宿主)
│ 掌控一切:拉起、超时、内存上限、并发数、何时杀
├── 中枢(= LSP 的 Language Client 角色)—— 只管生命周期与路由,不持业务状态
│ ├── 引擎执行单元 —— 引擎本体(CLI + 机器可读 JSON),原地待命
│ └── 重插件执行单元 —— 声明"需要隔离"的插件,原地待命
└── 轻插件 —— 与中枢同处(低延迟、无 IPC)
引擎与插件分处不同执行单元,谁都不掌控进程,都由宿主/中枢拉起(见 D-04)。
3.2 语言布局:一门语言做核心
| 层 | 归属 | 语言 | 变动频率 |
|---|---|---|---|
| 引擎核心 | 机制 + 数据形态的内容 | 月兔全栈 | 低(可冻结) |
| 插件 | 需要执行第三方代码者 | 任意语言(TS/JS 优先) | 高 |
| 生成器 / 归一化器 | 构建期,不进产物 | Go | — |
| 真值源 | 构建期/外部 | Clang / dotnet / Roslyn | — |
| 宿主 | 产物之外 | Node / 浏览器 / 原生 | — |
判据(B-08):进产物的多语言可接受,条件是"其中一方不是核心逻辑的第二个实现"。
3.3 分层内容
| 层 | 内容 | 判据 |
|---|---|---|
| 机制(语言语义的延伸) | 时间旅行 / 单步 / 内存映射结构 / 指针追踪 / VmObserver 协议 |
留核心 |
| 数据形态的内容 | 知识卡片文本 / 算法识别规则表 / 模板 / 容器与 BCL 定义 | 留核心(不执行 → 不需隔离) |
| 需要执行第三方代码者 | 用户自编程判分器 / 自定义可视化逻辑 | 插件(会执行 → 需隔离) |
须先划的一条界线:评分策略做成**参数化策略(数据)**则留核心;做成"用户可编程判分器(代码)"则天然是插件。建议前者,并在数据 schema 里一次划清(见 D-13)。
3.4 唯一开放项:S6 门
机制层(memory / host / vm + 时间旅行 + VmObserver)的语言决策不必现在另开——它精确地就是 S6 的内容。
为何 S6 是正确的决策时点:
- 已完成 S0.5–S5 是低风险切片;S6 才是月兔语言事实咬得最狠的一层(F1 每算术指令需显式溢出检测 / F3 内存载体密度 1:3.8 / F4 wasm-gc 栈预算 854 层)。
- 在 S6 之前停下 = 付完安全部分的钱,恰好在风险最大的地方止损。
- S6 验收线已定义(门 1 条件 A:1k×1k 对照 512/55ms 双基线;门 3:集成版快照往返)⇒ 不需新增工作即可把"继续/回退"变成可判定决策。
- 本层恰是回退成本最低的一层:
vitro_vm(8,863 行) +vitro_runtime(2,043 行) +src/unified/的 Rust 实现已存在且全绿(见 A-04 / A-06)。
3.5 五步演进(1–3 与语言决策正交,现在就能做)
| 序 | 动作 | 依赖 | 可机判判据 |
|---|---|---|---|
| 1 | 单源清单 + 跨语言单源纳入 L1 判据(§2.3 / C-04) | 无 | 清单入版本控制 + -check 幂等校验器 |
| 2 | SLOT_STRATEGY_VERSION 分档(§2.4) |
无 | 跨版本白名单为空不得绿;注入形状变更能证红 |
| 3 | gen_capi_bindings(§2.9) |
无 | 至少生成 Go 绑定并替换手写版 |
| 4 | Rust 侧按 L0–L9 重切 crate(§2.1) | 须在 2 之后 | 根 package < 1k 行 |
| 5 | 语言决策:S6 门 | S6 完成 | 门 1 条件 A + 门 3 集成版 |
第 1–3 步对"全栈月兔"与"回退 Rust"两种结局都成立,是零后悔动作。
第 4 部分 观点台账(分门别类)
记录本次审阅全部实质性论点:原观点、裁决、依据、是否采纳。 裁决标记:✅ 采纳 · △ 部分采纳 · ✗ 不采纳 · ⛔ 撤回 提出方:
U= 用户 ·A= AI
A 门类|语言选型
A-01|Rust 是否已被"放弃"(提出:A,初判错误)
- 原观点:Vitro 已放弃 Rust。
- 错在哪:把
c0b642f的 diff 当作"审阅文案本身"来读——看到的是删除动作,而它是指向档案的路标(内容在917251e,≈8,022 行)。且 F-1 原文为"Rust 版冻结不删、降级为差分对照 oracle"。 - 正确观点:Rust 未退役,是换岗——从"生产实现"变为"真值来源"。
- 为何正确:F-1 明示冻结纪律"Rust 侧只收安全修复";README 仍列 native cdylib 为出口 1。
- 采纳:✅(用户原意即如此)。产出:§1.2 档案覆盖度核查;归档 tag
moonbit-probe-archive/moonbit-probe-consolidation。
A-02|Rust 的生态、出口、背书是否构成压倒性理由(提出:U)
- 原观点:Rust 生态成熟、可与多语言互操作、出口方向更多;微软已确立 C# / C++ / Rust 三大语言。
- 裁决:△ 部分采纳。
- 成立部分〔核实〕:微软 2026-09-15 官宣 Rust 升 Tier-1(Rust Foundation 刊 DevDiv 首席工程师 Victor Ciura 文;
rustc_codegen_utc接 MSVC 后端,100+ 仓库在用)。补正:Tier-1 名单为四个——C++ / C# / TypeScript / Rust。 - 不成立部分:出口数量优势在本项目被浪费——项目刚主动砍掉 45 个导出中 28 个无消费者(见 B-02);且 C 的交际优势 Rust cdylib 已经拿到(任何语言都能经 C ABI 调 Rust)。
- 采纳:△ 承认"Rust 是更确定的选择"这一长期判断;但不接受"因此本项目应回退全栈"——回退受 B-03 的决定性理由约束。
A-03|月兔生态不稳、出口面小(提出:U)
- 裁决:△ 部分采纳。
- 成立:生态不稳是项目自陈——F8(moonc v0.10.13 跨文件顶层
pub letlink-core ICE,入风险登记册 A8)、门 0(LLM 效率)仅判"弱通过"、moonbit/AGENTS.md29 条工具链陷阱。 - 不成立:"出口面小"在语言层只成立一半——MoonBit 有 wasm / wasm-gc / js / native 四个稳定目标(llvm 仅 nightly),不比 Rust 窄多少;真正变窄的是项目承诺的出口(三出口 → 一出口)。
- 采纳:△ 生态风险成立并已计入 §3.4;出口表述修正为"承诺面收窄"而非"语言能力窄"。
A-04|Zig 是否比月兔更次(提出:U)
- 原观点:Zig 未到 1.0、缺高级特性、写编译器要多写。
- 裁决:△ 部分采纳。
- 成立:三重税确凿——无 owned
String(拼接要ArrayList(u8))、每个涉及分配的函数都要穿Allocator参数、无 operator overloading / traits(Type::to_c_string类单源渲染要手写 dispatch)+ 无 derive。 - 不成立:Zig 的
union(enum)+switch强制穷尽,不构成纪律缺口;且低层语义上 Zig ≥ 月兔(@bitCast/@ptrCast/ packed struct / 无隐藏分配),"更次"不准确。 - 排除 Zig 的真实理由是两条硬条件:① 无 wasm-gc 目标;② 无跨版本可承诺的接口面(Zig 明确不承诺 Zig ABI 稳定)。
- 采纳:✅(Zig 不作引擎语言),但理由替换。
- 追记(2026-09-25):Roc 编译器 Rust→Zig 重写的一线佐证〔外部二手报道,非亲测:developersdigest.tech 对 Feldman 重写总结的报道〕。Roc(roc-lang)2023 年启动编译器重写,487 天完成(约 30 万行 Rust → 46.4 万行 Zig)。其亲历的 Zig 缺失便利与本条"三重税"几乎逐条重合:测试中需手动
defer管理内存(↔ 无 ownedString/ Allocator 穿参税)、无参数多态与 ad-hoc 多态(↔ 无 traits,单源渲染手写 dispatch)、无私有字段强制、无死代码检测、版本间无向后兼容保证(↔ 理由②"无跨版本可承诺的接口面"直接印证)。其选 Zig 的动机(增量构建速度 / 细粒度内存控制 / LLVM 生态复用 / 原生 unsafe——原 Rust 版 1,200 处 unsafe)全为字符串密集教学编译器所不占的诉求。另两条数据级收获:① 其 Rust 版 21 个内存 bug 全是 miscompilation(借用检查器防不住生成代码损坏内存)——外部数据支持本项目"code 段逐位对拍"保持最高优先级防线(语言级安全 ≠ 产物级正确,A 级对拍 583/598 SAME 的价值获得旁证);② 其"目标语言能力未落地即押注"教训(宣称增量 35ms 依赖未稳定的-fincremental,Zig 0.16.0 现实 8.6s)与门 2-W gc教训同构——"能力是否已落地必须实测"已被双案例独立收敛验证。结论:本条裁决不变,排除理由的证据等级由推理级升为一线印证级。
A-05|C 是否适合做引擎("语言的交际花")(提出:U)
- 裁决:△ 部分采纳。
- 成立且比用户所述更硬的一条:语义同族零阻抗——C 的 int 回绕 / unsigned 区分 / i64 截断 / 位重解释全是原生行为,不需任何模拟。这比 Rust(要
as/wrapping_*)与月兔(F1 需自补溢出检测)都更贴。 - 不成立:①
switch无穷尽性检查,与项目核心纪律"增删变体必须让全仓编译红"直接冲突(137 码 / 132 opcode / AST 三族持续增长);② 内存安全靠人肉审,而 README 自陈"这是一个 AI 实验田"——最差组合;③ 无可机判纪律载体(无forbid/ clippy 等价物)。 - "出口多"在本项目被浪费:项目正在收缩出口(B-02)。C 独有的只剩"源码可被任意工具链直接吃",对已靠 Go+Node 验证的项目边际不大。
- 采纳:✅(C 不作引擎语言)。
A-06|C++ 是否可作机制层回退(提出:U:"月兔全栈或者 Rust/C++ 写")
- 裁决:✗ 不采纳。
- 为何:与已生效裁定冲突——F-2 已裁定砍 C++(零迁移;
shadow_cpp99 / E2E 83 / CI 三 tier 随砍退役归档)。引入 C++ 会同时带回模板 / RAII 负担与"重建一套防线"的成本。 - 修正:回退项只有 Rust;且机制层恰是回退成本最低的一层(
vitro_vm8,863 行 +vitro_runtime2,043 行 +src/unified/已存在且全绿)。 - 采纳:✅("Rust/C++" 收缩为 "Rust")。产出:§3.4。
A-07|Lua 是否是好选择(提出:U)
- 裁决:✗ 不采纳(作为引擎语言)。三条理由〔核实〕:
- 无 Lua → wasm-gc 的源编译路径——Lua/Luau 进浏览器一律是"把 C/C++ 实现经 Emscripten / WASI SDK 编成线性内存 wasm + JS 胶水"(Luau 官方 Playground 即 C++ 经 Emscripten 出
luau.wasm)⇒ 破坏"同 wasm-gc 栈"前提; - 无静态类型 ⇒ 编译期穷尽纪律为零(与 C 同一失败模式);
- 性能不达标(PUC-Lua 是解释器;Luau 的 JIT 仅 x64 / arm64,wasm 上无 JIT)。
- 无 Lua → wasm-gc 的源编译路径——Lua/Luau 进浏览器一律是"把 C/C++ 实现经 Emscripten / WASI SDK 编成线性内存 wasm + JS 胶水"(Luau 官方 Playground 即 C++ 经 Emscripten 出
- 但两处设计值得借:① Luau 的"渐进类型 + 类型推断 + 内建 lint / 变换 / 文档生成 / LSP"一站式工具链形态;② 它以"限制默认暴露 API 面、使嵌入方可安全执行不可信代码"为设计目标——正是教学引擎该有的姿态,可直接借到接口与文档层。
- 采纳:✅(设计借鉴部分)。
A-08|OCaml(wasm_of_ocaml)作稳定侧(提出:U)
- 裁决:△(方案己降级,见 A-10;候选池结论保留)。
- 成立〔核实〕:同一 WasmGC 目标,官方表述"OCaml values are managed by the host garbage collector;no custom collector is shipped;JS interop works through shared GC'd references"——与月兔 GC 模型同构;同一浏览器基线 Chrome 119+ / Firefox 120+ / Safari 18.2+(与项目 P1 逐字一致);Jane Street 实测比
js_of_ocaml快 2–8×;6.1 起直接产.wasm;6.3 有 number unboxing / reference unboxing。 - 决定性未知〔未验证〕:
wasm_of_ocaml从 bytecode 编译,官方文档写明 31 位整数;Int32.t/Int64.t在 bytecode 表示中是 boxed 自定义块,6.3 的 unboxing 未记载覆盖 Int32/Int64 ⇒ VM 热循环若靠 boxed Int32/Int64 做算术,则"吃性能 + 吃精确语义"两条同时落空。 - 附带成本:
wasm_of_ocaml必需系统依赖 Binaryen ≥119;本机 OCaml 工具链全无〔实测,见 §1.1〕。 - 采纳:△ 候选池结论保留,方案本身降级(A-10)。
A-09|Kotlin/Wasm 是否比 OCaml 更贴"吃性能 + 吃语义"(提出:A,待核)
- 裁决:〔未验证〕,需与 OCaml 同批实测。
- 理由(推论,未经一手核实):Kotlin/Wasm 的
Int/Long直映 wasmi32/i64——32/64 位精确、无装箱、二进制补码回绕,与 C 的int/long long匹配度高于 OCaml 的 31 位 + boxed Int32/Int64;位重解释有Float.fromBits/Double.fromBits;ByteArray是紧凑的 wasm-gc i8 数组;且 Windows 环境友好、AI 语料巨大。 - 代价:产物体积(Kotlin/Wasm 带运行时)、语言定位偏应用层、
UInt/ULong仍实验。 - 采纳:✗(当前不作候选),但列入同一批 spike 的第二样本(见 §6)。
A-10|月兔全栈 vs 混合架构("两边都弱、分摊趋近于零")(提出:U)
- 裁决:✅ 采纳全栈收敛。两条判据,后者独立于性能测试即成立:
- (条件性)若稳定侧未通过性能前置测试,则确为双输;
- (决定性)集成形态:全栈的直接产物是消费者
moon add vitro/engine一条命令完成集成——且已部分存在(source0.1.1 /lexer0.3.0 /opcode/diag/ast均已发布)〔核实〕。混合则要求消费者装两套工具链、取两个 wasm 模块、自行接线,且 IR 升格为其必须理解的新契约 ⇒ 与 §12「制度随迁」刚确立的承诺载体(包版本 +.mbti)直接冲突。
- 判据修正:原论证用"两个运行时 / 两倍体积"(弱,因实例隔离已由 U2 解决);应改用"集成形态冲突"(强,直接命中消费者)。
- 采纳:✅ 混合架构降级为"已评估、暂不采纳";保留其候选池结论 + "按变动频率分配"哲学(后者改为模块边界形式落地,见 D-13)。
A-11|候选池过滤结论的适用范围(提出:A,后自我修正)
- 原观点:过完两道门后"候选池只剩月兔"。
- 问题:该过滤问的是"哪门语言能干全部活"。用户把问题换成了"哪门语言干一片活"——过滤器不同,答案不同。
- 正确做法:引用任何过滤结论时必须携带问题形式;问题形式变更时显式声明旧结论失效。
- 采纳:✅。产出:v1 §9.9 显式登记了 §9.8.4 结论的失效声明。
A-12|TS/JS 可否参与核心(提出:U,指 L9)
- 裁决:✅ 采纳(限 L9 插件层),见 D-01 / D-14。
- 核心判据:进产物的多语言可接受,条件是"其中一方不是核心逻辑的第二个实现"。L9 插件消费 payload、不重算编译语义 ⇒ 合规。
- 反例:分而自治(两个核心实现)⇒ 不合规。
B 门类|出口与分发
B-01|锚定浏览器作为出口(提出:A 评估 / U 确认)
- 裁决:✅ 采纳。理由:零安装分发、一份产物跨平台、与社区前端同进程(无 IPC 开销)、移动端"看"的场景天然覆盖。
- 关键澄清:"锚定浏览器"与"砍掉进程内 C ABI"是两个决策。
- 采纳:✅。判据:Rust 的 wasm32 出口已实证(3.75MB 零修改构建 + Node 下 C API 全链路 + E3070 诊断)。
B-02|砍 capi(提出:项目既有裁定)
- 裁决:△ 部分采纳。依据"45 个导出中 28 个亲证无消费者"——那是没有消费者,不是没有能力,同样的砍法在 Rust 侧一样能做,故它不是换语言的理由。
- 保留意见:协议层的收缩是唯一一条我坚持不变的保留意见(§12 制度刚把 ABI 版本化改为包版本 +
.mbti,方向与"继续砍承诺面"相反)。 - 采纳:△。
B-03|集成形态=一条命令(决定性理由)(提出:U)
- 裁决:✅ 采纳,并升级为 A-10 的决定性判据。
- 补强(A 侧,实测):Rust 从未分发——
native/Cargo.toml与native/crates/vitro_ast/Cargo.toml均无publish、无description、version 均0.1.0;而月兔侧 5 个包明确标"✅ 已发布"。⇒ 集成形态是只有月兔才有的东西,不是两者皆有。
B-04|.mbti 三面硬约束(新发现,见 §2.5)
- 裁决:✗ 该约束未落地。实测 150 个公共符号 vs 承诺三面,且无 CI 校验。
- 采纳:✅(作为待办,优先于协议冻结)。
B-05|LSP 的 M×N → M+N 论证(提出:A,外部惯例)
- 裁决:✅ 采纳为"出口多"的正解:不是一个协议出口 × N 个宿主,不是多个二进制出口。
- 产出:
vitro_cli serve(出口 3)从"三出口之一"升格为主出口。
B-06|serve 分帧方式(提出:A)
- 裁决:△ 建议评估。LSP 用
Content-Length头 + JSON body 分帧,从根上避免"payload 含换行需转义";JSON-lines 依赖转义纪律(AGENTS.md#27 已有),但分帧更稳。 - 采纳:△(列入待评估,非阻断项)。
C 门类|分层与边界
C-01|native/src 上帝包(新发现,见 §2.1)· C-02|死文件 src/compiler/ast.rs(档案已有,见 §2.2)· C-03|compute_type_size 单源(档案已有·确认,见 §2.3)· C-06|A 级锚分侧(新发现,见 §2.4)· C-07|对拍锚锁死重构(病灶档案已有,解法为新,见 §2.4)· C-08|MoonBit 硬编码空表(新,见 §2.6)· C-09|Rust 侧 API 快照缺失(新,见 §2.8)· C-10|gen_capi_bindings(新,见 §2.9)——以上技术细节见正文对应小节,此处不重复。
C-04|L1 判据是否覆盖跨语言
- 项目
三语化整备审计计划.md§1 判 L1"同一概念多真相来源"为 ✅ 已收口(R3 审计归档)。 - 裁决:△ 判据范围不足。Rust 侧确实收口了(§2.3 实测确认),但迁移引入了跨语言版本:每个"单源"在月兔侧都有孪生,同步义务是人工的("照搬不私改")。R3 审计只检查仓内同语言重复,不覆盖"两侧各一份、靠人工同步"。
- 采纳:✅。建议把 L1 判据扩为"单一真相源清单"(每条记:单源位置 ×2、同步方式、检测锚点级别),入版本控制 +
-check校验器。
C-05|语言定义数据化 = 插件入口(提出:U 问"要不要预留插件入口")
- 裁决:✅ 采纳,且两者是同一件事。
- Δ 原建议"不预留空位,预留 schema"——数据插件与"语言定义数据化"是同一件事:预留插件入口 = 把语言描述定成有 schema、有版本的数据。一石二鸟:既得插件面,又得"加一门语言不改核心"。
- 纪律:若仍要预留任何空位,必须同时埋 tripwire(§12 已有该制度),否则即死代码加假承诺(
src/compiler/ast.rs正是先例)。 - 采纳:✅。
D 门类|进程与插件拓扑
D-01|"插件"的形态(提出:U)
- 原表述(A 方曾误读为进程内挂载点):隔离式多实例——引擎编 wasm-gc,调用一次拉起一个实例,出问题杀掉该实例;实例互不干预("不相交的平行线"),可并行多个;"带扩展的实例"与"不接受扩展的实例"并存,前者可被后者兜底。
- 裁决:✅ 采纳。机制与 LSP 的"Language Server 独立进程"一致;且该底座已在 U2 拍板中存在:
docs/current/06-出口与协议/wasm多实例并发模型与U2拍板.md已裁定浏览器 N × Web Worker(WebAssembly.Module结构化克隆后各自 instantiate,编译一次、实例化 N 次)、Nodeworker_threads;原话"能力从'单实例互斥'升为'N 实例构造性隔离'"。 - ⇒ 插件不需要新建任何机制,它是 U2 的一个用例。
- 〔修正 b·归属精确化〕TS 插件是宿主 JS 执行体(Worker/子进程),不经 wasm 实例化机制(结构化克隆
WebAssembly.Module那套);"无需新机制"的结论不变,但机制地基是宿主 Worker这一更底层能力,而非 U2 本身。U2 的"每实例 1MB"成本账属引擎实例,不属插件实例。终局裁决见 §8.5。
D-02|隔离是否来自 wasm-gc(提出:U:"由于是 wasm-gc 形态,进程互不干预")
- 裁决:⛔ 该归因错误,且与项目自身文档冲突——U2 文档 §2.1 标题即「隔离来自 core wasm 实例模型(不必等 wasm-gc)」。
- 正确:wasm-gc 的额外贡献仅两条:① 杀得更干净(无自管堆、无原生资源);② 能传对象(
externref)而非只传字节。 - 为何必须写清:若归因为"只有 wasm-gc 才能隔离",将来在线性内存版(退役前的 Rust 后端)上会做出错误取舍。
- 采纳:✅(采用修正后表述)。
D-03|"不会卡死"的前提(提出:U:"兜不住可以杀死进程,也不会卡死")
- 裁决:△ 成立但缺前提。wasm 没有抢占式中断——同步执行中的死循环无法被杀死,因为没有任何机会执行"杀"这一动作。
| 场景 | 能否终止 |
|---|---|
| 主线程同步执行插件 | 否 |
| Worker / 子进程内执行 | 是(terminate() / kill()) |
WebAssembly.Fuel 燃料计量 |
原理可行,未标准化,不得作架构依赖 |
- ⇒ "能杀"的功劳在 Worker / 进程,不在 wasm-gc。
- 采纳:✅,并升格为硬约束:插件必须运行在可终止的执行单元内。否则一旦有人主线程内跑插件,"可兜底"承诺即为空。
D-04|同居 vs 分居(保护谁)(提出:A 初判错误 → U 修正)
- A 初判(v1 §9.14.3):"一个会话一个 Worker,核心与插件同居"。
- 错在哪:同居意味着杀掉插件必连带杀掉引擎。该建议在"保护引擎"目标下不成立。
- 正确区分:
| 目标 | 正确做法 |
|---|---|
| 保护 UI | 同居 + 整锅杀 |
| 保护引擎 | 分执行单元——杀插件单元,引擎存活 |
- 用户目标是"插件影响几乎为零"= 保护引擎长跑 ⇒ 分执行单元。VS Code 正是如此(Extension Host 与 Language Server 分居两进程)。
- 采纳:✅(改为分执行单元)。
D-05|"原地待命"是否等于纯拉模式(提出:U"都是原地待命" → A 初判错误)
- A 初判(v1 §9.15):"引擎待命 ⇒ 纯拉模式 ⇒ 必须批量拉"。
- 错在哪(因果链错误):LSP 的语言服务器是 long-lived 进程,持续读 stdin / 写 stdout,但它可主动发 notification(
textDocument/publishDiagnostics即服务端主动推),同时也有 pull 形态(connection.languages.diagnostics.on)——两者并存。 - 正确:"原地待命"约束的是生命周期掌控权(谁拉起、谁杀死),不是消息方向。
- 推论:单步 / 时间旅行可以走推送,但必须配取消机制(LSP 对应物
$/cancelRequest);批量拉仍是有效优化,但属性能选择,非"待命"的必然要求。 - 采纳:✅(采用修正后表述)。
- 〔补遗 b〕D-04 × D-05 之间有一笔未对账的耦合:现状消费者入口
vitro_step_next_json/vitro_get_step_payloads_json是同步拉模式 FFI 直调——"批量拉只是性能选择"的结论只在进程内直调世界成立;在 D-04 的分居拓扑下逐步拉 = 每步一次 IPC 往返(单步调试 10k 步 = 10k 次消息往返),推送流或批量窗口至少其一为必需。该裁决应在 S7 有真实拓扑时落(见 §8.4)。
D-06|中枢的职责边界(提出:U"子进程中枢负责决定给插件还是引擎")
- 裁决:✅ 采纳,且有工业界成熟划分——"中枢"即 LSP 的 Language Client,职责四项:
- spawn 并管理 server 进程生命周期;
- document selector 配置(哪些文档交给哪个 server);
- 文件系统事件同步;
- 协议消息路由。
- 照四项写,不得超出——尤其不持有业务状态(业务状态归引擎)。
- 采纳:✅。
D-07|中枢的路由规则形态(提出:A 风险提示 → LSP 解法)
- A 曾提示风险:"中枢持有路由规则 ⇒ 可能成为第二个真相源"。
- 裁决:✅ 有标准解法——LSP 的
initialize握手:client 发 capabilities → server 回 capabilities → "Features are only enabled if both client and server support them during the initialize handshake"。 - ⇒ 路由规则 = 能力表协商,且能力表是数据(不是中枢里的分支代码)。与 C-05 的数据化纪律一致。
- 采纳:✅。
D-08|插件是否应分两档(提出:A,据 VS Code 惯例)
- 裁决:✅ 采纳。VS Code 明确两种模式并存:Direct Providers(跑在 extension host 进程内:简单场景、单文件、低延迟无 IPC)vs Language Server / LSP(独立进程:复杂、多文件、隔离可杀)。
- ⇒ 本方案插件同样分两档,按插件声明的"是否需要隔离"决定:轻插件与中枢同居;重插件独立执行单元。
- 价值:这解决了 D-04 与 D-01 之间"同居 vs 分居"的对立——答案不是二选一,是按需分档。
- 采纳:✅。
D-09|宿主资源是否在隔离之内(提出:项目 U2 §2.1 已记 → A 升格)
- 事实:U2 文档 §2.1 已记"宿主
import面不在构造性隔离之内";杀实例不回收宿主侧资源(DOM /fetch/ 定时器 / Worker / 文件句柄)。 - 裁决:✅ 采纳,并升格——在插件场景下这条从"已记录"升级为"主风险",因为插件的全部价值恰在于与宿主交互。
- 采纳:✅。要求:插件契约必须显式列出可访问的宿主能力清单 + 清理责任归属。
D-10|payload 归属竞态(提出:A)
- 裁决:✅ 采纳为硬约束。该拓扑会把一类历史偶发竞态升级为架构级必然:
代码审阅与修复追踪20260906.md曾记前端事故"每节点 2–3 次串行 FFI +_loadNodes重入竞态(旧任务旧数据覆盖新任务)"。那本是 UI 层偶发缺陷;本拓扑下主进程是多个实例的 payload 汇聚点,同类竞态成为结构性的。 - 必须结构性解决:每条 payload 带
instance_id/session_id+ 单调递增序号;主进程侧规定归属与丢弃规则——过期序号一律丢弃,而非后到者覆盖。 - 采纳:✅(升级为硬约束)。
D-11|部署形态优先级(提出:U"类 WebView 项目有进程,像 VS Code")
- A 初判(v1 §9.15.4 后果 1):按"最弱一级(浏览器无进程)"写契约。
- 错在哪:优先级反了。纯网页(浏览器标签页)确无进程,但 Electron / Tauri / WebView2 / WKWebView / MAUI / Flutter 宿主均有真进程。佐证:本项目前端三代(Flutter / Tauri+TS / MAUI)全是 WebView 或原生宿主形态。
- 正确:主形态按"有真进程"设计(强保证);纯网页作为显式声明的降级档(弱保证)。
- 采纳:✅(优先级反转)。
- 技术事实保留:Node / 桌面 = 真子进程(回收强,OS 回收一切);浏览器 =
Worker+terminate()(回收中,JS / 宿主 GC 回收但宿主侧引用需自查)。
D-12|引擎的进程内状态约束(提出:A,据 D-01 推论)
- 裁决:✅ 采纳。引擎不掌控进程 ⇒ 引擎不得有进程级单例状态,否则多实例互相污染。
- 可依先例:F-6(
Hasher种子 wasm/wasm-gc = 0,确定性)、总计划 §5 两条不变量("喂入不重置运行态" / "会话级配置不可被初始化覆盖")、审计已收口的 L1"会话单例"。 - 判据(可机判):实例状态全部由显式句柄(
session_id/instance_id)持有;验证方式 = 交错调用两个实例,断言互不影响。 - 采纳:✅。
D-13|机制与内容是否都应留核心(提出:U"都不外置,都是已有资产,不做切割")
- A 初判(v1 §9.14.4):用"变更频率"(机制低 / 内容高)作判据,据此把内容划归插件层。
- 错在哪:判据选错。隔离与可终止性的唯一用途是应对"会挂起、会失控的执行体";而内容是数据与规则,不执行、不会挂起 ⇒ 根本不需要隔离。
- 正确判据:这段东西会不会执行我们控制不了的代码?会 → 插件;不会 → 核心。
- "变更频率"未失效,只是解法不是外置而是生成器:高变动内容由"生成器 +
-check幂等"消化(项目已有gen_diag/gen_host_route两台)。 - 修正后的分层:机制 → 核心;数据形态的内容 → 核心;需执行第三方代码者 → 插件。
- 须先划的界线:评分策略做成**参数化策略(数据)**则留核心,做成"用户可编程判分器(代码)"则天然是插件 ⇒ 建议前者,且须在数据 schema 里一次划清。
- 采纳:✅(采用修正后判据)。
- 〔补遗 b〕三分法漏了第四类"推断算法":
semantic_label(infer_semantic_label,unified/collector.rs:78启发式函数)与算法识别(vitro_algorithm_steps2,273 行、七模块模板匹配 + confidence)——既非机制、非纯数据、非第三方代码,恰是 M6 零防线对象。§7.1 把"算法识别规则"派 TS 侧,"规则"二字掩盖了 2,273 行推断代码的重写成本(且无快照防线保护)。建议:推断留核心(月兔随 S8/S9 重写),呈现归 TS;若迁 TS,golden payload→输出快照防线必须先行(见 §8.6)。
D-14|L9 是否可用任意语言写(提出:U"插件不必月兔写,TS/JS 比较吃香")
- 裁决:✅ 采纳。这正是 Neovim 模型(C 核心 / Lua 扩展 / 任意语言 client ↔ 月兔核心 / L9 插件 / 任意语言宿主),且满足 A-12 判据。
- TS/JS 的四条真实优势:
- 在浏览器里消除一条边界——宿主本身就是 JS,JS 插件住在 payload 到达处(零搬运层);而月兔写的 L9 在浏览器里会变成第三个 wasm-gc 模块,凭空多一条跨模块边界。
- 零新增工具链——Node 宿主已在 F-4 裁定中。
- 抽卡效率与变更频率对齐——L9 变更频率最高,TS/JS 语料最丰富。
- 生态契合工作内容(数据变换 / 可视化 / 知识图谱 / 评分;项目历史上已用过 CodeMirror 6)。
- ⚠ 反直觉的坑:TS 插件放主线程 = 失去可杀性(见 D-03)。可杀性来自"与核心同居在一个可终止执行单元内",不来自 wasm-gc 或"它是独立模块"。 "Web 天然集成"最易被误读成"放主线程"。
- 采纳:✅。
E 门类|验证体系与防线
E-01|"Rust 冻结为 oracle"这一说法需精确化(提出:A)
- 裁决:✅ 采纳。Rust ↔ 月兔的差分是转写级的(
照搬不私改;发现 oracle 可疑登记不修正)⇒ 只抓翻译 bug,不抓设计 bug,价值随迁移完成衰减到零(无新代码可搬时即无对照物)⇒ 不是可长期依赖的验证层。 - 对照:Clang golden ↔ Vitro 是真独立(不同实现、不同作者)。
- 采纳:✅。推论:"永久保留 Rust 当 oracle"不是可持续架构路线。
E-02|Go 的角色(提出:U"确定 oracle 是 Go 写的")
- 裁决:△ 表述需修正。Go 不是 oracle,是归一化器;真正的 oracle 是 Clang / dotnet / Roslyn。
- 两个角色须拆开:
| 角色 | 现在是谁 | 独立性要求 | 裁决 |
|---|---|---|---|
| 真值源 | Clang / dotnet / Roslyn | 必须完全外部,绝不能自己写 | 不动 |
| 归一化 + 驱动 | Go,10,767 行,shadow_verify.go capi 直调 680 用例 |
不得与引擎共享代码 | 保持 Go |
- 保持 Go 的理由:它的任职资格是"和引擎不同"——Go 与 Rust / 月兔都不共享代码;
sort.Strings是字节字典序,与 canonicalize 口径一致(AGENTS.md#29 已记)。 - 采纳:✅(采用修正后表述)。
E-03|真值链的完整形态(提出:A,核实 U 所述)
- 裁决:✅。完整链条:外部真值(Clang / dotnet / Roslyn)→ Go 归一化驱动 → 两实现互拍(Rust oracle ↔ 月兔)。
- C# 侧已有双 oracle 格局〔核实
CSharp前端引入计划.md〕:dotnet 管运行时 golden("clang 之于 C shadow")+ Roslyn 管静态对拍;语料 = SharpTutor CourseData 13 章 521 个.cs+ 76 个 golden。 - 方法论提炼:项目的核心方法论是「真值必须来自实现之外」——Rust 冻结为 oracle 是同一方法论的延伸。
E-04|冻结快照的假绿陷阱(提出:A)
- 裁决:✅ 采纳为纪律。"冻结"唯一能被自动验证的形式是冻结快照对拍(取上一发布版本的 golden payload 喂当前引擎,断言新增字段可忽略、删除或改名即红)。
- ⚠ 该测试最典型的假绿是:快照本身是拿当前实现生成的。 ⇒ 必须写死纪律:冻结快照只能来自上一个发布版本,入库后禁止重新生成。 否则该测试会永远绿,而且绿得非常像真的。
- 采纳:✅。
- 〔修正 b·现状重估〕本条大部分已存在:StepPayload v0.1 已于 2026-09-12 冻结(锚
10591ad),字段冻结测试、出口形状一致性、serve 冒烟就位,五组签字回放 61/61 PASS 即 E-04 所要"冻结快照"的现存雏形资产;"只增不改"已在真实运转(2026-09-14ArraySnapshot.truncated新增为一次零影响演进)。真缺的只有机器可读形态——见 §8.4。
E-05|探针面三条判据(提出:A,据 U 的抽卡模型)
- 裁决:✅ 采纳为架构验收判据(替代原"能否重复实现")。
- (U)人机协作模型是抽卡式:人发任务 → AI 依训练分布生成 → 人靠探针与审阅找缺陷(而非逐行审代码);项目明确声明"眼见不一定为实"(假绿 / 假红并存,见审计 M1–M9)。
- 在该模型下,两份 AI 生成的实现是同分布的,独立性本就微弱 ⇒ 判据改为三条:
- 探针面够不够多(每个新出口 / 消费者 / 锚点 = 一条独立探针路径);
- 够不够便宜(生成物 > 手写物;结构化台账 > 自由文本);
- 够不够持久(冻结协议使旧探针长期有效)。
- 采纳:✅。
E-06|生成器语言(提出:U"更应该考虑生成器是什么语言写的")
- 裁决:✅ 采纳——保持 Go,且必须是 Go,任职资格与 E-02 同源:
| 要求 | 为何 Go |
|---|---|
| 必须与引擎不同 | 同语言的生成器 ⇒ 生成器 bug 与引擎 bug 同类、互相印证、形成自证循环。全栈月兔后该约束强化:绝不能用月兔写"生成月兔码表"的生成器 |
| 能读引擎侧源码 | gen_diag 读 Rust 源、gen_host_route 读 host_func_id.rs;行 / 正则级抽取即可 |
| 零依赖跨平台 | go run ./scripts/... 无依赖安装;Windows / CI 一致 |
| 输出字节确定 | 幂等 -check 依赖可控格式化与排序 |
| 不共享引擎运行时 | 避免"引擎坏了生成器也坏" |
- 采纳:✅。
E-07|多语言的正当性判据(提出:A,据 U"两边都弱")
- 裁决:✅ 采纳,并升级为本章收敛判据:
| 形态 | 进产物 | 消费者感知 | 裁决 |
|---|---|---|---|
| 构建期多语言(生成器 / 归一化器 / 对拍驱动 / CI 脚本) | 否 | 否 | 鼓励 |
| 运行期多语言 = 核心逻辑的第二个实现 | 是 | 是 | 拒绝 |
| 运行期多语言 = 扩展层或宿主层 | 是 | 否 | 可接受 |
- 精确表述:进产物的多语言并非一律禁止,条件是"其中一方不是核心逻辑的第二个实现"。Neovim 的 Lua 是扩展层(用户自写插件),不是 core 第二版 ⇒ Neovim 结构成立。
- 按此复核当前项目:月兔引擎 + Go 生成器 / 归一化器 + Node 宿主 + Clang / dotnet / Roslyn ⇒ 全部合规;分而自治 ⇒ 不合规;Rust oracle ⇒ 过渡期合规。
- ⇒ 项目现有的多语言结构本来就是"对的多语言"。要改的不是语言数量,是边界切分、生成层与冻结层。
- 采纳:✅。
E-08|L9 外置的真实代价(提出:A)
- 裁决:✅ 采纳为风险登记。L9 外置 = 把"教学正确性"移到防线之外。 审计已记 M6「教学内容零防线」(
semantic_label/ 算法 description / 知识卡片的内容正确性无任何自动验证),而 L9 正是这些内容唯一所在层。 - ⇒ 插件侧必须自带防线,而那条防线不受项目控制。 应对:契约要求「golden payload → 插件输出」的快照测试,且遵守 E-04 的假绿纪律(快照不得由当前插件实现生成)。
- 采纳:✅(登记为风险)。
E-09|C++ 语料的处置缺口(提出:A)
- (U 裁决)"留存 C / C++ / C# 语料"。
- 裁决:✅ 该方向对,但存在未明确处置:语料留存 ≠ 防线留存。F-2 已记"C++ 防线(
shadow_cpp99 / E2E 83 / CI 三 tier)随砍退役归档"⇒ 语料留下、防线退役,则 99 + 83 条成为无人校验的资产(不再执行,将来无从判断是否仍成立)。 - 处置二选一(不可悬置):① 最小回归——只跑语料、只断言"不崩 + 不 panic",不做 Clang 对拍(成本低,保住活性);② 明确冻结——台账记"冻结日期 + 冻结时状态"。
- 判据:台账每条语料带
status ∈ {active, frozen}+ 日期(与既有差异台账class/carry_over格式同构,可直接并入)。 - 采纳:✅。
E-10|S6 是否是语言决策的正确时点(提出:A)
- 裁决:✅ 采纳。已完成 S0.5–S5 是低风险切片,S6 才是月兔语言事实咬得最狠的一层(F1 / F3 / F4);且 S6 验收线已定义(门 1 条件 A + 门 3 集成版)⇒ 不需新增工作即可把"继续 / 回退"变成可判定决策。
- 采纳:✅。见 A-06、§3.4。
F 门类|流程与方法论(含 AI 侧失误)
F-01|声称"已落盘"但未落盘(A 侧)
- 事实:v1 某轮口头称"把那一节并进报告了(§9.10)",实际未执行写入;下一轮 grep 核实(
grep -c "9\.10"→ 0)才发现。 - 教训:声称"已落盘"之前必须 grep 验证,不能凭记忆。
- 采纳:✅(写入本报告 §0.2 与
repo-architecture-review技能)。
F-02|前提未核就下判断(A 侧)
- 事实:把
c0b642f读作"审阅文案本身",而不是"指向档案的路标"。 - 教训:读一个"删除类"提交时,先问它指向什么,而不是它删了什么。
- 采纳:✅(见 A-01)。
F-03|结论性断言论断未核(A 侧)
- 事实:断言"wasm-gc 的 GC 类型是模块私有、跨模块互认机制未成熟"。错误——规范支持跨模块结构化类型等价(V8
TypeCanonicalizer把 isolate 内所有模块类型 canonicalize 成全局uint32_tid;M1 的(struct (mut i32) (mut i64))与 M2 的同形定义是同一类型),规范侧对应机制称"封闭类型"。 - 准确表述:规范允许,工具链不支持(MoonBit 与
wasm_of_ocaml都未提供"按 canonical 形式暴露类型"的机制)⇒ 边界今天是字节级,属工具链限制而非永久约束。 - 教训:涉及"某工具链是否支持某能力"的断言,必须核。 本次会话共有三处此类断言出错(F-02 / F-03 / F-05)。
- 采纳:✅(见 D-02)。
F-04|判据选错(A 侧)
- 事实:用"变更频率"作分层判据(D-13)。
- 教训:判据要选"决定性的那一维"——此处决定性的是"是否执行不可控代码",不是"变更快慢"。判据选错会让整条分层结论反向。
- 采纳:✅(见 D-13)。
F-05|优先级搞反(A 侧)
- 事实:把"按最弱一级(无进程的浏览器)写契约"当作保守做法(D-11)。
- 教训:"保守"用错了地方——按最弱档写契约会让主形态被降级档拖累。正确做法是按主形态设计 + 显式声明降级档。
- 采纳:✅(见 D-11)。
F-06|因果链错误(A 侧)
- 事实:由"原地待命"推出"纯拉模式",再由"纯拉"推出"必须批量拉"(D-05)。
- 教训:"待命"是生命周期语义,不是消息方向语义。 推论前先确认约束的作用域。
- 采纳:✅(见 D-05)。
F-07|归档标签缺失与工具坑(A 侧,实操)
- 事实一:项目已有 6 个 tag(
before-frontend-split/editor-refactor-freeze/pre-restructure/rust-oracle-freeze/s0.5-baseline-freeze/v2-before-remove-v1)——已有"为可回溯状态打 tag"的惯例,但 8,022 行探测档案只有裸哈希。裸哈希不自解释(本次我从c0b642f的 diff 误判即是实证)。 - 已补:
moonbit-probe-archive→917251e;moonbit-probe-consolidation→c0b642f(annotated,内容校验通过)。仅本地,需git push origin <tag>发布。 - 事实二〔实测,工具坑〕:带斜杠的 tag 名在本环境静默失败——
git tag -a archive/xxx <ref>返回rc=0但 ref 不创建(无法建.git/refs/tags/xxx/嵌套目录)。凡写.git/的操作必须校验结果,不能只看退出码。 - 事实三:
.github/workflows/的actions/checkout@v4未设fetch-depth(默认浅克隆)⇒ CI 侧取不到917251e。 - 采纳:✅。
F-08|覆盖度自我核查方法(A 侧,方法论产出)
- 方法:在声称任何"新发现"之前,先 grep 项目自身的债务 / 审计文档语料,统计关键词命中数:
- 命中高 → 重发现,标注"档案已有"并下调价值;
- 概念词命中 0 但素材类词有命中 → "素材已有,结论是新";
- 全 0 → 才是新发现。
- 效果:本次 9 条架构发现中 2 条被识别为重发现(§1.2),避免了把重发现当新贡献——对已有 137 条发现台账 + M1–M9 机制解剖的项目尤为必要。
- 采纳:✅。已沉淀入
repo-architecture-review技能的必备步骤。
第 5 部分 错误与撤回总表
本次审阅共 12 处判断被更正或撤回。汇总如下(详情见台账对应条目):
| # | 原判断 | 性质 | 修正为 | 条目 |
|---|---|---|---|---|
| 1 | Vitro 已"放弃" Rust | 前提未核 | Rust 未退役,是换岗(生产实现 → 真值来源) | A-01 / F-02 |
| 2 | "§9.10 已并进报告" | 流程失误 | 实际未写入;声称落盘前必须 grep 验证 | F-01 |
| 3 | wasm-gc GC 类型模块私有、跨模块不可依赖 | 事实错误 | 规范允许跨模块结构化类型等价;工具链不支持 | D-02 / F-03 |
| 4 | 分而自治是反模式(判据:重复提供独立性) | 判据错误 | 抽卡模型下两份 AI 实现同分布,该资产不存在;降级为"已评估、暂不采纳" | A-10 / A-11 |
| 5 | 跨语言序列化层会掩盖字节级差异 | 事实错误 | 项目已证明跨边界可逐字节精确比对(canonicalize + A 级 code 段 + F9) | A-11 |
| 6 | 拆分判据用"变更频率" | 判据选错 | 改为"会不会执行我们控制不了的代码" | D-13 / F-04 |
| 7 | 核心与插件同居一个 Worker | 目标混淆 | 同居保护 UI,不保护引擎;须分执行单元 | D-04 |
| 8 | 原地待命 ⇒ 纯拉模式 ⇒ 必须批量拉 | 因果链错 | "待命"约束生命周期掌控权,不约束消息方向;推送合法(须配取消) | D-05 / F-06 |
| 9 | 按最弱一级(无进程浏览器)写契约 | 优先级反了 | 按主形态(有真进程)设计 + 显式声明降级档 | D-11 / F-05 |
| 10 | "候选池过完第二关只剩月兔" | 问题形式漂移 | 该结论属"哪门语言干全部活";换问题即为失效,须显式声明 | A-11 |
| 11 | Zig"更次" | 表述不准 | Zig 低层语义 ≥ 月兔、穷尽性合格;排除理由换为"无 wasm-gc + 无跨版本接口面" | A-04 |
| 12 | "Rust/C++ 写"机制层 | 与既有裁定冲突 | C++ 已被 F-2 砍;回退项收缩为 Rust | A-06 |
| 13 | 〔修正 b〕§9.9.2(v1)判 Kotlin/Wasm"倒在无 C 式 struct 布局控制"一刀切排除,A-09 又判其"Int/Long 直映 i32/i64 无装箱、匹配度高于 OCaml" | 对同一语言两处能力断言矛盾(未核) | 两处均未一手核实;按 F-03 教训,排除表属未核断言。现值低(§9.9 已降级)但为同类病灶定稿期复发之证 | v1 §9.9.2 / A-09 |
| 14 | 〔修正 b〕"#![forbid(unsafe_code)] 使 JIT 结构性不可能" |
形态误判(把模板 JIT 当机器码 JIT) | 55ms/8.8× 的真身是模板超级指令(函数指针表,无机器码无 unsafe,jit_templates.rs:1-8)——forbid 并未阻止它;模板 JIT 与宿主 JIT 正交,MoonBit 可表达 |
§2.10 P-1 / §8.3 |
| 15 | 〔修正 b〕把"协议冻结"列为待办(B 组 #11 / E-04) | 对自家已建资产盘点不足 | v0.1 已于 2026-09-12 冻结(锚 10591ad)+ 字段冻结测试 61/61 回放就位——"待办"实为"机器可读化 + TS 类型生成" |
E-04 / §8.4 |
共性归因:12 处中 5 处源于未核实前提或断言(1 / 3 / 2 / 11 / 12)、3 处源于判据或优先级选错(6 / 9 / 4)、2 处源于因果链(8 / 7)、1 处源于问题形式漂移(10)、1 处源于流程(2)。 〔修正 b〕第二会话新增 #13–15,共性归因为第六类:对自家已建资产盘点不足——把"已冻结"当"待办"(#15)、把"形态受限"当"不可能"(#14)、把同一能力的两次评估互不对照(#13)。据此给 F-08 方法论扩一条:声称任何"待建/不可能"之前,先盘点自家已建基础设施(grep 不止对债务语料做,也要对资产语料做)。
⇒ 最有效的单一改进:涉及"某工具链/某产品是否支持某能力"的断言,一律先核。 此类断言本会话出错 3 次(#1 / #3 / 以及 A-09 的自我标注),是错误密度最高的一类。
第 6 部分 未验证项与待办
6.1 未验证(不得当作结论使用)
| # | 项 | 检查方式 | 成本 |
|---|---|---|---|
| U-1 | wasm_of_ocaml 的 Int32 / Int64 装箱 |
跑 1k×1k i32/i64 循环,与门 1 的 512ms / 156ms 同口径对比 | 半天 |
| U-2 | Kotlin/Wasm 的 Int/Long 装箱与体积 |
同上;另测产物体积 | 半天 |
| U-3 | Windows 上 OCaml 工具链可用性 | opam + dune + wasm_of_ocaml + Binaryen ≥119 能否装起来并产出可跑 .wasm |
半天 |
| U-4 | 全量 cargo clippy --all-targets |
本机沙箱拒写 native/target/;待 CI 复核 |
— |
| U-5 | moon test / moon check --target all |
moonbit/_build 写入受沙箱限制 |
— |
| U-6 | as usize 类无防御转换的全库审计 |
审计 L6 提及,本次仅覆盖 clippy 可见部分 | 数天 |
| U-7 | src/shared/source_loc.rs 与 crates/vitro_shared 是否双路径 |
核 SourceLoc 定义处 |
半小时 |
| U-8 | templates/ 与 containers 的数据驱动化程度 |
逐项核 | 半天 |
| U-9 | 性能实测(本次全部条目按既有文档口径标注为未实测) | 需基准环境 | — |
⚠ spike 顺序不可反:先 U-3(环境可用性),后 U-1/U-2(性能)。环境不通则性能无需测。
6.2 待办(按可动手性排序)
A 组 — 不依赖任何未决策,现在可做
- 收窄对外面〔修正 b〕:未发布 6 包先于各自首次发布收面(零成本窗口,parser 已排队"随下一 minor"——正在关);已发布 5 包走版本化弃期(19 下载多自测,弃期为零);收缩单位 = 管线签名闭包(§8.2);先立白名单
-check闸并证红,再加priv(MoonBit 默认公开,面会自动再长)。顺带剔除parser::parse的cpp_mode? : Bool参数(F-2 终局,parser 未发布是最后的免费时机)。 SLOT_STRATEGY_VERSION分档 + 证红(§2.4);〔补遗 b〕实施 = Go 脚本消费 dump 外层slot_strategy(已在)+ Rust 补外层字段(冻结区白名单动作)+ 白名单显式列global_data_end交叉点。gen_capi_bindings(§2.9)。- 删除
src/compiler/ast.rs(§2.2)。 - C++ 语料处置二选一(E-09)——倾向最小回归(冻结语料与
docs/archive/同命运:归档物"不具备参考价值",冻结即失去活性)。 - CI 依赖方向断言(§2.1 第 0 步)。
- 单源清单 + 跨语言单源纳入 L1 判据(C-04);〔补遗 b〕清单须含
parser/decl.mbt:889第三消费点。 归档 tag 推送(F-07)〔更新 b:已推送远端并复核〕。- 〔新增 b〕
@vitro/protocol生成链:Go 从native/src/unified/types.rs抽类型生成 TS 定义 + schema 版本常量,发 npm 包(§8.4)——兑现 §7.5 并补 E-08 结构性一半。 - 〔新增 b〕插件系统不立项(触发线见 §8.5);当下实做 = 让 TS 内容层作为第一个消费者跑通(import 协议包、消费 StepPayload 最小渲染)——按普通 TS 应用建,不建注册/钩子/扩展点。
B 组 — 依赖前置动作
9. Rust 侧按 L0–L9 重切 crate(须在第 2 项之后)。
10. temp_slot 病灶根除(须在第 2 项之后)。
11. 协议冻结(须在第 1 项之后)〔修正 b:v0.1 已冻结且字段冻结测试就位——本项实为"机器可读化 + TS 类型生成",即 A 组第 9 项;消息模式(推送+取消 vs 批量拉)留 S7 真实拓扑时裁,见 §8.4〕。
12. MoonBit 空表调用点加哨兵(§2.6)。
13. *_FAILURES.md 收敛为机器台账(§2.7)。
C 组 — 依赖 S6 判决 14. 机制层语言最终裁定(§3.4)。 15. JIT 按出口分别裁定(§2.10 P-1)。
附录 A 外部事实核查清单
| 事实 | 核查结论 | 用途 |
|---|---|---|
| 微软 Rust Tier-1 | 2026-09-15 官宣;名单为 C++ / C# / TypeScript / Rust 四个;rustc_codegen_utc 已过 100+ 仓库 |
A-02 |
moonc 版本 |
v0.10.13+cbb11c36f (2026-09-15)(一手实测);官方 1.0 原定 2026 上半年 ⇒ 已迟 |
§1.1 / A-03 |
| Neovim 语言构成 | Vim Script 16,853,990 B > Lua 14,229,311 > C 11,517,462(GitHub Languages API)⇒ 核心约占三者的 27% |
v1 §9.9 对照项 |
| Neovim 与 Vitro 体量重心 | Neovim 核心 27% vs Vitro 核心 88%(77,771 / (77,771+10,767) 行)⇒ 能借的是方法,不是体量分布 | E-07 |
wasm_of_ocaml |
同 WasmGC 目标、同浏览器基线;Jane Street 2–8× vs js_of_ocaml;必需 Binaryen ≥119 |
A-08 |
| Lua / Luau → wasm | 无 wasm-gc 源编译路径;一律 C/C++ 经 Emscripten 成线性内存 wasm | A-07 |
| wasm-gc 跨模块类型 | 规范允许(canonicalization / 封闭类型);工具链不支持 | D-02 |
| VS Code 进程模型 | Main / Renderer / Extension Host / Language Server(后两者独立进程) | D-04 / D-06 |
| LSP 能力协商 | initialize 握手交换 capabilities,双方都支持才启用 |
D-07 |
| LSP 两档模式 | Direct Providers(进程内)vs LSP(独立进程) | D-08 |
| LSP 分帧 | Content-Length 头 + JSON body(非 JSON-lines) |
B-06 |
| LSP 动机 | M 语言 × N 编辑器 = M×N → 协议标准化后 M+N | B-05 |
| 本机 OCaml 工具链 | ocaml / opam / dune / wasm_of_ocaml / binaryen 全部 NOT FOUND |
U-3 |
附录 B 档案取回命令
git show archive/moonbit-probe-archive --stat # 探测档案清单(≈8,022 行,15 份 + docs/README)
git show archive/moonbit-probe-archive:"docs/current/07-质量与裁定/MoonBit迁移_typeck模块勘察报告20260918.md"
git show archive/moonbit-probe-consolidation --stat # 浓缩点(16 份 → 2 份)
〔更新 b〕两标签已推送远端并经
git ls-remote复核(archive→917251e/ consolidation→c0b642f)。第二会话曾因本地克隆未 fetch 误判"tag 缺失"——多副本环境下"落盘验证"须在正确的副本上做(本地没有 ≠ 没做,先查远端)。
第 7 部分 最终形态确认与技术栈辨析(2026-09-21 追加)
7.1 最终形态(用户裁决)
| 层 | 内容 | 技术栈 | 分发 |
|---|---|---|---|
| 核心 | 时间旅行 / 单步 / 内存映射结构 / 指针追踪 / VmObserver 协议 |
月兔全栈 | mooncakes(moon add vitro/engine) |
| 内容与呈现 | 知识卡片文本 / 算法识别规则 / 评分策略 / 可视化 | TS(吃 Web 生态,Vite 构建) | npm |
与 §3.2 / §3.3 的一致性:核心仍保有"看程序怎么跑"的全部能力(产出完整 StepPayload 流);教学内容是消费这些数据得到的。⇒ 核心的教学价值不依赖内容层,故该切法自洽。
跨分发体系的唯一约定 = StepPayload schema(见 E 门类)。
7.2 ⚠ D-13 的判据须补正:单一判据被当成万能判据
D-13 原结论:"内容是数据、不执行 ⇒ 不需要隔离 ⇒ 留核心。"
问题:最后一步是过度推论。判据("会不会执行我们控制不了的代码")回答的是「要不要隔离」,不直接回答「归属哪一侧」。"不需要隔离"只意味着"可以放核心",不意味着"必须放核心"。
补正后的两层判据:
| 问题 | 决定性维度 |
|---|---|
| 这一段要不要隔离? | 会不会执行我们控制不了的代码 |
| 这一段放哪一侧? | 迭代效率与生态 |
⇒ 内容不需要隔离,但需要快速迭代 + Web 生态 ⇒ 放 TS 侧是合理选择。用户裁决成立。
性质登记:与 F-04 同类(判据选错 / 过度延伸),且是修正后的判据又一次被过度延伸——说明"单一判据当万能判据"是本会话反复出现的思维捷径,应列入自查项(已写入 repo-architecture-review 技能)。
7.3 技术栈辨析:与 Electron 相似吗
答:不相似。最接近的工业先例是 Figma 模式(wasm 核心 + TS 上层),不是 Electron。
Electron 解决的问题是"把 Web UI 打包成桌面应用"——那是分发形态;本方案解决的是"编译型核心 + JS 上层"的分层——那是分层形态。两者正交。
| 维度 | Electron | 本方案 |
|---|---|---|
| 解决的问题 | 把 Web UI 打包成桌面应用 | 编译型核心 + JS 上层的内容/插件 |
| 语言与产物 | JS/TS 为主 + 原生 addon(N-API) | wasm-gc 核心 + TS 上层 |
| 边界性质 | IPC(main ↔ renderer) | 协议(core ↔ plugin) |
| 核心可移植性 | addon 为平台专用二进制 + N-API ABI | wasm-gc 一份产物,跨所有宿主 |
| 核心崩溃面 | 进程内 C++,崩溃带走整个进程 | 独立执行单元,可终止 |
| 跨平台 | 三平台分别打包 | 一次产物 × 任意宿主 |
| 与本方案的关系 | 它可以是一种宿主(与 Tauri / WebView2 / 纯浏览器并列) | 可运行在上述任一宿主中 |
更贴的工业先例:Figma(渲染与文档模型编成 wasm + TS UI)、Google Earth / Docs Web、AutoCAD Web、Photoshop Web——通称"原生/编译型引擎 + Web 前端"。
两条本方案强于 Electron addon 模式之处:① 一份产物跨所有宿主(含纯浏览器),无平台专用二进制与 ABI 负担;② 核心在独立执行单元,可杀而不带走宿主。
结论:若宿主选 Electron,则"技术栈像 Electron"在宿主选择层面成立;但架构层面本方案是 Figma 模式。两者勿混。
7.4 Vite 用于可视化:方向对,但必须列一张打包验证清单
方向正确,且理由比"好做"更强:Vite 的 dev server + HMR 与"内容层是变更频率最高的一层"属性对齐——这与 §E 门类"抽卡效率与变更频率对齐"是同一条思路的延伸。
但 wasm-gc 与前端构建的接缝有四类典型坑,默认它工作是最典型的踩坑方式(应用 F-03 的教训:工具链能力断言必须先核):
| # | 坑 | 检查方式 |
|---|---|---|
| 1 | instantiateStreaming 要求 Content-Type: application/wasm;MIME 不对则静默降级为非流式 |
查 dev server 与生产 CDN 的响应头 |
| 2 | file:// 下 fetch 不可用 ⇒ 若宿主走 file://(部分 Electron / Tauri 配置),wasm 必须走自定义协议或内联 |
打包后实测,而非 dev 模式 |
| 3 | Worker 内的相对路径基准与主线程不同 ⇒ 打包工具的 base path 在 Worker 里可能解析错 | 在 Worker 里实测加载 |
| 4 | Vite 对 .wasm 有多种集成模式(?init、vite-plugin-wasm、手工 fetch) |
选定一种并写进构建文档 |
⇒ 建议:把"wasm 加载路径与 MIME"列为打包验证清单的固定项,不进"应该没问题"的默认假设。
7.5 一条可补回 E-08 风险的产出:从权威 schema 生成 TS 类型
E-08 登记的风险是:"L9 外置 = 把教学正确性移到防线之外"。其中"结构性"的那一半可以补回来:
用 Go 生成器(见 E-06)从权威
StepPayloadschema 生成 TS 类型定义,发布为 npm 包(如@vitro/protocol,含 TS 类型 + schema 版本常量)。
- 收益:TS 侧的字段级错误在编译期即红(字段名写错 / 字段缺失 / 版本不匹配),而不是运行时才发现。
- 纪律一致性:生成器保持 Go(E-06),且该生成器属构建期多语言(E-07),不进产物 ⇒ 合规。
- 剩余部分:语义层的教学正确性仍须靠插件侧快照测试(E-08 / E-04)。
- ⇒ 该动作把 E-08 的风险从"完全外置"降为"字段级在内、语义级在外"。
7.6 一条必须写进 schema 的设计护栏:核心只出语义数据,不出渲染意图
| 侧 | 应产出 | 不应产出 |
|---|---|---|
| 核心(月兔) | 地址 / 类型 / 生命周期 / region 归属 / 关联关系 | 坐标、SVG、绘制指令、布局 |
| 呈现(TS) | 几何计算、布局、绘制 | — |
为何:若核心返回"给前端用"的坐标或绘制指令,渲染逻辑会被锁进核心,TS 侧失去自由度,且核心的每次视觉调整都要动月兔侧并过一遍 A 级对拍——成本与收益完全倒挂。
判据:核心只出语义数据,TS 自己算几何。 该界线须写入 StepPayload schema 的定义说明,否则会被后人无意跨越。
第 8 部分 第二会话核查批与终局裁决(2026-09-21 追加,标注 b)
本部分是 v2 定稿后同一日的第二轮代码级复核(含同一日工作树增量——codegen 扩展批二号 5 个未提交文件):对本文 8 组断言逐项实证,13 项证实、3 项修正、5 项补遗、1 项归属修正,并落插件架构终局裁决(§8.5)。散点修正已以〔修正 b〕/〔补遗 b〕标注在对应条目。
8.1 复核汇总
| 原断言 | 复核结果 | 证据 |
|---|---|---|
| §1.1/§2.5 对外面 150 符号 | 修正:^pub 口径漏 pub(all)(35)与抽象 type(4),真实口径 200 |
逐包重算:ast 70 / typeck 35 / bytecode 22 / diag 19 / lexer 16 / names 10 / opcode 7 / source 7 / codegen 6 / parser 4 / libc 4 |
| §2.3 单源表两处 MoonBit 委托 | 补遗:第三消费点 parser/decl.mbt:889 |
三表全空 + sz>0 回退 None(防御性回退,非 §2.6 对象) |
§2.4 Rust 侧无 SLOT_STRATEGY_VERSION |
证实(native 零命中) | 补遗:MoonBit dump 外层已带 slot_strategy(cmd/dump_compile/main.mbt:99),codegen_diff 只比内层 14 键 ⇒ 分档实施面更小,但 Rust 补字段属冻结区白名单动作 |
| §2.4 分侧表(code 锚锁 codegen / 映像锚锁 VM) | 证实 + 补遗交叉点 | CompileOutput 13 字段含 global_data_end(R1 布局量,运行层堆起点据此算)——两锚在此交叉 |
| §2.10 P-1 "forbid(unsafe_code) 使 JIT 结构性不可能" | 修正:55ms 真身是模板超级指令 | jit_templates.rs:1-8:函数指针表、非机器码、无 unsafe;与宿主 V8 JIT 正交可叠加;MoonBit 一等函数可表达(见 §8.3) |
| E-04 / B 组 #11 "协议冻结"为待办 | 修正:v0.1 已冻结且防线就位 | docs/spec/STEP_PAYLOAD_SCHEMA_V0_1.md:冻结 2026-09-12、锚 10591ad、字段冻结测试 + 五组签字回放 61/61;2026-09-14 truncated 即一次零影响演进(见 §8.4) |
| §7.1 "算法识别规则"归 TS | 补遗:真身是推断代码非"规则" | vitro_algorithm_steps 2,273 行(七模块)+ infer_semantic_label(collector.rs:78)——D-13 三分法漏"推断算法"第四类(见 §8.6) |
| D-01 插件 = U2 用例 | 修正(归属) | TS 插件是宿主 JS 执行体,不经 wasm 实例化机制;地基是宿主 Worker;结论(无需新机制)不变 |
| §1.1 规模 235 文件 / 60,023 行 | 时点更新:240 文件 / 61,989 行 | 工作树扩展批二号(assign/binary/cast/control_flow/unary 未提交);包清单自述 baseline SAME 56→174/363(文档声明,未亲跑复核,真值以 codegen_diff 实跑为准) |
| F-07 tag "仅本地待推送" | 更新:已推送远端 | git ls-remote 复核通过;本地克隆未 fetch 曾致第二会话误判缺失 |
| 总计划 §4 三接口引用 | 补遗:未区分现状/规划 | VmObserver native 代码零命中(规划态名字);AlgorithmContext 实证存在(algorithm_steps/lib.rs:5) |
8.2 对外面:口径、两账与收缩单位
- 口径:真实对外面 200 个可引用符号(§8.1)。单例:
parser计 1 实为 ≥4(DeclaratorNode/ParseError/ParseResult三个pub(all)全字段暴露——DeclaratorNode是纯内部构件,无任何下游签名需要它)。 - 两账:已发布 5 包(真实口径约 119 符号)是 mooncakes 既成承诺——收窄即破坏性变更,走版本化弃期(19 下载多自测,弃期为零);未发布 6 包(约 81 符号)在零成本窗口。优先级从"先于协议冻结"改为"未发布包先于各自首次发布"。
- 收缩单位 = 管线签名闭包,非包清单:全管线唯一消费者
cmd/dump_compile实测只消费 7 个跨包符号(lexer.tokenize/tokenize_with_vfs/LexResult、parser.parse、typeck.TypeChecker、codegen.compile、bytecode.compile_output_dump_json、ast.json_escape_string)。签名内类型(ProgramNode/Token/CompileOutput等)保持名义可见并抽象化(降pub(all)→默认抽象或priv),其余全priv。逐包独立收会把下游入口收死(如收掉ast.ProgramNode则codegen.compile对外无法调用)。 - 根因与闸门:MoonBit 顶层类型默认对外可见(
struct BytecodeGen无修饰即入.mbti),priv自家代码零使用——面膨胀是默认态。一次性收缩不够,必须同立白名单-check闸(先证红:改一个priv应让校验器红,J9 同款),否则每建一个包自动长全开面。 - 打伤自家的面极小:黑盒测试仅 4 文件(lexer×2 / names / parser);bytecode/typeck/ast/codegen 全白盒
_wbtest不受可见性影响。
8.3 JIT:按出口 × 形态分别裁定(对 P-1 的升级)
jit_templates.rs:1-8 自陈:"模板 JIT——将 JitTrace 编译为预优化的函数指针序列(超级指令);由于 forbid(unsafe_code) 无法动态生成机器码,采用安全 Rust 内的模板策略:常见字节码模式映射到预编译函数,跳过两层 match 分支"。
⇒ ① 55ms/8.8× 不靠机器码——"forbid(unsafe_code) 使 JIT 结构性不可能"的表述不成立(它没有阻止 8.8×);② 模板 JIT(消 dispatch 层数)与宿主 V8 JIT(解释循环机器码化)正交、可叠加——F-3"宿主自带 JIT 边际缩水"只覆盖机器码形态;③ MoonBit 一等函数/闭包完全可表达模板 JIT——"不可搬"实为"未搬"。
⇒ 门 1 增加第三基线:wasm-gc + 模板 JIT(现有 512/55/156/81/156 五数无此组合);P-1 从"按出口分别裁定"升级为"按出口 × JIT 形态分别裁定"。
8.4 协议层现状重估
StepPayload v0.1 已冻结(2026-09-12,锚 10591ad):字段冻结测试、出口形状一致性、serve 冒烟就位;五组签字回放 61/61 = E-04 所要"冻结快照"的现存雏形资产;"只增不改"已在真实运转(2026-09-14 ArraySnapshot.truncated 新增为一次零影响演进)。
真缺的只有机器可读形态(schema 是 md 表格)。⇒ §7.5 的 Go→TS 生成器输入不应是 md(解析文档做生成器是反模式),应从 native/src/unified/types.rs 类型定义抽取——即 gen_host_route 读 host_func_id.rs 的既有模式,零新方法论。
另(D-04 × D-05 耦合,见 D-05 条目):现状消费者入口 vitro_step_next_json / vitro_get_step_payloads_json 是同步拉模式 FFI 直调;在分居拓扑下逐步拉 = 每步一次 IPC 往返。消息模式(推送+取消 vs 批量拉)留 S7 有真实拓扑时裁,现在没有拓扑就没有答案。
8.5 插件架构终局裁决:不建框架,养第一个消费者
- 判定:插件架构作为"现在要建的系统"不可取——每一个真实用例均已被终局分工消解(知识卡片/可视化 = TS 层本职;评分 = 参数化数据;学生代码 = 引擎的输入而非插件;唯一幸存用例"代码级判分器"零需求证据,mooncakes 19 下载多为自测)。作为"协议开放性的自然衍生"则已免费拥有——serve NDJSON + 公开冻结 schema 下,任何进程外客户端今天即可接入(即 B-05 所引 LSP M×N→M+N 的本义:协议标准化后,"第三方扩展"不需要被设计,它就是"另一个客户端")。
- 自家 TS 内容层不是插件,是第一方消费者:按普通 TS 应用建(
import协议包、订阅 payload 流、渲染状态),不建注册/钩子/扩展点/插槽——插件心智会滑回预建框架的老路;消费者心智直接写功能。 - 唯一值得提前投资的难改物(schema)已冻结;拓扑/协商/两档全是宿主侧 TS 代码,重构零 API 破坏成本——难改的已投完,好改的不预设计。
- D 门类最小保留集(其余随终局归档):
- ① 进程外客户端 = 事实插件(serve 协议承载,已存在);
- ② D-03 硬约束:不可信执行必须活在可终止的执行单元内——对学生代码同样成立,永不过时;
- ③ 触发线:第一个仓库外"参数化表达不了、必须写代码"的需求出现才立项,届时从 serve 协议起步服务那一个真实用户。
- 触发后的三条设计约束(现在不实施):轻插件必须是"经受限 provider 钩子注册的回调"而非任意 import(两档声明制缺强制点);能力协商须支持热插拔增量更新(LSP
initialize是静态的,不可照抄);payload 归属(instance_id+ 单调序号 + 过期即弃,D-10)随 serve 多客户端自然成立。
- 当下实做:A 组第 9 项(
@vitro/protocol生成链)+ 第 10 项(TS 内容层最小消费渲染)。第一个消费者跑通之日,插件架构的所有开放问题都有了实证答案;跑不通,任何预建框架都是空中楼阁。
8.6 其它补遗
- D-13 第四类"推断算法"(详见 D-13 条目补记):
semantic_label启发式 +algorithm_steps2,273 行推断代码——M6 零防线对象,§7.1 归派掩盖重写成本。建议推断留核心(月兔随 S8/S9 重写),呈现归 TS。 - A 级锚交叉点:分档白名单须显式列
global_data_end(动堆起点布局会同时打红 codegen 锚与 VM 映像锚)。 - 接口面引用纪律:引用三接口须区分现状/规划——
VmObserver(规划态)vsAlgorithmContext(实证存在)。 - 方法论沉淀(F-08 扩条):声称任何"待建/不可能"之前,先盘点自家已建基础设施——审阅对象里最便宜、最容易被重复发明的,永远是已经造好的那部分。