# `nana-story` V1 开发计划

> 中文名：《听娜娜讲故事》
> 计划版本：0.1
> 制定日期：2026-07-28
> 目标平台：Windows 桌面端
> 执行模型：1 个集成人 + 最多 3 个并行子智能体

## 1. 交付目标

V1 不是“通用 AI Galgame 平台”的完整形态，而是一段能够证明整个产品闭环的可玩纵切：

1. 用户通过 LAPP 选择并调用聊天模型；
2. 导入或使用内置的娜娜试玩资源包；
3. 创建故事并进行 20～30 分钟的自由输入式角色扮演；
4. 经历至少一次隐藏的简化 CoC7 判定；
5. 建立一项重要许诺，并让行为影响信赖、希望等关系维度；
6. 获得、使用或转移一件带隐藏背景的持有物；
7. 随时查看自己的持有物、已知线索和已接受许诺；
8. 查看模糊化的六维关系雷达图；
9. 每轮自动保存，退出后能够恢复；
10. 回溯到旧节点继续时创建新分支，并保留原线路。

V1 的成功标准不是资源数量，而是：

> 从创建故事到完成纵切，全程不需要开发者干预；状态可重放、隐藏信息不泄漏、回溯不破坏旧分支、异常不会留下半轮存档。

## 2. 已冻结的产品范围

### 2.1 技术与平台

- 仓库名：`nana-story`。
- 首发 Windows 桌面端；安卓以后再考虑。
- 技术栈：
  - Tauri 2
  - Vue 3
  - TypeScript
  - Vite
  - Naive UI
  - Pinia
  - Vue Router
- 管理界面可以使用 Naive UI，游玩界面采用自定义视觉实现。
- 管理端、游玩端和运行时位于同一个 Tauri 桌面进程，不引入 daemon、sidecar 或本地网关。

### 2.2 模型与 LAPP 边界

- 供应商协议、模型选择、凭据解析和网络调用由 `lapp-rs` 负责。
- `nana-story` 不直接依赖任何供应商 SDK。
- `nana-story` 只提供必要的 LAPP 查看、选择、连接测试和简单管理能力。
- 本地模型只作为 HTTP 服务接入。
- 不负责安装、下载、启动、停止、更新或管理 Ollama、LM Studio 等本地模型运行器。
- 开发时可使用相邻仓库的 path dependency；发布构建必须固定到明确的 `lapp-rs` 提交，不能复制其协议语义。

### 2.3 游戏交互

- 自由输入为主。
- 每轮可提供 2～3 个可编辑行动建议；点击后只填入输入框，不直接提交。
- 提供“继续”按钮，表示沉默、观察、等待或允许 NPC 继续行动。
- AI 不得替玩家决定说话、主动行动或内心想法。
- 时间按照实际行动推进，不按消息轮数推进。
- 高风险行动需要先给出叙事风险提示，再执行隐藏的简化 CoC7 判定。
- 玩家默认看不到骰点、技能数值和精确关系变化。
- 回复由若干短演出节拍构成，而不是一整块聊天气泡。

### 2.4 角色、关系与剧情

- V1 只有一个完整角色：娜娜。
- 剧情模块可以包含会说话的轻量配角，但不为其建立完整关系系统、长期记忆或自主调度。
- 六维关系：
  - 好感 `affinity`
  - 信赖 `trust`
  - 希望 `hope`
  - 尊重 `respect`
  - 亲密 `intimacy`
  - 依恋 `attachment`
- 信赖主要来自已经发生的行为。
- 希望主要来自被接受的许诺和共同未来计划。
- 重要许诺必须保存为独立对象，不能简化成一次关系数值变化。
- 怨恨、恐惧、愧疚、嫉妒等保存为带来源的关系状态，不占六维。
- 创建故事时绑定剧情模块；V1 不支持中途增删模块。

### 2.5 持有物、知识与分支

- 物品定义、物品实例和角色认知必须分离。
- 所有权 `owner` 与当前持有者 `holder` 必须分离。
- 玩家可以随时读取自己当前可访问的持有物。
- NPC 的真实持有物不能直接暴露；只能显示公开可见或玩家已经知道的内容。
- V1 不处理负重、体积、格子或背包容量。
- 每轮自动保存。
- 从旧节点继续时创建新分支。
- V1 不做分支合并，也不建立跨分支记忆。

## 3. V1 明确不做

- 多个完整角色及多角色发言调度。
- 导演 Agent、自动总结 Agent 或运行时多 Agent 编排。
- 完整 CoC7 职业、成长、战斗、理智、追逐和幸运消费。
- Live2D、语音、口型、动态镜头和复杂动画。
- 分支合并和跨分支记忆。
- 中途增删剧情模块。
- 负重、体积、容量、制作和物品经济。
- 内置资源编辑器或可视化剧情编辑器。
- Steam Workshop 正式接入、DLC 支付、社区付费内容抽成。
- 云同步、账户、多人或跨设备存档。
- Android、Web 和多语言发布。
- 模型下载器、模型启动器和本地模型进程管理。
- 任意脚本、任意插件或任意外部工具执行。

V1 只保证资源包格式未来可以承接 Workshop/DLC，不实现市场本身。

## 4. 总体架构

### 4.1 三类事实

| 层级 | 内容 | 权威来源 |
|---|---|---|
| 创作资源 | CharacterCard、WorldBook、Persona、PlotModule、ItemSpec 和素材引用 | 不可变的版本化资源 |
| 分支状态 | 世界状态、关系、许诺、知识、持有物、时钟、判定记录 | 根状态/检查点 + 节点 delta |
| 上下文投影 | 当前意图、命中的世界书、最近演出、提示词 | 每轮临时编译，可重建 |

任何模型推测、检索结果和临时提示词都不是持久事实。需要持续存在的变化必须经过类型化操作，由状态 reducer 验证后写入当前分支。

### 4.2 单一权威写入路径

```mermaid
flowchart TD
    A["玩家输入"] --> B["上下文编译"]
    B --> C["单模型工具循环"]
    C --> D["暂存工具结果与状态操作"]
    D --> E["规则校验与 reducer"]
    E --> F["原子提交 StoryNode"]
    F --> G["生成 PlayerView"]
    G --> H["Vue 演出"]
```

约束：

- 模型和前端都不能直接修改 `RuntimeState`。
- reducer 是状态变化的唯一权威入口。
- 一轮生成要么完整提交，要么完全不提交。
- 模型失败、取消、超时或畸形输出不得留下半轮状态。
- 同一玩家行动的重新生成复用原判定记录；推骰属于新行动和新节点。

### 4.3 前后端安全边界

前端不得得到完整 `RuntimeState`，只能得到经过投影的 `PlayerView`：

```text
PlayerView
├─ 当前演出节拍
├─ 可编辑行动建议
├─ 玩家可访问的持有物
├─ 已知线索
├─ 已接受许诺
├─ 模糊化六维关系
└─ 可回溯节点摘要
```

这条边界用于结构性防止：

- NPC 隐藏物品泄漏；
- 未揭示世界事实泄漏；
- 精确关系数值泄漏；
- 内部骰点和难度泄漏；
- 未触发的剧情节点泄漏。

### 4.4 推荐持久化

V1 默认采用嵌入式 SQLite，由 Rust 侧独占访问。

- 内容包以经过校验的不可变文件形式安装到应用数据目录。
- SQLite 保存故事、资源绑定、节点、分支、检查点和迁移版本。
- 节点中的状态操作使用版本化、类型化 JSON。
- 每隔固定节点数创建检查点，恢复时使用“最近检查点 + 后续 delta”。
- SQLite 事务同时提交用户输入、模型演出、工具结果、判定记录和状态 delta。

建议最小表：

```text
stories
story_bindings
story_nodes
story_branches
state_checkpoints
installed_packs
schema_migrations
```

不要在 V1 过早拆出几十张关系、物品、许诺明细表。分支事件流才是事实来源，当前状态是可重建投影。

### 4.5 模型运行时

V1 游戏运行时仍然只有一个模型，不引入导演 Agent。

每轮流程：

1. 读取当前节点、资源绑定和重建后的运行时状态；
2. 按优先级编译上下文；
3. 调用 `lapp-rs` 选中的聊天模型；
4. 模型按需调用判定、关系、许诺、知识、持有物、时钟等领域工具；
5. Rust 验证工具参数并写入暂存事务；
6. 模型根据工具返回继续生成短演出节拍和行动建议；
7. 校验演出协议与全部状态操作；
8. 原子提交新节点；
9. 生成脱敏后的 `PlayerView` 给前端。

上下文优先级：

1. 玩家自主权和输出协议；
2. 当前角色卡、Persona 与场景；
3. 当前关系、许诺、知识、持有物和剧情压力；
4. 命中的世界书条目；
5. 当前分支最近演出；
6. 非关键风格参考。

V1 不做向量记忆库。世界书先采用确定性的标签、关键词和条件触发；长上下文压缩只能作为可丢弃缓存，不能成为新的事实来源。

## 5. 仓库结构与代码所有权

建议从第一天就使用 Rust workspace，把并行边界变成目录边界：

```text
nana-story/
├─ src/                         # Vue 应用
│  ├─ app/                     # 路由、全局状态、真实/Mock adapter
│  ├─ features/play/           # 主演出屏
│  ├─ features/inventory/      # 持有物、线索、许诺
│  ├─ features/relationship/   # 关系档案
│  └─ features/history/        # 回溯与分支
├─ src-tauri/                  # Tauri 外壳与窄命令
├─ crates/
│  ├─ nana-domain/             # 资源、状态、delta、PlayerView
│  ├─ nana-engine/             # reducer、规则、简化 CoC7
│  ├─ nana-store/              # SQLite、节点、检查点、恢复
│  └─ nana-runtime/            # LAPP、上下文、工具循环、Turn Engine
├─ contracts/                  # 生成的 JSON Schema 与 TS 类型
├─ content/nana-demo/          # 试玩纵切资源包
├─ fixtures/                   # 稳定的契约和模型响应样例
├─ tests/e2e/                  # 跨模块验收
└─ docs/adr/                   # 已接受架构决策
```

单一契约源：

```text
Rust serde 类型
→ 生成 JSON Schema
→ 生成 TypeScript DTO
→ CI 检查生成物是否漂移
```

禁止同时手写 Rust、Schema 和 TypeScript 三套同名模型。

## 6. 多子智能体并行方案

### 6.1 推荐拓扑

并行度维持在 1 个集成人 + 3 个子智能体。

| 角色 | 独占范围 | 主要职责 |
|---|---|---|
| 集成人 | 公共契约、根配置、入口、生成物、跨模块接线、最终验收 | 拆任务、冻结契约、日常合并、拒绝范围扩张 |
| 子智能体 A：Core | `nana-domain`、`nana-engine`、`nana-store` | 类型、reducer、规则、SQLite、分支重放 |
| 子智能体 B：Runtime | `nana-runtime` | LAPP、上下文编译、工具循环、Turn Engine |
| 子智能体 C：Experience | `src/`、`content/nana-demo/` | Vue 界面、演出、官方 Fixture、娜娜纵切内容 |

测试靠近各自模块编写；跨模块 E2E 和契约生成由集成人负责。

不建议同时开启 5～8 个编码子智能体。这个项目的瓶颈是共享语义而不是代码量，过高并行度会把收益变成 Schema、状态提交和 UI DTO 的返工。

### 6.2 必须先冻结的契约

进入正式并行前必须合并：

- `ResourceHeader`
- `CharacterCard`
- `WorldBook`
- `Persona`
- `PlotModule`
- `ItemSpec` 与 `ItemInstance`
- `Story`、`StoryBinding`、`StoryNode`
- `RuntimeState`
- 所有状态操作的判别联合
- `PresentationBeat`
- `PlayerView`
- `TurnRequest`、`TurnResult`、`TurnFailure`
- 五组 canonical fixtures：
  - 普通对话；
  - 隐藏判定；
  - 许诺建立与履行；
  - 物品转移且 NPC 尚未知情；
  - 回溯后分叉。

### 6.3 Worktree 与分支

每个子智能体必须使用独立 Git worktree：

```text
integration/v1
agent/core
agent/runtime
agent/experience
```

规则：

- 所有分支从同一份已冻结契约提交开始。
- 每个子智能体只修改分配的目录。
- 根 `Cargo.toml`、`package.json`、锁文件、CI、Tauri 入口、Vue 入口和生成代码只由集成人修改。
- 子智能体需要新增依赖或修改公共契约时，提交短小的变更请求，不直接跨目录修改。
- 禁止无关重构、全仓格式化和顺手升级依赖。
- 每天至少合并一次小的可验证增量，不在阶段末进行巨型合并。
- 只有集成人解决跨模块冲突。

### 6.4 子智能体任务包模板

每个任务必须包含：

```text
目标：
基线提交：
允许修改的目录：
输入契约版本：
必须新增的测试：
验收命令：
明确不做：
交付时需要说明的风险：
```

没有这些信息的任务不应分派给子智能体。

## 7. 里程碑与并行波次

基准工期为 25 个有效开发日，约 5 周。AI 子智能体可以压缩编码时间，但不能取消契约、纵切联调和 Windows 发布验证。

### M0：工程骨架与契约冻结（第 1～2 日）

交付：

- 初始化 Tauri 2 + Vue 3 工程和 Rust workspace；
- 接入相邻 `lapp-rs`，固定开发基线；
- 建立 `pnpm verify` 或等价的一键校验；
- 定义 Rust 领域类型、生成 JSON Schema 和 TS DTO；
- 建立 canonical fixtures；
- 内置最小娜娜资源包；
- 写入首批 ADR。

演示：

- Windows 桌面窗口可以启动；
- 前端通过窄命令读取应用版本和一份静态 `PlayerView`；
- 最小娜娜资源包能够加载。

硬门槛：

- CI 可重复运行；
- 非法资源返回稳定错误；
- ID、版本、哈希、依赖和引用可校验；
- Rust、Schema、TS 不存在多个事实来源。

并行：

- 集成人：契约与仓库骨架；
- A：CI 和 Rust workspace；
- B：Fake LAPP Provider/录制模型响应夹具；
- C：按四张原型搭静态 UI 外壳。

### M1：确定性状态内核（第 3～6 日）

交付：

- `RuntimeState`；
- 纯 reducer；
- 六维关系与单轮限幅；
- 许诺生命周期；
- 知识与可见性；
- 物品 owner/holder、位置、取得方式；
- 时钟；
- 简化 CoC7 判定记录；
- `PlayerView` 投影；
- SQLite 节点、delta 和检查点。

演示：

- 不调用模型，仅通过固定命令完成：
  - 获得物品；
  - 作出许诺；
  - 改变关系；
  - 推进时钟；
  - 执行一次判定；
  - 保存、关闭、恢复。

硬门槛：

- 同一根状态 + 同一组 delta 必须产生完全相同的 `RuntimeState` 和 `PlayerView`；
- delta 重放不能重复结算；
- 关系变化会被规则限幅；
- owner/holder 语义正确；
- 玩家无法读取 NPC 未知持有物；
- 同一行动重新生成时可以复用判定记录。

并行：

- A：domain、engine、store；
- B：基于 fixture 实现 runtime 接口和 Fake Provider；
- C：前端使用同一份 `PlayerView` fixture 完成完整假数据交互。

### M2：单轮模型运行循环（第 5～10 日，与 M1 后半段重叠）

交付：

- LAPP profile 读取、模型选择和连接测试；
- 非流式与流式聊天适配；
- 上下文编译器；
- 领域工具目录；
- 工具循环和最大轮次保护；
- `PresentationBeat`；
- 暂存事务、校验、提交和回滚；
- 取消、超时、畸形输出和一次安全重试。

演示：

- 用户输入一句自由文本；
- 娜娜以短节拍回应；
- 表情发生一次可选变化；
- 生成 2～3 个可编辑建议；
- 一次需要判定的行动经过工具返回后继续叙述；
- 整轮状态一次性提交。

硬门槛：

- 模型和 UI 都不能直接写 `RuntimeState`；
- 工具失败、取消和断流不产生半轮节点；
- 模型不能替玩家决定主动言行和内心；
- 高风险行动先有风险提示；
- 凭据、完整上游错误体和隐藏状态不进入日志或前端 DTO；
- CI 使用 Fake Provider，不依赖真实付费模型。

并行：

- A：补齐 reducer 和 store 的运行时接口；
- B：实现 LAPP adapter、上下文和工具循环；
- C：把主演出屏从 fixture adapter 切到真实 Turn API。

### M3：自动存档、回溯和分支（第 8～14 日）

交付：

- 每轮原子自动保存；
- 分支头和节点父子关系；
- 从任意旧节点创建新分支；
- 旧线路保留；
- 故事恢复；
- 同输入重新生成与推骰的不同语义；
- 简洁故事时间线和完整回溯页。

演示：

- 连续玩 5 轮后退出；
- 重启恢复；
- 回到第 2 轮并继续；
- 原分支和新分支同时存在；
- 两条分支的持有物、许诺和关系状态互不污染。

硬门槛：

- 每轮只出现一次有效提交；
- 重启后的状态与提交前一致；
- 回溯不会修改旧节点；
- 重新生成复用原判定；
- 推骰创建新行动和新判定记录；
- 500 节点以内的分支恢复不需要扫描无关分支。

并行：

- A：分支、检查点、恢复和迁移；
- B：重新生成、推骰与运行时事务；
- C：故事回溯 UI 和错误恢复交互。

### M4：完整玩法系统与四个界面（第 11～18 日）

交付：

- 主游戏界面；
- 持有物/线索/许诺侧栏；
- 六维关系档案；
- 故事回溯页；
- 角色卡、世界书、Persona、剧情模块和 ItemSpec 的导入校验；
- 轻量配角；
- 场景、立绘、表情和姿态保持；
- “自由输入 / 建议 / 继续”三种操作。

演示：

- 四张界面原型对应的真实交互全部接入运行状态；
- 玩家查看自己的持有物；
- NPC 未知物品不会出现；
- 重要许诺能从 accepted 发展到 fulfilled 或 broken；
- 关系雷达变化但不显示精确数字；
- 轻量配角可以参与一段事件但不会获得完整关系系统。

硬门槛：

- 所有 UI 数据只来自 `PlayerView` 或其他脱敏 DTO；
- 不显示骰点、检定难度和精确关系增量；
- UI 不把消息和演出节拍混为一套结构；
- 静态资源加载失败有占位和明确错误；
- 导入包不能通过路径穿越写出目标目录。

并行：

- A：资源导入、规则系统和 PlayerView；
- B：世界书触发、剧情工具和轻量配角上下文；
- C：四个界面和演出状态机。

### M5：娜娜 20～30 分钟纵切（第 17～21 日）

纵切至少包含：

- 一个明确开场目标；
- 一次普通自由对话；
- 一次隐藏判定；
- 一次玩家可感知的风险提示；
- 一个被接受的重要许诺；
- 一次用行动证明或破坏许诺；
- 一件具有隐藏背景的物品；
- 一次 owner/holder 不同的物品状态；
- 一次关系路线分化；
- 一个剧情时钟；
- 一次轻量配角发言；
- 一次回溯；
- 至少两个结果方向。

硬门槛：

- 无开发者操作可以从开场玩到结果；
- 条件触发没有死路；
- 角色不泄露玩家尚未获得的知识；
- 不同选择产生可回放的不同状态；
- 引擎规则中不得硬编码 `nana` 或纵切事件 ID；
- 使用另一个最小反例资源夹具证明引擎仍然通用。

本阶段停止增加系统功能。所有子智能体只处理纵切暴露的问题。

### M6：Windows 发布前加固（第 22～25 日）

交付：

- Windows 安装包；
- 崩溃和断电恢复；
- 存档迁移夹具；
- 模型超时、断网、限流、拒绝、畸形工具调用处理；
- 长对话上下文预算测试；
- 日志脱敏；
- 内容包边界和尺寸限制；
- 用户可理解的错误页；
- 最小发布说明和已知限制。

硬门槛：

- 全新 Windows 环境能够安装、启动和卸载；
- 配置一个 LAPP 模型后能够完成纵切；
- 进程被强制关闭后不会产生半轮节点；
- 旧 Schema 存档夹具能够迁移或给出明确的不兼容提示；
- 凭据不进入前端状态、日志、崩溃报告或存档；
- 所有 canonical fixtures、单元测试和跨模块验收通过。

## 8. 合并顺序与质量门

每个并行波次按以下顺序合并：

1. 契约与 fixtures；
2. domain/engine/store；
3. runtime；
4. client adapter；
5. 跨模块 E2E；
6. 纵切内容更新。

任何合并都必须跑同一条最小跨模块场景：

```text
创建故事
→ 三轮交互
→ 一次隐藏判定
→ 获得物品
→ 建立许诺
→ 关闭并重启
→ 回溯
→ 从旧节点继续
→ 校验两个分支互不污染
```

阶段门禁：

- M1 未通过确定性重放，不接正式存档 UI。
- M2 未实现“校验后一次性提交”，不允许模型修改运行时状态。
- M3 未通过分支隔离，不开放回溯入口。
- M5 未跑通完整纵切，不扩展第二完整角色、编辑器、Workshop 或 Live2D。

## 9. 测试矩阵

| 层级 | 主要测试 |
|---|---|
| Domain | Schema、ID/引用、版本/哈希、资源依赖、反序列化错误 |
| Engine | reducer、限幅、许诺生命周期、知识可见性、物品 owner/holder、CoC7 |
| Store | 原子提交、重放、检查点、分支隔离、迁移、损坏恢复 |
| Runtime | Fake Provider、工具循环、上下文预算、取消、超时、畸形输出 |
| Security | PlayerView 泄密、日志脱敏、内容包路径穿越、凭据边界 |
| Frontend | 演出状态机、建议编辑、继续、侧栏、雷达、回溯 |
| Cross-module | 完整最小场景、重启恢复、回溯继续、真实 LAPP 冒烟 |
| Content | 条件可达性、无死路、隐藏事实、不同结果、角色一致性 |

真实模型测试不作为普通 CI 的前提。CI 使用确定性 Fake Provider；真实模型只运行独立冒烟和录制回归。

## 10. 主要风险与控制

| 风险 | 控制 |
|---|---|
| 多个子智能体各自发明 Schema | Rust 类型为唯一事实来源；契约目录独占；变更必须更新 fixtures |
| UI 或模型直接修改状态 | 所有修改必须经过类型化工具和唯一 reducer |
| 模型输出不稳定 | 工具调用、结构校验、最大循环次数、一次安全重试、失败不提交 |
| 假数据与真实接口分裂 | UI 和运行时共用 canonical `PlayerView` fixtures |
| 分支恢复不确定 | 纯 reducer；固定 delta 顺序；每次重放做哈希校验 |
| 重生成刷骰 | 判定绑定玩家行动；重新生成复用，推骰创建新行动 |
| 隐藏信息泄漏 | 后端生成 `PlayerView`；前端永远不获得完整状态 |
| `lapp-rs` 仍处于 0.1 阶段 | 固定提交；包一层窄 adapter；用三种协议夹具做回归 |
| 本地模型不支持工具调用 | V1 启动前做能力检测并明确提示；不在 V1 增加第二套自由文本解析运行时 |
| 子智能体大范围重构 | 目录所有权、短任务包、禁止无关改动、每日小合并 |
| Windows 问题最后才出现 | M0 即建立 Windows 构建；每个阶段做一次安装/启动冒烟 |
| 纵切内容被写成娜娜专用代码 | 引擎只识别通用标签和资源引用；增加非娜娜反例夹具 |

## 11. V1 Definition of Done

满足以下全部条件才称为 V1：

- Windows 安装包可用；
- 使用 LAPP 可以选择云端或本地 HTTP 模型并通过连接测试；
- 内置娜娜试玩包可直接创建故事；
- 20～30 分钟纵切可从头玩到至少两个结果方向；
- 自由输入、可编辑建议和“继续”都能稳定完成一轮；
- 六维关系、许诺、知识、持有物、时钟和隐藏判定都在纵切中真实生效；
- 每轮原子自动保存；
- 重启恢复一致；
- 回溯后建立新分支并保留旧线；
- 同一行动重新生成不重新掷骰；
- NPC 隐藏状态、精确关系值、骰点和凭据不泄漏；
- 模型取消、超时和畸形输出不产生半轮状态；
- canonical fixtures、自动测试和 Windows 冒烟全部通过；
- 没有引入 V1 明确不做的功能。

## 12. 开工顺序

第一批实际开发任务按下面顺序创建：

1. 初始化 `nana-story` 仓库、Tauri/Vue 工程和 Rust workspace；
2. 写入架构 ADR 与目录所有权；
3. 定义 `ResourceHeader`、资源绑定、`StoryNode`、`RuntimeState`、状态操作、`PresentationBeat` 和 `PlayerView`；
4. 生成 JSON Schema、TypeScript DTO 和 canonical fixtures；
5. 同时启动三个独立 worktree：
   - Core：reducer + SQLite；
   - Runtime：Fake Provider + Turn API；
   - Experience：静态主演出屏 + fixture adapter；
6. 在 M1 确定性重放通过后，接入真实 `lapp-rs` 和第一条完整游戏回合。

第一条集成目标应当非常小：

> 玩家输入“我答应天亮前回来”，娜娜回应并接受许诺；引擎创建 Promise、提高希望、保存节点；关闭应用后恢复；回溯到许诺之前继续，旧分支仍然存在。

它同时覆盖角色演出、工具调用、许诺、关系、存档、恢复和分支，是最适合作为 `nana-story` 第一条端到端主线的开发样例。
