docs/current/06-出口与协议/下游需求处置回执.md
GitHub ↗
当前有效

下游需求清单处置与窗口表态

3527 字·约 9 分钟 阅读 2026-10-10 17:07

对象:SharpTutor docs/Vitro后端-C#扩展期需求清单.md(2026-09-12,对端锚定 10591ad; 对端仓库本地镜像 D:\code\SharpTutor,非本仓库内容,故不提供仓库内链接) 处置日期:2026-09-12 | 处置基线:本仓库 master 后续裁定注记(2026-09-27):本文 §3(C1 第二批 capi 窗口)与 §8 遗留 1 的"capi 第二批落地时" 表态已被 U2 拍板(2026-09-19)取代——第二批 capi 裁不做、19 声明冻结现状、对应能力以 serve 协议为终态载体(见 wasm多实例并发模型与U2拍板 §3);正文按当日回执留痕不改写。 关联文档:schema v0.1、 C# 扩展计划、CLI 手册、变更记录

本文档是对端需求清单的逐项回执:每项给出处置状态、落地载体与可复现证据。 第一批(A1/A2/B1/D1)的处置细节落在 CHANGELOG.md,本文档只引用不重复; 本批新增处置 B2 / C1 / C2 / D2 / D3。


0. 处置总表

项 内容 状态 落地载体
A1 EOF 终结语义 ✅ 已修复(第一批) crates/vitro_vm/src/host/io.rs;回归 baseline/scanf_eof_loop.c、scanf_eof_after_exhaust.c
A2 交互式输入喂入 ✅ 已实现(第一批) serve input.feed(语义单源 session_api::input_feed)
A3 C# 子集规范 + 顶层语句入口合成 ⏳ CS0/CS1 交付面 CSharp前端引入计划.md §11(入口合成为 CS1 硬需求)
B1 C# 码段 + error_catalog 导出 ✅ 已落地(第一批) 码段 E5xxx;vitro_get_error_catalog_json + serve error_catalog
B2 schema v0.2 激活流程 ✅ 本次落地 schema §9 + unified/contracts.rs + 冻结测试(见 §1)
B3 C# 已知差异清单 ⏳ CS1 前由我方出草稿 对标 native/tests/*_FAILURES.md 格式
B4 排期确认(透视 preview / G9) ✅ 本次表态(§2) —
C1 第二批 capi 窗口 ✅ 本次表态(§3) —
C2 memory.regions 三段式标注 ✅ 本次落地(serve 过渡形态) session_api::memory_regions + 测试 memory_map_segments_test(§4)
D1 成员函数类型重载 trap ✅ 已修复(第一批) mangled 名带参数类型编码 + E4026
D2 多会话语义澄清 ✅ 本次落地 serve session.create/reset/destroy 响应带 session 拓扑字段(§5)
D3 target_name 跨帧解析 ✅ 本次落地 VitroVM::find_variable_name_at_addr + collector 回退链(§6)

1. B2:schema v0.2 激活轨道(预留位 → 激活)

请求:① 预留位激活 = v0.2 只增事件且必须重跑 C1–C3+S1–S5;② UNWINDING 不得合并单步 写进字段冻结测试;③ semantic_label 受控词汇表落 schema 附录;④ code_file 与 ApiFrameInfo.return_line 排入 v0.2。

处置:四项全部落地,且不是文档承诺而是机器防线。

  1. v0.1 正式冻结:schema 头部状态改为"v0.1 已冻结(2026-09-12,S1–S5 签字回放 61/61)", 冻结锚点 10591ad。
  2. 激活轨道(schema §9,机器可读单源 native/src/unified/contracts.rs):
    • 预留位字段名集合 RESERVED_FIELDS_V0_2(四字段)冻结为常量;
    • V0_2_ACTIVATION_CHECKLIST(五条:只增事件 / 更新 §7 校验表 / 重跑 C1–C3 / 重跑 S1–S5 + S4 激活契约 / 解除预留位断言并知会下游)落代码;
    • tripwire:test_v0_1_reserved_fields_absent 扫描嵌套全量键集合,一旦出现任一 预留位字段即失败,并把清单原样打印——"悄悄激活"在结构上不可能;
    • 出口:serve contracts 方法 + capabilities.schema(消费方可直读轨道与台账)。
  3. UNWINDING 不得合并单步有可执行判据:contracts::check_unwinding_granularity—— 相邻展开步的 unwind_frames_left 下降幅度 ≤ 1(一次弹多帧 = 合并单步); 允许为 0(finally 步不弹帧,S4 §5 A5);回增为违规;进入展开须 ≥ 1、离开必须归零。 合成序列单测 + 集成断言双覆盖,CS3b 回放驱动将直接复用该函数。
  4. semantic_label 受控词汇表(schema 附录 B,单源 native/src/unified/vocabulary.rs):C 域 10 条(active)+ 异常域 4 条(reserved, 取自 S4 §6:throw / unwind / catch_enter / finally),出口 serve semantic_labels。 防线 test_semantic_label_vocabulary_closed 断言引擎实际产出的每个非空 label 都能归类 ——新增标签不登记词汇表即失败。
  5. code_file / call_stack[].return_line / func_display_name / func_mangled_name 已进 v0.2 字段台账(§9.2,状态 pending),并在 §8 #1/#2/#9 的计划列指向台账。

防线首日即抓到实缺陷(诚实记录):词汇闭合测试上线后立刻发现 释放内存、 调用 printf、返回 三条词汇在真实程序里几乎不可达——infer_semantic_label 把"循环上下文"判定排在具体语句模式之前,而循环变量在循环结束后仍在作用域内, 于是循环之后的 printf / free(p) / return 0 全被标成 循环 i=3。 已修订判定顺序(具体语句模式优先,循环上下文降为兜底前的最后一档),实测 循环体内的普通语句仍保留"循环 i=k"标注(教学价值最高的用法不变), 循环之后的语句恢复正确标签。


2. B4:排期确认

  • 透视模式 preview(CS2):确认 CS2 交付点包含可运行 build/分支,由我方在 CS2 完成后提供"内容组试用报告",作为 CS3 投入的最后一道确认。
  • G9 算法验证落点:确认落点 CS5 之后、LeetCode 批次之前。 关于"41 个 infer 模板元数据提前可见"——无需等待:算法标注器与模板元数据 已在仓库公开可读:
    • 标注器:native/crates/vitro_algorithm_steps/(7 个模块:sorting / search / graph / tree / dp / math / structures,41 个 infer_* 函数);
    • 模板元数据:templates/*/meta.yaml(88 个模板,含 source.c / source.cpp 与 meta.yaml)。 对端可据此立即起草 CS6 的对拍用例集("同题 C/C# 解法的算法名/phase/置信度一致")。

3. C1:第二批 capi(memory / breakpoints)窗口表态

结论:第二批 capi 走"CS 批次之间插入"——落在 CS1 验收面稳定之后、CS2 交付点之前。

理由(按依赖强度排序):

  1. CS1 不需要它:CS1 验收面是 ch01–04 语料的"可编译可运行 + 入口合成", 只依赖第一批(compile / run / diagnostics)与 A3;
  2. CS2 反而需要它:CS2 是"预览版交付点"(白箱跑算法 + ARC 别名可视化), ARC 别名可视化要 region 高亮、透视模式要内存/断点数值形态——第二批若排在 CS2 之后, CS2 只能做半张画面;
  3. M3(调试面板)依赖第二批的三段式 kind,而 C2 已把 serve 过渡形态先行落地 (见 §4),第二批的增量被压缩为"语言中立化 + capi 导出",量级可控。

可退让的降级线(对端可直接采用其降级方案):若 CS0/CS1 出现进度压力, 退让顺序为 把第二批整体后移到 CS2 之后(M3 顺延,先做满 M1/M2)。 该退让不需要对端重新谈判——文中"降级方案"即为我方接受的口径。


4. C2:memory.regions 三段式 + 栈/全局标注

请求:kind(global/stack/heap)定型时,为栈/全局区域补 alloc_line / 名字。

处置:已在 serve 出口落地(过渡形态),并附可执行测试。

kind 来源 name alloc_line alloc_by 备注
heap 内部堆区清单 heap_N / FILE:<path> 分配点行号 malloc/calloc/realloc/strdup/fopen/vfs 保留 is_freed(三色堆图)
global VM 全局符号合成 变量名 声明行 static ty 为 C 风格类型名
stack 活跃调用帧合成 函数名 进入该帧的调用行(main 为 0) call size = 帧跨度
  • 数组按地址升序;新增 region_counts{global,stack,heap}(判据字段);
  • 堆统计口径零影响:栈/全局区域只在导出层合成,不写回 session.memory.regions ——否则 total_allocated / 碎片率会把栈帧算成"已分配堆内存"。 该不变量有独立测试(test_segments_do_not_pollute_heap_region_list);
  • 实测(S3 的 swap 载体):main 帧 alloc_line=0、swap 帧 alloc_line=14(调用点行)、 全局 GLOBAL_N alloc_line=3(声明行)。

对 S3 断言的影响:regions 现在同时含栈/全局条目,S3 A10–A12 按 alloc_by == "malloc" 过滤,不受影响;A4 的"指针 target_addr 落在此前无 region 覆盖的 栈空间"现在变成有 kind:"stack" 区域覆盖,是契约增强而非冲突。


5. D2:多会话语义澄清

结论(一句话版):单 serve 进程 = 单活跃会话;session.create = 清空重建, session.reset = 清空运行状态但保留会话级配置,session.destroy = 清空重建(进程存活)。 方法表不携带并发句柄参数,也不支持并发逻辑会话——并发拓扑(长寿命诊断 + 瞬态运行)请起两个 serve 进程。

不只是文档:三个方法的响应都带上机器可读的拓扑字段,消费方不必靠文档猜:

{"id":1,"method":"session.create"}
{"id":1,"ok":true,"result":{"created":true,
  "session":{"model":"single-active-session","active_sessions":1,"concurrent_sessions":false,
             "handle_parameter":false,
             "create_semantics":"clear-and-rebuild(清空重建同一实例)",
             "reset_semantics":"清空编译/运行状态,保留会话级配置(隔离预算/deterministic/argv)",
             "destroy_semantics":"清空重建(进程存活;如需回收进程请用 shutdown)",
             "concurrency_recommendation":"并发场景起多个 serve 进程"},
  "config":{…}}}

于是"M1 的双进程拓扑能否收敛为单进程"的答案是:不能收敛,也无需收敛 (两进程各自独立会话,互不干扰;单进程方案会引入会话句柄与互斥,收益为负)。


6. D3:target_name 跨帧解析

请求:被 pointed-to 变量在其他帧快照中可匹配时给出名字。

处置:已落地。VitroVM::find_variable_name_at_addr(addr) 搜索范围 = 当前帧 → 全局符号 → 其余活跃帧(由内向外):

  • 命中判据:地址等于变量起始地址,或落在数组类型变量的元素区间内(&arr[2] → arr);
  • 作用域判定与 get_variable_snapshot 同源(函数归属 + 声明行;非顶层帧的"当前行" 取被调帧记录的 caller_line,语义自洽);
  • collector 侧保留两级顺序:当前帧快照优先,未命中才问 VM(同名变量时内层更贴近学生视角)。

实测(S3 P1 swap 载体):

{"name":"a","target_addr":1048568,"target_name":"x","status":"Valid"}   // 此前 target_name==""
{"name":"b","target_addr":1048572,"target_name":"y","status":"Valid"}

Schema 影响(已在 §2.5 写明):target_name 的空串语义收窄为"地址不属于任何 可见变量槽"(指向堆块内部、struct 字段中间、已出作用域变量),不再等同于"跨帧解不出来"; 消费方不得把空串当作悬空信号(悬空与否由 status 表达)。无字段增删,属值语义增强。


7. 证据与防线(本次全部实跑)

防线 命令 结果
Rust 全量 cargo test --workspace ✅ 全绿(含 3 个新增测试文件/用例组)
静态检查 cargo clippy --workspace --all-targets --all-features -- -D warnings ✅ 零警告
字段冻结(含新增) cargo test --test step_payload_schema_v0_1_test ✅ 10 passed(预留位缺省 / 词汇闭合 / 展开粒度 / 文档↔代码)
内存地图与跨帧 cargo test --test memory_map_segments_test ✅ 3 passed
C 影子验证 go run ./scripts/shadow_verify(时点命令为 Python 版,已随 D5 退役) ✅ 662 用例(见 CHANGELOG 记录)
C++ 影子验证 go run ./scripts/shadow_verify_cpp(时点命令为 Python 版,已随 D5 退役) ✅(见 CHANGELOG 记录)
serve 出口一致性 go run ./scripts/serve_smoke(当时为 Python 版) ✅ 40 项断言通过(新增三段式内存地图 / schema 轨道 / 词汇表 / 会话语义)
签字回放 S1–S5 go run ./scripts/replay/replay_s1_s5.go(时点命令为 Python 版,已随 D5 退役) ✅ 61/61 PASS(前置门禁要求产物含当前 HEAD)

8. 遗留与诚实记录

  1. C2 属过渡形态:kind 三段式目前在 serve 出口;capi 第二批落地时做语言中立化 (kind + status + alloc_line),届时 memory.regions 可能增字段(status), 现有字段语义不变。
  2. 全局区域 size 口径:优先取符号表槽位跨度(与 codegen 实际分配一致); 末位符号退化为 compute_type_size(标量/数组精确,struct/union/class 因 VM 侧无布局表 返回 0 时再退化为 1 字节)。这是可视化口径,不是 sizeof 语义,勿用于判分。
  3. target_name 已知限制:非数组复合类型不参与区间匹配(struct 字段取址解不出名字)。
  4. B2 的 CS3b 部分仍是"已登记、待激活":四预留位 + 异常域词汇 + unwinding_step_granularity 契约均为 reserved,激活顺序与验收按 schema §9.1 清单与 S4 §5 A1–A8 执行。
  5. A3 / B3 属 CS0/CS1 交付面(子集规范、差异清单草稿),不在本批处置范围。