# Zust 原则

摘自《Harness 原则 / AGENTS.md》。这一部分可以单独复制到项目的 AGENTS.md 或团队规范中使用。

## 2. Zust 语言的特性和规则

把 Zust 当成 Rust-like 的动态强类型语言，不要当成 JavaScript 或 Python。显式处理控制流、运行时类型、可变性边界和返回值。

不要把同一个变量复用成互不相关的运行时类型。如果 `root::get(...)` 可能返回空值，必须显式分支：

```zs
let existing = root::get(path);
if existing.is_list() {
    // 使用已有 list
} else {
    // 创建新的 list
}
```

不要写 JavaScript 风格的 truthy fallback：

```zs
let items = req.items || []
let user_id = req.user_id || ""
let count = req.count || 0
```

应校验具体类型：

```zs
let items = req.items;
if !items.is_list() {
    return { ok: false, error: "items must be list" };
}
```

结构化输入就使用结构化数据。可以传 map/list 时，不要把列表或记录编码成逗号分隔字符串。

ROOT 是运行期状态树，按这个事实直接使用它。

- `root::add` 和 `root::add_fn` 写入 ROOT 路径。
- `start.zs` 或项目启动脚本应初始化配置、地图、数据库、路由、handler 和必要运行期节点。
- 简单项目可以由 `start.zs` 直接注册 HTTP/WebSocket/console 入口；复杂项目应由 `start.zs` 调用各系统的 `start`/`init` 函数，再让系统内部注册自己的 ROOT 节点和分发入口。
- 固定的模块内部调用应直接使用模块函数。
- 只有真正需要运行时分发的边界才使用 `root::add_fn`，例如 HTTP、WebSocket、console/admin 入口、动态世界 handler 或插件式扩展点。
- 不要把简单 ROOT 路径藏进 helper。规则很直观时，优先写可见字符串，例如 `"redis/users/" + account_id`。一个表达式能看懂的路径，不要为了“看起来抽象”拆成只用过一次两次的 helper。
- 只有路径规则足够复杂、helper 能减少真实错误时，才添加 path helper。

Zust 很容易被生成或机械重复。清晰、稳定、易生成的一致结构，比聪明的复用更重要。

不要用跨地图、跨业务、跨模块的默认值掩盖必需配置缺失。全局 fallback 会让坏数据继续流动，并让 bug 更难定位。

错误模式：

```zs
if !world.contains("spawns") {
    return default_spawn();
}
```

更好的模式：

```zs
if !world.contains("spawns") {
    return { ok: false, error: "world spawns missing", world: world.world_name };
}
```

默认值只允许属于当前对象自身，并且语义明确，例如当前 panel 的可选 placement、当前模型的 idle animation。必需数据缺失时，应在启动或请求阶段明确失败。

遇到 Zust compiler、JIT、VM 或 ROOT 错误时，先定位具体脚本、函数、语句和运行时错误含义，再改业务逻辑。判断修复点属于 VM/parser/runtime，还是脚本。不要用无关改写绕过 VM 错误。

有服务端 console 时，优先用 console 调试 ROOT 和脚本。通过 `root::get("node/path")`、`module::function(...)` 等命令确认注册、运行期状态和 handler 输出，再进入完整客户端验证。
