Files
structrail-design/.trae/documents/tracer-validation.md
skycurtain 5cc4f96b46 docs: 新增设计决策文档并实现数组追踪器校验
- 新增决策文档:移除 preset 指令,将其合并到 create 指令中,以简化生命周期和系统复杂度
- 新增决策文档:确立 SDK 侧影子状态校验机制,实现快速失败和最小必要状态原则
- 在 ArrayTracer 中实现影子状态校验,维护数组长度并进行索引边界检查
2026-02-06 01:56:52 +08:00

55 lines
2.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tracer 指令校验机制设计决策
## 背景
在设计 Tracer 协议时,我们面临一个关键的架构决策:**应该在哪里对 Tracer 的操作指令进行合法性校验(例如数组越界、空指针访问)?**
我们有两个主要的选择:
1. **SDK 侧(生产者)**:在用户调用 SDK API`tracer.pick(i)`)生成指令时即时校验。
2. **渲染器侧(消费者)**SDK 只负责生成指令,由前端渲染器在回放指令序列时进行校验。
## 决策SDK 侧“影子状态”校验
经过讨论,我们决定采用 **SDK 侧校验** 作为主要防线,并引入 **“影子状态Shadow State”** 模式来实现。
### 核心设计原则
1. **Fail Fast快速失败**
* 用户的逻辑错误(如访问越界)应在代码执行阶段立即抛出异常,而不是等到生成了错误的 JSON 并在渲染器播放时才报错。
* 这样可以将错误精确定位到用户代码的具体行号,极大提升调试体验。
2. **最小必要状态Minimal Viable State**
* SDK 不需要维护完整的数据结构副本(那会带来巨大的内存和性能开销)。
* SDK 只需要维护用于校验合法性的 **元数据Metadata**
* **案例**:对于 `ArrayTracer`,我们不需要存储数组的具体元素值,只需要维护一个整数 `currentSize`
### 实现方案
#### 影子状态维护
在每个 Tracer 实例内部维护一个轻量级的状态变量:
* **ArrayTracer**: 维护 `int currentSize`
* `create(array)`: 初始化 `currentSize = array.length`
* `scale(newSize)`: 更新 `currentSize = newSize`
* `pick/patch(index)`: 校验 `0 <= index < currentSize`
#### 跨语言适应性
这种模式具有极强的跨语言通用性:
* **TypeScript/JS**: 简单变量。
* **Java/C#**: 类的私有成员字段。
* **C/C++**: 结构体中的字段(即便 C 语言原生数组不带长度,通过 Tracer 封装后反而赋予了其边界检查能力)。
### 对比分析
| 维度 | SDK 侧校验(采用方案) | 渲染器侧校验 |
| :--- | :--- | :--- |
| **错误反馈时机** | **即时**(用户运行代码时) | **延迟**(用户观看回放时) |
| **错误定位能力** | **高**(直接抛出异常,有堆栈信息) | **低**(难以关联回源码行号) |
| **实现复杂度** | 中(需维护影子状态) | 低(仅需防御性编程) |
| **性能开销** | **极低**(仅维护元数据,如 `int` | 无额外开销 |
| **用户体验** | 类似本地调试,符合直觉 | 可能会看到“崩坏”的动画 |
## 结论
虽然渲染器依然需要具备防御性编程能力以防止崩溃,但**业务逻辑的正确性校验应当由 SDK 在指令生成源头保证**。这不仅符合“数据源头治理”的原则,更能为用户提供更友好的开发和调试环境。