Skip to content

SDK/Foundation 2.0 冻结前全面审查清单(ABI 收口) #147

Description

@LiuYunPlayer

SDK/Foundation 2.0 冻结前全面审查清单

2.0 发布后 TuneLab.SDKTuneLab.Foundation 即冻结 ABI,本 issue 记录发布前全量审查(96 个源文件逐一通读)发现的问题,按优先级逐项清账。

总评:整体接近可冻结状态。快照体(sealed + required init 纯加性)、线程纪律/坐标系头注释、config 工厂+With 范式、ImageResource 封闭层次、agent 会话 DIM 默认实现、会话/快照/声明面三分——均为成熟的冻结面设计,不动。


一、高优先级(冻结前必须处理,冻结后无机会)

  • 1. Last 一词两义IReadOnlyNotifiableLinkedList<T>.LastTuneLab.Foundation/Event/IReadOnlyNotifiableList.cs)是末元素IVoiceSynthesisNote.Last / IInstrumentSynthesisNote.Last前一个邻居。插件作者两个面都会碰(context.Notes.Last vs note.Last),同名反义。建议 note 邻居改 Previous(对齐 .NET LinkedListNode.Next/Previous)。 ✅ e2363bf(取 Previous 全词;整个链表家族对齐:ILinkedNode 根→SDK note→数据层 Note/Part/Vibrato→合成 context 代理→调用点→legacy compat→测试/样例插件→5 文档;集合 First/Last 末元素访问器保持不动)
  • 2. IValueConfig 注释与 AutomationConfig 现实矛盾IControllerConfig.cs 注释称"是否实现本接口在类型上区分有无默认基线(连续型 AutomationConfig 实现、分段型不实现)",但 AutomationConfig 是单一类、恒实现 IValueConfig<double>,分段型只是运行时 DefaultValue = NaN。按 is IValueConfig 做"重置为默认值"的通用逻辑会把分段轨当"默认值 NaN 的有基线配置"。要么拆两个类型兑现注释,要么改注释并钉死"消费方必须先判 IsPiecewise"的契约。 ✅ dc71879(取方向 B:保留单类统一形态。查明 IValueConfig 真实语义 = 默认值递归 GetDefaultValue 的「叶子」基例,与「有无基线」无关;改注释如实描述叶子 vs 复合,钉死 NaN 哨兵契约,AutomationConfig 显式实现处就地点明。纯注释、无行为改动)
  • 3. VoiceSourceInfo / InstrumentSourceInfo 是可变 struct + public 字段:声库目录元数据必然增长(作者/版本/语种/标签/试听……),public 字段冻结后永不能改属性、struct 永不能改 class(均为二进制破坏)。不在热路径,建议改与快照家族同款 sealed class + required init,加字段纯加性。 ✅ 6b0cfd1(附带:TryGet* 的 out 参数改 [MaybeNullWhen(false)] out …?,编译期强制 false 分支不解引用)
  • 4. DraggableNumberBoxConfig / TextBoxConfig 的 With 手抄字段SliderConfig.cs 明文承诺"With 走 Clone,加字段只改声明处",但 DraggableNumberBoxConfig 7 个 With 与 TextBoxConfig.WithPassword 全是逐字段手抄对象初始化器——将来加字段漏抄一个 With = 静默丢配置,编译器不报。统一 Clone 模式(private set + MemberwiseClone)。 ✅ 60153d9(其余 config 已确认无此问题:AutomationConfig/ComboBoxConfig 已 Clone、复合 config 与 CheckBoxConfig 只有 Create 无 With)
  • 5. TuneLabContext.Global public setter 可被插件劫持:SDK 是所有插件 ALC 共享的一份,任一插件 TuneLabContext.Global = 自己的实现 即可替换全体插件的语言/日志器(绕过"日志前缀由宿主判定、插件无法伪造"的设计)。setter 改 internal(宿主经既有 InternalsVisibleTo 注入)或注入一次后再赋值抛异常。 ✅ 6469b28(取 internal set 方案,getter 仍 public)
  • 6. SoundSourceInfo.ID 命名不一致:全 SDK 皆 IdEffectInfo.IdPropertyKey.IdAgentToolCall.Id),唯此处 ID。改名一次到位。 ✅ 753f9cc(JSON/CBOR key 是小写字面量 "id"、与属性名解耦,不动已序列化数据)
  • 7. Map<K,V>.Empty 共享单例可被下转型改写:声明为 IReadOnlyMap 但实例是可变 Map((Map<K,V>)Map<K,V>.Empty).Add(...) 污染全进程(含 PropertyObject.Empty 底层 map);IReadOnlyOrderedMapBuilder 的 EmptyOrderedMap 同理。给真不可变空实现(或 Add 抛异常的私有子类)。 ✅ c9e2288(新增 EmptyMap/EmptyOrderedMap 直接实现只读接口而非子类化,下转型抛 InvalidCastException;空 PropertyObject 底层也改指向不可变单例)
  • 8. OrderedMap.AddMap.Add 重复键语义分叉:前者 = 删旧 + 挪到末尾(静默重排),后者 = 抛异常(Dictionary 语义)。且 OrderedMap.Insert(index, key, value) 在键已存在且旧位置 < index 时实际落点前移一位、语义未定义。统一(倾向 Add 恒抛、替换走索引器)或把现状写成明文契约。 ✅ a5c257a(取统一方案:Add/Insert 恒抛、替换走索引器;全量审计调用方无一依赖旧覆盖/挪位;新增/改测试锁契约)
  • 9. EffectSynthesisSnapshot 漏 sealed:Voice/Instrument 快照均 sealed,唯它是裸 public class。不 seal = 把"可被插件继承"冻进 ABI。 ✅ 91f4892
  • 10. Format 面文档最薄弱 + Serialize 形态:(分三提交 0dc9590 / abc24e8 / 7fd7615
    • IExportFormat.Serialize(ProjectInfo) → Stream 迫使插件全量缓冲进 MemoryStream,且流所有权/Position 无契约;void Serialize(Stream output, ProjectInfo info) 更常规省内存——现在是唯一能改的窗口。 ✅ 0dc9590(改 void + 宿主给流;FormatsManager 内部缓冲保留原子写;TLP/CBOR/compat/样例/测试/文档同步)
    • DataInfo 全族无单位文档:NoteInfo.Pos/DurTempoInfo.PosPartInfo.Pos 是 tick(PPQ 多少?)、Gain 是 dB 还是线性、AudioPartInfo.Path 绝对还是相对——外部格式作者只能猜。按 Voice 面注释水准补齐。 ✅ abc24e8(逐处查实:tick=PPQ 480、Pos 全局 vs part 相对、秒 vs tick 混用点明、Gain=dB、Pan∈[-1,1]、Path 相对可多层上级 .. 等)
    • NoteInfo.Phonemes 是 get-only 计算属性(每次 LINQ 拼接):DTO 域里反射式序列化器(插件拿 System.Text.Json 序列化 NoteInfo 很常见)会把派生属性当数据写出。改方法 GetPhonemes()。 ✅ 7fd7615(in-repo 零消费,直接删除而非改方法——SDK 面最小;消费方自行 Concat)

二、中优先级(改动小、值得顺手做)

  • 11. int 换算不一致PropertyValue.ToInt 截断 (int)dPropertyObject.GetInt 四舍五入 Math.Round——同一值体系对 2.7 一个给 2 一个给 3。统一成 Round。 ✅ 58e0f02
  • 12. 复合 config 不做防御性拷贝ArrayConfig/ListConfig/ObjectConfig/ExtensibleObjectConfig/ComboBoxConfig.Create 直接持有调用方 list/map 引用,事后改动穿透进已构造 config——与 PropertyObject/PropertyArray"构造即拷贝、值语义由构造保证"的纪律相悖。要么拷贝、要么把"传入后不得改动"写成明文契约。 ✅ a206216(取拷贝方案:5 处 Create 一律 [.. x] 构造即拷入自持副本;map 经 CollectionBuilder 物化、空态落真不可变单例;元素/项均不可变 → 浅拷封死整树。命名保留 CLR 类型名共识不变)
  • 13. ComboBoxItem public set:全 SDK 唯一可变公开配置件(Value/DisplayText/SubItemsset),改 init(对象初始化器写法不受影响)。 ✅ aaa9ea4(IsSeparator 亦 private init)
  • 14. Merge/MergedEvent 持惰性 enumerable:Subscribe/Unsubscribe 各枚举一次 sources,传 LINQ 查询且两次结果不同时退订失配泄漏。构造时 ToArray 一次。 ✅ f2398a8(取固化方案:MergedEvent 与孪生 Merged 均构造时 ToArray 锁死同一批源;明文契约 Merge=固定源快照、动态成员走 WhenAny/WhenAnyItem。核实无行为变化——消费方源本就稳定)
  • 15. IAutomationEvaluator.Evaluate(times) 是否要求升序未定契约:宿主实现大概率按升序游标优化,乱序传入是静默错值还是合法?一句注释钉死。 ✅ eb820bc
  • 16. DIM 加性政策只写了一处IInstrumentSynthesisSession 头注释有"新增成员一律 DIM 兜底"约定,但同为插件实现面的 IVoiceSynthesisSessionIEffectSynthesisSession、三个 Engine、IImportFormat/IExportFormatIExtensionSettings 都没写。每个插件实现面接口统一声明。 ✅ ef5ec4b(8 个接口补齐;IVoiceSynthesisSession 注明 IsContinuation 刻意无默认为例外)
  • 17. 注释残留旧类型名IVoiceContextIVoiceEngine(IVoiceSynthesisSession.cs)、IInstrumentContext(IInstrumentSynthesisSession.cs)、engine 注释里 IVoicePart*/IInstrumentPart*IExtensionSettings.cs 有"Init/CreateSession/CreateSession"重复词。冻结前统一 grep 注释里的类型名。 ✅ ef5ec4b(另扫掉 item 1 漏网的 3 处 note.Next/Last→Next/Previous)
  • 18. DisposableManager.DisposeAll 用 HashSet 无序销毁:退订常有"后建先拆"期望。改 List + 逆序;顺便考虑实现 IDisposable 支持 using。 ✅ 3be2612(List 保序 + 逆序 Dispose + 实现 IDisposable)
  • 19. 文件卫生:DataInfo 全族与 IExport/ImportFormat 的模板期无用 using(System.Threading.Tasks/System.Text/自引用 using TuneLab.SDK;);IReadOnlyNotifiable 藏在 IReadOnlyNotifiableProperty.cs、IReadOnlyNotifiableLinkedList 藏在 IReadOnlyNotifiableList.cs。冻结版源码是外部阅读基准,清一次。 ✅ 1d48ac0(清冗余 using + IReadOnlyNotifiable / IReadOnlyNotifiableLinkedList 移到同名文件)
  • 20. 字段扩展缺口IReadOnlyNotifiablePropertyObjectExtension 有 Number/String/Bool/ValueField 但缺 IntField/EnumFieldPropertyObject 有 GetInt/GetEnum 对应物)。事后补纯加性、不急,想对称现在补最干净。 ⛔ won't-doba1d914):结论相反——int/enum 非 PropertyType 存储类型(To<int> 恒 false、无 Create(int)),*Field 只绑存储类型,不该补 IntField/EnumField。反而顺势删除了三个零调用的派生转换器 PropertyValue.ToInt/PropertyObject.GetInt/GetEnum(ToInt 是 IsInteger 时代宿主转 int 契约的遗留,换 INormalizedScale 后取整规则已下放插件),并统一取值面命名到 CLR 类型名(Bool→Boolean、NumberField→DoubleField)。

三、低优先级(记录在案,接受现状也合理)

  • 21. struct 形态三种并存需定政策SynthesizedPhoneme/SynthesisStatusSegment(可变 struct+public 字段)vs VoiceSynthesisPhonemeSnapshot(readonly struct+ctor)vs PhonemeLayoutNote/SynthesizedSyllable(readonly struct+init)。字段冻结后永不能改属性,明文选定政策。 ✅ 84873d4(取 readonly struct + init:淘汰可变 struct+public 字段;判据=值/引用按数据本质选、struct 内成员选属性非裸字段(field↔property 二进制破坏)、readonly 除坑;未加 required 留与 default 安全议题并定。成文政策并入 item 31)
  • 22. SynthesizedSyllable.Phonemes/VoiceSynthesisNoteSnapshot.Phonemes 每次 get LINQ 拼接分配;default(SynthesizedSyllable).Phonemes NRE。 ✅ eca6612(取「移出 SDK」而非修补:删两类型 Phonemes;SynthesizedSyllable 宿主独用→宿主扩展 PhonemeCount/AllPhonemes(值+nullable 双重载,吸 default NRE);VoiceSynthesisNoteSnapshot 插件面→守 SDK 最小不补便利,消费方从 Leading/Body 自数;compat/样例已迁。分配+NRE 结构性消除)
  • 23. PropertyKey 为 default 时 GetHashCode NRE(Id=null);PointIEquatable<Point>(Equals 装箱)、NaN 点不自反。 ✅ d52349e(PropertyKey.GetHashCode 改 Id?.GetHashCode() ?? 0 除 default NRE;Point 实现 IEquatable + Equals 用 double.Equals 自反(NaN 可被集合找回、免装箱),== 保留 IEEE 刻意分叉,对齐 BCL)
  • 24. AnyItemEvent(WhenAnyItem)同一 handler 重复订阅时 mWires 账本被覆盖、退订失配(边角)。: 5aaf696(mWires 账本 (item,handler)->值改 Stack<转发器列表>,对齐 mDownstreams 容忍重复;订 N 次触发 N 次、退 N 次净,单订阅不变。覆盖式 bug 只毁拆除路径-退订后悬挂转发器泄漏+幽灵回调-订阅期间无碍。补重复订阅测试用例,堵 a22c6bb 盲区)
  • 25. 小分配项:OrderedMap.Values 每次 get 新建 ValueCollection、map 枚举逐项分配 ReadOnlyKeyValuePairComboBoxConfig.Append O(n²)——均在低频域。 ✅ 记录接受(won't-do,无代码改动):三点均冷路径且成本被夸大——Values 是 ValueCollection wrapper(持 map 引用按需 ValueAt,非复制值;Keys 更直接返回底层 List);KVP 逐项分配是唯一真分配但去不掉——Enumerator.Current 类型是接口 IReadOnlyKeyValuePair,改 struct 装箱回接口一样分配,且零分配路径已在(热循环用 KeyAt/ValueAt 索引,只有 foreach 付 KVP);ComboBoxConfig.Append O(n²) 低频(config 建一次、n 小)且有批量重载 Append(IEnumerable) 作逃生口。按“SDK 干净=API 面最小”取向,冷路径微分配不属 API 面问题,不 churn 冻结内部。
  • 26. GetPhonemePropertyConfigs 扁平索引对齐契约脆弱(长度须精确等于扁平音素总数)——注释已钉死口径,接受。 ✅ 406dd53(升格为重设计而非接受:审出真缺陷=契约粒度(逐音素 schema)与 UI 多选合并粒度(按核相对 slot)错位——宿主取首成员 config 当「代表」实质是宿主合并 schema,违反「宿主只并值」原则。改按核相对 slot 键控 IReadOnlyMap<int, ObjectConfig>(键=角色:0=核/<0=引导/>0=核后),多选合并归引擎(值依赖时对同 slot 成员值三态 Merge,同 GetNotePropertyConfig 契约);键控自描述、扁平漂移契约消失;SDK 增共享口径 PhonemeSlots(PhonemeAt/UnionSlots)。备选已论证否决:返回合并结果=逼插件复刻宿主对齐规则;逐 note 嵌套=值依赖 schema 下宿主拿到互异 schema 无从合并)
  • 27. IAgentModelSession SendAsync 三连 DIM 重载:将来 tool-call 流式增量时考虑 callbacks 聚合类新重载(纯加性),现不动。 ✅ 2832b90(升格为发布范围决策:agent-model 整面收回宿主内部模块(TuneLab/Agent/Contracts,命名空间 TuneLab.Agent),不随 2.0 SDK 冻结;新模型适配走 PR(贡献指南 docs/agent-model-adapters.md),外部扫描分支摘除报自解释错误,现无外部实现者、零破坏。理由:LLM 协议面迭代速度与冻结错配 + owner 未 review 的契约不冻 + 适配器少/小/无资产更适 PR。原 SendAsync 重载问题随之作废。将来若开放为插件点的先决整改 checklist(亦钉 IAgentModelEngine 头注释):①SendAsync 重载阶梯收敛为 callbacks 聚合对象 ②AgentTokenUsage int→long ③枚举(Role/Modality/ContentKind)补未知值容忍契约 ④两接口补 DIM 加性政策头 ⑤AgentMessage Content/Parts 双真相源契约钉紧、角色限定措辞改「当前仅」 ⑥AgentModality 与 AgentContentKind 对应关系注释交叉引用)
  • 28. NumberFormat.Decimals 用 CurrentCulture:不同区域设置下滑条文本不同(小数点/逗号),知晓即可。 ✅ bf7a8bd(升格为修复而非知晓:NumberFormat.Decimals 的 Format/Parse 改 InvariantCulture——值本质是数据(持久层句点存),CurrentCulture 会在逗号区把句点当千位分隔误读("3.14"→314);本地化数字走 Custom 逃生口。附 1.0 血证 c764721「尝试修复部分地区svg图标不显示」——rgba alpha 经 CurrentCulture 出 "0,5" 多逗号致分量错乱,当年用 zh-CN 碰巧句点绕过;本次 SvgIcon 一并改 InvariantCulture 为正解)

四、冻结纪律(流程层,比单点修改更重要)

  • 29. public API 基线守护:两程序集接入 Microsoft.CodeAnalysis.PublicApiAnalyzers(PublicAPI.Shipped.txt 进仓库)或 CI 跑 ApiCompat——冻结从此是每个 PR 的机械校验,不是一次性审查。 ✅ e5f7a13(选 PublicApiAnalyzers 而非 ApiCompat:编译期即时+零 CI 依赖+文本基线可 diff,贴合本地 build 工作流且是 AI 执笔的硬护栏。SDK 673 + Foundation 303 条 public 签名(含 nullability)进 PublicAPI.Shipped.txt=2.0 基线;RS0016/17/24/25 提 error;运营约定钉 csproj 注释(加性走 Unshipped、发布时挪 Shipped+提 SdkVersion、breaking 用 REMOVED 留痕)。已验证:改名 public 成员触发 RS0017+RS0016 双 error 编译失败)
  • 30. 钉死 AssemblyVersion 政策:两 csproj 现无任何版本属性(默认 1.0.0.0)。显式固定 <AssemblyVersion>2.0.0.0</AssemblyVersion> 永不再动,真实版本走 FileVersion/InformationalVersion——否则未来版本跳动使已编译插件绑定失败。 ✅ e8f3a81(两 csproj 显式 2.0.0.0,切断 Version 派生链、2.x 永不再动;防「作者升级快×用户升级慢」组合的无辜绑定失败(与是否用新 API 无关);合法拒绝归 manifest sdk-version 门(另一版本轴,勿对齐)+PEReader 直扫兜底。已验证:DLL 版本 2.0.0.0、V1.Voice 重编译引用记录烧 2.0.0.0)
  • 31. 接口分类表进 SDK 文档:**(含 item 21 合成域值 DTO 三形态政策:readonly struct+init 默认 / ctor 全字段必填备选 / 淘汰可变+public字段;struct vs class 判据)**插件实现面(加成员必须带 DIM 默认体)/宿主实现面(可自由加成员)/纯值 DTO(加字段带默认、不标 required)三张清单一次列全,成为可执行的冻结契约。 ✅ 1de1a02(docs/sdk-api-evolution.md:61 接口三分类全列(A 插件实现面=DIM 必带+语义责任 / B 宿主实现面=自由加、插件自实现不受保护 / C 开放数据结构=按 A 对待 / D config 封闭族)+ item 21 DTO 三形态政策并入 + 枚举未知值容忍 + 变更操作速查表 + 三道防线与发布收口总览。另写 AI 记忆 project_sdk_public_api_gate:遇 RS0016/17 的正确处置(breaking 先呈报、禁机械改清单绕过))

明确不动的(审查确认为正确设计):三大合成域会话/快照/声明面三分与镜像对称;required init 快照纯加性形态;PhonemeLayout 只冻 I/O 形状、算法留宿主侧演进;PropertyValue 单 box 无装箱联合 + 新增类型臂检查单注释;voice/instrument/effect 刻意平行不抽基类;IsContinuation 无默认体强制显式表态。

五、收尾统一修(issue 全清后一次性 sweep)

清完所有单点项后做一次全量测试工程 sweep:build + run 每一个测试工程(非仅 tests/TuneLab.Tests)+ build 全部样例插件,一次性修净编译遗漏与爆红,确保无工程带病发布。已知遗漏:

  • compat 测试工程 FakeNote 仍声明旧名 Last(item 1 Last->Previous 改名未同步到 TuneLab.Hosting.Compat.Legacy.Tests,自那时起编译不过):NoteOverlapClampTests.cs / LeadingIntoUnpinnedNeighborTests.cs 各一处改名。 ✅ a07f4d3(两处改名,compat 测试复活 10/10 通过)

✅ 全清(2026-07-21):31 项 + 收尾 sweep 全部了结。最终全量验证:solution build 0 error、TuneLab.Tests 149/149、Compat.Legacy.Tests 10/10、22 个样例插件全部 build 通过。SDK/Foundation 冻结面此后由 PublicAPI 门禁(每次编译机械校验)+ AssemblyVersion 钉死 + sdk-version 门三道防线持续守护;演进纪律见 docs/sdk-api-evolution.md。


六、第二次全面复审(投影 API 下沉,2026-07-21)

在三道防线就位后,对全量 SDK public 面做第二次复审,判据:grep 消费方,全在宿主(含 GUI/测试)+ SDK 内部即候选下沉;插件/legacy 读则留(沿用 item 22 「投影是消费侧私事、SDK 面只承载契约」先例)。第一批 5 处已了结:

  • A. NoteInfoExtension.StartPos/EndPos(整类):StartPos 是 Pos 恒等别名、EndPos=Pos+Dur,仅 Midi.cs 两处宿主消费。整类删除、就地内联。
  • B. ComboBoxItem.ShowText():纯渲染投影,仅宿主 ComboBoxController 三处。移宿主扩展 ComboBoxItemExtensions(TuneLab.GUI)。
  • C. ComboBoxItem.IsGroup:宿主独用且可派生,仅 ComboBoxController 两处。内联为 SubItems is not null
  • D. PhonemeLayoutNote.PhonemeCount:零外部读者,仅 PhonemeLayout.Resolve 内部用。降为 PhonemeLayout 私有静态助手、移出 public 面。
  • E. IVoiceSynthesisNoteView.Phonemes:Leading++Body 的派生投影(唯一读者是 SDK 内 PhonemeSlots,宿主只 implement),移出接口、拼接折进 PhonemeSlots(跨两列表就地索引、省一次物化);删宿主实现,docs 四篇同步。

✅ 一并提交 12cade0(PublicAPI.Shipped.txt -7 行)。验证:solution build 0 error、TuneLab.Tests 149/149、Compat.Legacy.Tests 10/10、样例插件 V1.Voice 0 error。

保留(复审确认不动)PhonemeSlots.UnionSlots/PhonemeAt(插件面,V1.Voice+docs 使用)、PhonemeLayout.Resolve(宿主+legacy 依赖)、PhonemeTiming.Duration/Deconstruct/ToString(Resolve 返回类型的自然值类型 API)。

待续:第二次复审仅完成上述第一批;其余 SDK 面(Foundation 响应式组合子扩展等)尚未逐一过 grep,留后续批次。

六.1 穷举 sweep + 冻结契约回归覆盖(2026-07-22)

对全部 SDK public 成员逐一 grep 消费方(分 voice / instrument+effect+shared / DTO / config+杂项 四路)。结论:第一批已把派生投影成员清光,全域再无新的派生投影。各路初报的"宿主独用候选"经"派生投影 vs 正交状态 vs 对称设计"复核后几乎全为误报——它们不是可下沉的投影,而是:① DTO 正交序列化状态(Effects/PiecewiseAutomations/Bitrate 等,删=格式插件丢数据);② 跨域对称声明面(三域平行设计);③ 正交快照/上下文状态;④ 半接线配置能力(活 getter 的声明半边)。判定:SDK 契约面在"去投影"目标上已收口,其余"当前宿主独用"成员均为合法契约,全留。

沿途查出若干已冻结契约路径零回归保护,补测试(提交 4d2fdda):PhonemeSlotsTests(PhonemeAt/UnionSlots 定位与边界 + 多选声明合并范式)、VoiceSynthesisSnapshotPropertiesTests(合成期 NoteSnapshot/PhonemeSnapshot.Properties 读回)。主测试 149→161 全绿。

方法论纠正(重要):sweep 的 grep 只扫了仓内 tests/plugins/V1.* 夹具,漏了外部真实 voice 引擎插件(跨仓 ProjectReference 直引 2.0 SDK 源,是权威消费方)。以其为准复核后:

  • 第一批 5 处删除对真插件零破坏——真插件本就自己 [.. Leading, .. Body] 拼接、从不读被删的 IVoiceSynthesisNoteView.Phonemes,反证该删除正确。
  • 大量被 sweep 判"存疑/宿主独用/零消费死代码"的成员,真插件实际在用VoiceSynthesisSnapshot.Automationscontext.AutomationsNoteView.{Lyric,BodyOffset,LeadingPhonemes,BodyPhonemes}PhonemeView.{Symbol,Properties}WithRangeDraggableNumberBoxConfig.IntegerAutomationConfig.WithRandomizableGetSnapshotPreviousPartProperties+MergeIsContinuationPhonemeLayout.Resolve。"全留"决定获强验证。
  • 今后消费方判据:外部真实插件为第一权威,V1.* 夹具只是补充;仅凭仓内 grep 会严重低估插件面。

第二次复审收口:SDK 契约面去投影已完成;design-decision 项(EffectSynthesisSnapshot、VoiceSourceInfo.Description、NormalizedScale.Ceil/Floor/Rounded 等)经决定全留不动;Foundation 通用工具库经复审亦全留(见记忆)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions