Pathos 是一个用 Rust 编写的交互式叙事引擎。工作区结构:crates/*(10 个 crate),根 Cargo.toml 用 members = ["crates/*"]。
核心 crate:pathos-core(状态机、表达式、脚本、宏、运行时),pathos-parser(.pathos / TOML 解析),pathos-render(渲染 trait),pathos-tui(终端渲染),pathos-cli(CLI 入口)。
- 依赖变更必须用
cargo add,禁止手动编辑Cargo.toml中的依赖项。 如果cargo add因网络等原因失败,必须停下来询问用户如何处理,不得自行编辑Cargo.toml绕过去。 - 各 crate 独立维护
[package]版本号,不使用version.workspace。 - 测试必须加超时守卫——本项目历史上出现过解析器无限循环导致系统崩溃的事故。对可能超时的测试用
with_timeout包装(见集成测试),对外部命令用timeout 30 cargo test ...。timeout命令通过 Homebrew 安装。 - 跑完整测试套件用
cargo test --all -- --test-threads=1,避免并行竞争导致内存压力。
ScriptEngine是 enum(Rhai/JS/Lua),不是 trait。Rhai 始终编译,JS/Lua 在 feature gate 后。- LLM 配置由用户运行时提供(CLI flag / 环境变量 / Web 设置面板),不在
.pathos文件中。 MacroHandlertrait 是 core 内唯一对外扩展点——内建宏和 Mod 注册的 Rhai 宏都走这个接口。RenderBackendtrait 是 core 与渲染端之间的唯一跨 crate trait。引擎是同步的,LLM 调用由渲染后端异步驱动。- Phase 1 没有 LLM:
walk_content遇到 AIBlock 直接渲染 fallback,不走WaitingForStream。
- 原因:
step()返回WaitingForStream→ TUI 调end_stream(fallback 未设置)→ 循环回去再step()→ 再次撞到同一个 AIBlock。ai_fallback从未被赋值。 - 修复:Phase 1 的
walk_content直接把 AIBlock fallback 渲染为StreamFailed,不走异步流程。step()尾部 AIBlock 检查循环已删除。
- 原因:
step()用std::mem::take(&mut self.pending_choices)把 choices 掏空后放入 Render,随后submit_input调用self.pending_choices.get(idx)时拿到空 Vec,按键被静默丢弃。 - 修复:改成
self.pending_choices.clone()保留原数据,submit_input在用户做出选择后才clear()。
- 原因:
StoryState::default()把globals/temp初始化为Value::Null,对 Null 调set("x", val)会走as_mut_nested返回None。 - 修复:
StoryState::default()手动实现,globals和temp初始为Value::Object(HashMap::new())。
- 原因:
Value::as_mut_nested创建中间节点时用了Value::Null,下级set无法匹配Object分支。 - 修复:中间节点改为
Value::Object(HashMap::new())。
- 原因:
r#"..."#内\u{2192}是字面文本,不是 Unicode →。解析器找不到箭头,链接解析失败。 - 修复:用普通字符串
"..."而非 raw string;或直接用->箭头。(解析器同时支持->和→。)
- 原因:frontmatter
---后的空行被split_into_passage_blocks带入块内容,parse_passage_block把空行当标题,真正的# heading被归入正文。 - 修复:
split_into_passage_blocks增加seen_content标志,在第一个标题或正文行之前跳过空行。
- 所有单元测试放在各 crate 的
src/内(#[cfg(test)] mod tests)。 - 跨 crate 集成测试放在
pathos-core/tests/。 - 解析器相关测试用
with_timeout包装,防止无限循环导致系统崩溃。不涉及NarrativeRuntime(非Send)的解析测试可以安全 spawn 线程加超时;运行时测试内联执行即可。 cargo test --all之前确保没有残留的 cargo 进程(pkill -9 cargo以防 Cargo.lock 冲突)。
重大 bug 和修复记录在 docs/incident-report-*.md。工作进度跟踪在 docs/TODO.md。