docs/current/05-教学体验/模板维护指南.md
GitHub ↗
当前有效

Vitro 模板系统规范与提示规划

4005 字·约 11 分钟 阅读 2026-10-10 17:07

本文档面向模板维护者,定义模板目录结构、占位符语法、生成用例防线及未来改进方向。模板的展示形态(模板栏、参数对话框、教程面板、可视化组件)已随 2026-09-11 前端切割移交社区前端,本文档只描述本仓库(纯后端)可维护的部分。

最后核对日期:2026-10-07(删区批现役化——§4 生成链路改「存量产物态」〔sync_templates.py 已随删区退役、82 生成用例迁 corpus/template_generated 不可自动重生成〕、§5.2 出口表换统一入口现役版、§5.3 影子验证/Rust E2E 改退役注记 + clang_direct 承接;anchor 现状与 §6 历史记录不变。前一沿革 2026-09-29 anchor 现状与 S8 契约节新增)

历史修订:2026-09-11(移除 CideFlutter/、index.json、Dart 测试相关引用)

1. 目录结构

每个模板位于 templates/<key>/ 目录下:

templates/
  <key>/
    meta.yaml     # 模板元数据
    source.c      # C 模板源码(二选一)
    source.cpp    # C++ 模板源码(二选一)

source.c 与 source.cpp 只能存在一个。scripts/sync_templates.py(已退役)(R4 G1 恢复版)会扫描模板目录并按扩展名渲染;历史版本的 index.json 产物已随 2026-09-11 前端切割移除、不再生成。维护者仍应人工遵守该约定(见 §4)。

实测(2026-09-11):templates/ 下 88 个模板目录 = 82 个 source.c + 6 个 source.cpp;native/tests/cases_template_generated/ 下 83 个文件 = 82 个 .c + 1 个 E2E_FAILURES.md。

2. meta.yaml 字段

key: bubble                    # 目录名,必须唯一
name: 冒泡排序                  # 展示名称
category: 排序                  # 分类,用于分组展示

params:                        # 参数定义(可选)
  n:
    label: 数组长度
    type: int                  # int | string | identifier
    default: 5                 # 默认值,用于 shadow 用例生成

tutorial:                      # 教程步骤(可选)
  steps:
    - title: 外层循环
      description: 控制排序轮数
      anchor: outer_loop       # 对应源码中的 // @tutorial-anchor: outer_loop

knowledge_nodes:               # 关联知识图谱节点(可选)
  - Algorithm
  - Sorting

anchor 现状与 S8 契约(2026-09-29 拍板,as_of 当日实测)

  • 现状(诚实披露):88 个 meta.yaml 的 95 个 anchor 值全部为中文描述(如 「红黑树性质」),源码中 @tutorial-anchor 标记出现 0 次——按上表 schema 语义全量失配;params 有内容的文件数 0/88。此前 sync_templates.py 的 Dart 硬编码教程读取(前端切割后恒空)已于 2026-09-29 作为死代码删除。
  • S8 契约拍板(用户裁定:先闸后铺):教学步骤生成器契约定型时,先落 最小 schema 校验闸(anchor 匹配数纳入机判,禁静默 default——当前全失配 事实须显式红/白名单化),再按生成器实际需要的锚点粒度半自动铺 @tutorial-anchor 标记;不在契约定型前预铺(避免锚点粒度返工)。

3. 占位符语法

模板参数在源码中以合法 C 注释形式出现,便于默认状态下直接被 Clang/Vitro 编译:

int arr[/*__PARAM_n__*/ 5] = {1, 2, 3, 4, 5};
int n = /*__PARAM_n__*/ 5;
  • /*__PARAM_<key>__*/ 为占位符标记。
  • 占位符后的第一个非空标记为默认值。
  • 当前支持的默认值字符集:[^\s\[\]();,]+,可覆盖数组大小、变量初始化、函数参数等场景。

4. 生成用例链路(2026-10-07 删区后现状)

4.1 链路现状(自动生成段已断——存量产物态)

templates/<key>/source.c                     模板源(合法 C,人类维护)
        ↓  ✂ sync_templates.py 已随 2026-10-05 删区退役(R4 G1 恢复版的生成链路终止;
        ↓     原 native/tests/{cases_template_generated,cases_golden}/ 已物理删除)
corpus/template_generated/<key>_default.c    82 个 .c = **存量产物**(随删区迁入 corpus/,
                                             生成器删除后不可再自动重生成——改模板须人工同步或恢复生成器,
                                             与 #40〔templates meta.yaml 孤儿处置〕联动)
        ↓
└─ clang_direct 直拍门禁(现役):corpus/template_generated 在其六目录语料域内
     (scripts/clang_direct/main.go),Clang 实跑产 golden 逐例对照;
     go run ./scripts/clang_direct

4.2 生成器终局:sync_templates.py 已随删区退役

  • 2026-09-12 ~ 2026-10-05:R4 G1 曾自历史提交恢复 sync_templates.py 为纯后端形态(扫描 templates/ → 渲染占位符 → Clang 产 golden);2026-10-05 S9 工序④随 Python 残留清理整体删除(对象 = Rust 区配套防线,防线本体已由 clang_direct 吸收)。生成能力如需恢复属新立项(与 #40 三选一处置联动:删 / 并入课程体系 / 封存登记)。
  • 原第 4/5 步(CideFlutter/assets/templates/index.json、Flutter assets 拷贝)已随前端切割(2026-09-11)永久移除。
  • 静态一致性校验脚本 scripts/test_templates.py 自前端切割起即不在仓库中(历史资产,已迁出——反例语境,非失效指引),模板目录完整性核对仍依赖人工(见 §5.1)。

4.3 人工核对流程(test_templates.py 恢复前的临时约定)

  1. 修改 templates/<key>/source.c(或 source.cpp)与 meta.yaml。
  2. 直接用 Clang 编译运行模板源确认合法、输出稳定(模板本身即合法 C/C++,这是本设计的关键红利)。
  3. 人工同步 corpus/template_generated/<key>_default.c(生成器已删,不可自动重生成):用默认参数替换 /*__PARAM_x__*/ 占位符,并在首行补 // @category: ... 标记。
  4. 完整义务链(clang golden → e2e → 直拍 → facts)见 Agent Skill vitro-baseline-corpus-workflow;模板生成用例的 golden 由 clang_direct 运行期实产(不入库)。
  5. 跑 go run ./scripts/clang_direct 确认无新增非预期差异(原 Shadow/Rust E2E 双防线已随删区退役,见 §5.3)。

5. 测试防线

5.1 静态一致性核对(当前无自动化脚本)

原命令 python scripts/test_templates.py 已失效(脚本已随前端切割删除,不在仓库中,且未随 R4 G1 恢复;历史资产,已迁出)。恢复前只能人工核对下列条目:

  • 每个模板目录包含 meta.yaml 和 source.c/source.cpp 之一。(2026-09-11 实测:88 个目录、88 个 meta.yaml,此项无缺)
  • meta.yaml 中 key/name/category 存在且 key 与目录名一致。
  • meta.yaml 中声明的 params 与源码中的占位符一一对应(⚠️ 2026-09-11 实测:现存 88 个模板的 params: 全部为空,而占位符仍散落在 source.c 中——即这条校验在当前数据上已失去约束力,须逐个人工比对)。
  • 每个 param 都有 default 值。
  • native/tests/cases_template_generated/<key>_default.c 与模板源保持同步(改模板后运行 python scripts/sync_templates.py(已退役) 自动重新生成;一致性人工抽查)。
  • native/tests/cases_golden/<key>_default.out 与生成用例一一对应。

缺口记录:以上核对在切割前由 sync_templates.py + test_templates.py 自动保证;前者已由 R4 G1 恢复(2026-09-12),后者仍缺、依赖人工纪律。这是已知缺口,不是"已完成"状态。

5.2 出口冒烟(替代原 Dart 单元/Widget 测试)

原 Dart 测试(flutter test test/models/template_loader_test.dart、test/widgets/template_bar_test.dart、test/widgets/ide_template_bar_test.dart、test/providers/ide_notifier_test.dart,历史资产,已迁出)已随前端切割整体移出仓库(CideFlutter/ 见标签 before-frontend-split)。模板相关行为现在只能通过三个后端出口验证:

出口 验证方式(2026-10-07 现役)
统一入口 CLI scripts/bin/vitro run corpus/template_generated/<key>_default.c(或 step / api;native/wasm 双臂同形)
wasm-gc demo 单页(demo/)现场消费 gateway wasm.wasm;CI demo_smoke 冒烟
serve JSON-lines 会话帧冒烟:go run ./scripts/serve_smoke(原 vitro_cli serve 已随删区退役)

原 Dart 测试覆盖的语义点(模板加载、ext 字段、占位符替换、教程模式下切 main.c/main.cpp)现在属于社区前端职责,本仓库不再提供对应测试。

5.3 影子验证与 Rust E2E(均已随 2026-10-05 删区退役)

模板生成用例的对拍防线由 clang_direct 直拍门禁整体吸收(删区批语料域差量 0——见《Clang直拍门禁》):

  • 现役驱动:go run ./scripts/clang_direct(corpus 六目录全量,含 corpus/template_generated/;Golden 来自 Clang 实时输出〔不能来自 Vitro 自己〕的设计原则不变;known 白名单 scripts/clang_direct/known_direct.json digest 锁定)
  • 原驱动(历史口径,tag rust-oracle-freeze):scripts/shadow_verify(Go)与 native/tests/vitro_e2e.rs(KNOWN_TEMPLATE_FAILURES 双向登记机制随之终结;corpus/template_generated/E2E_FAILURES.md 台账存量保留)

6. 历史修复记录(6.1~6.3 已归档,6.4 仍有效)

归档说明(2026-09-11):§6.1~§6.3 记录的是已被移出仓库的前端代码(CideFlutter/ 下的 TemplateLoader、IdeState、ide_screen.dart、ExecutionControlPanel)的缺陷与修复过程,作为历史资产保留在本文档,不再具备可执行性;相关代码见标签 before-frontend-split。§6.4 记录的是模板源自身的缺陷,与前端无关,仍然有效。

6.1 C++ 模板无法加载(历史归档 · 前端已迁出)

现象:CideFlutter/assets/templates/ 中存在 cpp_*.cpp,但 TemplateLoader.load() 只尝试加载 .c,导致 C++ 模板被静默跳过。

修复:

  • TemplateLoader 读取 index.json 的 ext 字段,加载 .c 或 .cpp。
  • CodeTemplate 增加 ext 字段。
  • completeTutorial 根据 ext 将当前文件切换为 main.c 或 main.cpp,确保 Rust 后端按正确语言模式编译。

6.2 教程无法退出 / 运行状态混乱(历史归档 · 前端已迁出)

现象:点击模板进入教程后,点击“跳过”或“运行代码”,底部教程面板理论上应消失,但顶部的 ExecutionControlPanel 仍显示上一段运行的进度条/覆盖率,造成“还在模板里”的错觉。

根因:

  • IdeState.copyWith 无法通过 activeTutorial: null 清除教程状态(被 ?? this.activeTutorial 覆盖)。
  • 教程模式下未隐藏 ExecutionControlPanel,上一段运行的残留状态会与新界面叠加。

修复:

  • IdeState.copyWith 增加 clearActiveTutorial 标志。
  • completeTutorial 使用 clearActiveTutorial: true 正确退出教程。
  • ide_screen.dart 在 activeTutorial != null 时隐藏 ExecutionControlPanel。(上述 Dart 文件均为历史资产,已迁出)

6.3 覆盖率显示超过 100%(历史归档 · 后端修复部分仍有效)

本条前半段的前端绕过逻辑(ExecutionControlPanel)已随前端迁出;后半段的后端修复(SourceLoc.file_id 过滤 heatmap 行号)仍在仓库中生效,是本条最有价值的部分。 现象:执行控制面板显示“覆盖率 633.3%”。

根因:VM 执行热力图会记录标准库/预编译字节码的行号,导致 lineCounts 中出现远超当前源码行号的条目。

修复:

  • 给 vitro_shared::SourceLoc 增加 file_id: i32 字段(0 表示用户主文件,非 0 表示外部文件/标准库),旧产物通过 #[serde(default)] 保持兼容。
  • Bytecode Libc 加载器 (vitro_vm::bytecode_libc_loader) 在加载预编译产物后,将所有 libc 指令的 loc.file_id 置为 1。
  • VM 执行器 (vitro_vm::core::executor) 记录 heatmap 时只统计 file_id == 0 且 line > 0 的指令,从源头避免外部行号混入。
  • ExecutionControlPanel._buildCoverageText 移除前端按 totalLines 过滤的绕过逻辑,heatmap 数据本身已只包含用户源码行号。

诚实记录:SourceLoc 字面量构造点众多(Parser/CodeGen/测试等),本次通过批量脚本补全 file_id: 0 完成迁移;后续新增构造应优先使用 SourceLoc::new(line, column) 或 SourceLoc::with_file(line, column, file_id)。

6.4 模板源码自身缺陷

模板 问题 修复
factorial meta.yaml 声明 n 参数,但 source.c 未使用占位符 在 factorial() 调用处加入 /*__PARAM_n__*/ 5
fib 同上 在 fibonacci() 调用处加入 /*__PARAM_n__*/ 7
merge merge() 函数定义在 mergeSort() 之后,Clang 报隐式声明 调整函数顺序
threadedBinaryTree 线索化遍历缺少终止条件,导致无限循环 引入标准头节点法遍历

7. 未来改进规划

7.1 模板提示增强(P1 · 已移交社区前端)

以下三项均属社区前端职责,本仓库不再实现;后端只保证模板目录结构、meta.yaml 与占位符语法稳定可用。

  • 参数输入提示:在 TemplateParam 中增加 hint 字段,参数对话框展示填写示例与取值范围。
  • 模板选择提示:模板栏(TemplateBar)支持长按/悬停显示模板简短说明。
  • 搜索与分类:模板数量增加后,增加分类下拉与关键字搜索。

7.2 占位符能力扩展(P2)

  • 支持多行默认值(如初始化列表 /*__PARAM_arr__*/ {1,2,3})。
  • 支持同一参数在源码中出现多次(当前已支持,正则会全部替换)。
  • 支持条件占位符,如 /*__PARAM_lang__==cpp*/ ... /*__PARAM_END__*/。

7.3 教程系统升级(P2 · 已移交社区前端)

教程渲染(步骤高亮、变量提示、面板切换)属社区前端职责。本仓库只负责在协议载荷中提供语义标注字段(见 docs/spec/STEP_PAYLOAD_SCHEMA_V0_1.md 的 semantic_label / algorithm_step)。

  • meta.yaml 支持 explanations 完整写入,不再依赖前端硬编码。
  • 教程步骤支持高亮多行与变量提示。
  • 教程结束时自动展示运行输出,避免学生看不到运行结果。

7.4 后端热图过滤(P3 · ✅ 已完成后端部分)

  • 后端已完成:vitro_shared::SourceLoc 的 file_id 字段 + Bytecode Libc 加载器把 libc 指令标记为外部文件 + VM 记录 heatmap 时只统计 file_id == 0 && line > 0(见 §6.3)。
  • 协议侧口径:docs/spec/STEP_PAYLOAD_SCHEMA_V0_1.md 的 heatmap_line / heatmap_count 已是纯用户源码行号,消费方不需要再自行过滤。

8. 相关命令速查

# 防线 1:Shadow Verification(含模板生成用例,Golden 来自 Clang)
go run ./scripts/shadow_verify

# Rust E2E:模板用例对 cases_golden/*.out 断言(工作目录 native/)
cd native && cargo test --test vitro_e2e

# 单条模板用例人工冒烟(三出口之一:CLI)
cd native && cargo run --release --bin vitro_cli -- run tests/cases_template_generated/<key>_default.c

# serve 出口冒烟
go run ./scripts/serve_smoke

# ⚠️ 以下历史命令已失效(脚本随前端切割删除,历史资产,已迁出):
#   python scripts/test_templates.py     # 校验模板静态一致性(R4 G1 未恢复,仍缺)
#   cd CideFlutter && flutter test ...   # Dart 单元/Widget 测试

模板维护流程:改模板后运行 python scripts/sync_templates.py(已退役) 重新生成用例与 Golden(R4 G1 恢复版);静态一致性人工核对见 §5.1。