docs/current/07-质量与裁定/Vitro架构审阅报告v2.md
GitHub ↗
当前有效

Vitro 架构审阅报告 v2(2026-09-21)

24214 字·约 61 分钟 阅读 2026-10-10 17:07

版本说明: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 集成版)。

最紧要的三条待办(按"不依赖任何未决策"排序):

  1. 收窄对外面——实测 11 个包暴露 150 个公共符号,而总计划 §4 的硬约束是"只暴露三面"。该约束从未落地、无 CI 校验。必须先于协议冻结,否则冻结的是 150 个符号。
  2. SLOT_STRATEGY_VERSION 分档——A 级 code 段锚锁死 codegen,是 temp_slot 病灶(3 起同类 bug)至今未根除的真实原因。
  3. 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 映像锚。分档白名单必须显式列出该交叉点。

建议:

  1. Rust 侧补上 SLOT_STRATEGY_VERSION(MoonBit 侧已有 v1 常量),A 级判定改三态:同版本 → 逐位必等;跨版本 → 按显式形状白名单判语义等价;白名单外 → 红。 〔补遗 b〕实施面比原估更小,但多一道流程门:MoonBit 侧 dump 外层已带 slot_strategy(cmd/dump_compile/main.mbt:99),codegen_diff 只比内层 14 键 ⇒ 分档 = Go 脚本消费外层键 + Rust dump-compile 补外层字段——后者是动冻结区(native 白名单 P1–P7/U1/U2),需走白名单裁定,非"无依赖现在就能做"(见 §8.1)。
  2. 必须带证红:注入一条形状变更,证明白名单能区分"预期"与"非预期"。一个从未红过的检查,与一个坏掉的检查,在观测上无法区分。
  3. 沿用既有"白名单为空时不得绿"纪律。
  4. 顺序:分档机制 → 再动 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)。

为何必须现在管(三条叠加):

  1. ast(44) + typeck(31) 占一半,而这两个恰是变更最频繁的包(Type 17 / Expr 26 / Stmt 16 三族持续增长)。
  2. §12「制度随迁」刚把承诺载体改为"包版本 + .mbti" ⇒ 承诺的维护成本正比于面的大小,现状等于承诺 150 个符号。
  3. 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:

  1. 先抽元数据:vitro_capi.h 从"手写声明"改为机器可读 capi_manifest.json(函数名 / 参数 / 返回 / 所有权语义 / 线程模型 / ABI 版本),头文件降为生成物。

    为何"所有权语义"必须进元数据:tests/capi_string_ownership_contract_test.rs 的存在说明这类契约最易在抽卡时丢失。

  2. 生成 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 let link-core ICE,入风险登记册 A8)、门 0(LLM 效率)仅判"弱通过"、moonbit/AGENTS.md 29 条工具链陷阱。
  • 不成立:"出口面小"在语言层只成立一半——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 管理内存(↔ 无 owned String / 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_cpp 99 / E2E 83 / CI 三 tier 随砍退役归档)。引入 C++ 会同时带回模板 / RAII 负担与"重建一套防线"的成本。
  • 修正:回退项只有 Rust;且机制层恰是回退成本最低的一层(vitro_vm 8,863 行 + vitro_runtime 2,043 行 + src/unified/ 已存在且全绿)。
  • 采纳:✅("Rust/C++" 收缩为 "Rust")。产出:§3.4。

A-07|Lua 是否是好选择(提出:U)

  • 裁决:✗ 不采纳(作为引擎语言)。三条理由〔核实〕:
    1. 无 Lua → wasm-gc 的源编译路径——Lua/Luau 进浏览器一律是"把 C/C++ 实现经 Emscripten / WASI SDK 编成线性内存 wasm + JS 胶水"(Luau 官方 Playground 即 C++ 经 Emscripten 出 luau.wasm)⇒ 破坏"同 wasm-gc 栈"前提;
    2. 无静态类型 ⇒ 编译期穷尽纪律为零(与 C 同一失败模式);
    3. 性能不达标(PUC-Lua 是解释器;Luau 的 JIT 仅 x64 / arm64,wasm 上无 JIT)。
  • 但两处设计值得借:① 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 直映 wasm i32 / 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)

  • 裁决:✅ 采纳全栈收敛。两条判据,后者独立于性能测试即成立:
    1. (条件性)若稳定侧未通过性能前置测试,则确为双输;
    2. (决定性)集成形态:全栈的直接产物是消费者 moon add vitro/engine 一条命令完成集成——且已部分存在(source 0.1.1 / lexer 0.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 次)、Node worker_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,职责四项:
    1. spawn 并管理 server 进程生命周期;
    2. document selector 配置(哪些文档交给哪个 server);
    3. 文件系统事件同步;
    4. 协议消息路由。
  • 照四项写,不得超出——尤其不持有业务状态(业务状态归引擎)。
  • 采纳:✅。

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_steps 2,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 的四条真实优势:
    1. 在浏览器里消除一条边界——宿主本身就是 JS,JS 插件住在 payload 到达处(零搬运层);而月兔写的 L9 在浏览器里会变成第三个 wasm-gc 模块,凭空多一条跨模块边界。
    2. 零新增工具链——Node 宿主已在 F-4 裁定中。
    3. 抽卡效率与变更频率对齐——L9 变更频率最高,TS/JS 语料最丰富。
    4. 生态契合工作内容(数据变换 / 可视化 / 知识图谱 / 评分;项目历史上已用过 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-14 ArraySnapshot.truncated 新增为一次零影响演进)。真缺的只有机器可读形态——见 §8.4。

E-05|探针面三条判据(提出:A,据 U 的抽卡模型)

  • 裁决:✅ 采纳为架构验收判据(替代原"能否重复实现")。
  • (U)人机协作模型是抽卡式:人发任务 → AI 依训练分布生成 → 人靠探针与审阅找缺陷(而非逐行审代码);项目明确声明"眼见不一定为实"(假绿 / 假红并存,见审计 M1–M9)。
  • 在该模型下,两份 AI 生成的实现是同分布的,独立性本就微弱 ⇒ 判据改为三条:
    1. 探针面够不够多(每个新出口 / 消费者 / 锚点 = 一条独立探针路径);
    2. 够不够便宜(生成物 > 手写物;结构化台账 > 自由文本);
    3. 够不够持久(冻结协议使旧探针长期有效)。
  • 采纳:✅。

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_cpp 99 / 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_t id;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 组 — 不依赖任何未决策,现在可做

  1. 收窄对外面〔修正 b〕:未发布 6 包先于各自首次发布收面(零成本窗口,parser 已排队"随下一 minor"——正在关);已发布 5 包走版本化弃期(19 下载多自测,弃期为零);收缩单位 = 管线签名闭包(§8.2);先立白名单 -check 闸并证红,再加 priv(MoonBit 默认公开,面会自动再长)。顺带剔除 parser::parse 的 cpp_mode? : Bool 参数(F-2 终局,parser 未发布是最后的免费时机)。
  2. SLOT_STRATEGY_VERSION 分档 + 证红(§2.4);〔补遗 b〕实施 = Go 脚本消费 dump 外层 slot_strategy(已在)+ Rust 补外层字段(冻结区白名单动作)+ 白名单显式列 global_data_end 交叉点。
  3. gen_capi_bindings(§2.9)。
  4. 删除 src/compiler/ast.rs(§2.2)。
  5. C++ 语料处置二选一(E-09)——倾向最小回归(冻结语料与 docs/archive/ 同命运:归档物"不具备参考价值",冻结即失去活性)。
  6. CI 依赖方向断言(§2.1 第 0 步)。
  7. 单源清单 + 跨语言单源纳入 L1 判据(C-04);〔补遗 b〕清单须含 parser/decl.mbt:889 第三消费点。
  8. 归档 tag 推送(F-07)〔更新 b:已推送远端并复核〕。
  9. 〔新增 b〕@vitro/protocol 生成链:Go 从 native/src/unified/types.rs 抽类型生成 TS 定义 + schema 版本常量,发 npm 包(§8.4)——兑现 §7.5 并补 E-08 结构性一半。
  10. 〔新增 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)从权威 StepPayload schema 生成 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 对外面:口径、两账与收缩单位

  1. 口径:真实对外面 200 个可引用符号(§8.1)。单例:parser 计 1 实为 ≥4(DeclaratorNode/ParseError/ParseResult 三个 pub(all) 全字段暴露——DeclaratorNode 是纯内部构件,无任何下游签名需要它)。
  2. 两账:已发布 5 包(真实口径约 119 符号)是 mooncakes 既成承诺——收窄即破坏性变更,走版本化弃期(19 下载多自测,弃期为零);未发布 6 包(约 81 符号)在零成本窗口。优先级从"先于协议冻结"改为"未发布包先于各自首次发布"。
  3. 收缩单位 = 管线签名闭包,非包清单:全管线唯一消费者 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 对外无法调用)。
  4. 根因与闸门:MoonBit 顶层类型默认对外可见(struct BytecodeGen 无修饰即入 .mbti),priv 自家代码零使用——面膨胀是默认态。一次性收缩不够,必须同立白名单 -check 闸(先证红:改一个 priv 应让校验器红,J9 同款),否则每建一个包自动长全开面。
  5. 打伤自家的面极小:黑盒测试仅 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 插件架构终局裁决:不建框架,养第一个消费者

  1. 判定:插件架构作为"现在要建的系统"不可取——每一个真实用例均已被终局分工消解(知识卡片/可视化 = TS 层本职;评分 = 参数化数据;学生代码 = 引擎的输入而非插件;唯一幸存用例"代码级判分器"零需求证据,mooncakes 19 下载多为自测)。作为"协议开放性的自然衍生"则已免费拥有——serve NDJSON + 公开冻结 schema 下,任何进程外客户端今天即可接入(即 B-05 所引 LSP M×N→M+N 的本义:协议标准化后,"第三方扩展"不需要被设计,它就是"另一个客户端")。
  2. 自家 TS 内容层不是插件,是第一方消费者:按普通 TS 应用建(import 协议包、订阅 payload 流、渲染状态),不建注册/钩子/扩展点/插槽——插件心智会滑回预建框架的老路;消费者心智直接写功能。
  3. 唯一值得提前投资的难改物(schema)已冻结;拓扑/协商/两档全是宿主侧 TS 代码,重构零 API 破坏成本——难改的已投完,好改的不预设计。
  4. D 门类最小保留集(其余随终局归档):
    • ① 进程外客户端 = 事实插件(serve 协议承载,已存在);
    • ② D-03 硬约束:不可信执行必须活在可终止的执行单元内——对学生代码同样成立,永不过时;
    • ③ 触发线:第一个仓库外"参数化表达不了、必须写代码"的需求出现才立项,届时从 serve 协议起步服务那一个真实用户。
    • 触发后的三条设计约束(现在不实施):轻插件必须是"经受限 provider 钩子注册的回调"而非任意 import(两档声明制缺强制点);能力协商须支持热插拔增量更新(LSP initialize 是静态的,不可照抄);payload 归属(instance_id + 单调序号 + 过期即弃,D-10)随 serve 多客户端自然成立。
  5. 当下实做:A 组第 9 项(@vitro/protocol 生成链)+ 第 10 项(TS 内容层最小消费渲染)。第一个消费者跑通之日,插件架构的所有开放问题都有了实证答案;跑不通,任何预建框架都是空中楼阁。

8.6 其它补遗

  • D-13 第四类"推断算法"(详见 D-13 条目补记):semantic_label 启发式 + algorithm_steps 2,273 行推断代码——M6 零防线对象,§7.1 归派掩盖重写成本。建议推断留核心(月兔随 S8/S9 重写),呈现归 TS。
  • A 级锚交叉点:分档白名单须显式列 global_data_end(动堆起点布局会同时打红 codegen 锚与 VM 映像锚)。
  • 接口面引用纪律:引用三接口须区分现状/规划——VmObserver(规划态)vs AlgorithmContext(实证存在)。
  • 方法论沉淀(F-08 扩条):声称任何"待建/不可能"之前,先盘点自家已建基础设施——审阅对象里最便宜、最容易被重复发明的,永远是已经造好的那部分。