Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
df8a49a
chore: 落地 v0 vertical slice issue 草稿(23 条 GH# 已发布)
HeDaas-Code Jun 14, 2026
6218119
docs: 添加工作Wiki(项目分析 + 设计 wiki)
Jun 14, 2026
19e9e73
fix(wiki): 修脑补内容——对齐 v0 spec 真实定义
HeDaas-Code Jun 14, 2026
08784cc
feat: 落地 v0-issue-1 仓库骨架 + 包结构 + pytest 配置
HeDaas-Code Jun 14, 2026
9ff1602
feat: 落地 v0-issue-2 AST 节点 dataclass + 错误类
HeDaas-Code Jun 14, 2026
1c47340
docs: 添 v0-issue-2 完成评论草稿
HeDaas-Code Jun 14, 2026
03fdb81
feat: 落地 v0-issue-3 命令 schema dataclass
HeDaas-Code Jun 14, 2026
9995247
feat: 落地 v0-issue-4 事件 schema dataclass
HeDaas-Code Jun 14, 2026
98ff479
feat: 落地 v0-issue-5 EngineBus 双向 Queue 封装
HeDaas-Code Jun 15, 2026
427567a
feat: 落地 v0-issue-6 neon 围栏块提取器
HeDaas-Code Jun 15, 2026
dafb110
feat: 落地 v0-issue-7 块级骨架解析
HeDaas-Code Jun 15, 2026
3930f7a
feat: 落地 v0-issue-8 元数据区解析
HeDaas-Code Jun 15, 2026
e242f31
feat: 落地 v0-issue-9 next 声明解析
HeDaas-Code Jun 15, 2026
cdaa634
feat: 落地 v0-issue-10 块内执行区解析
HeDaas-Code Jun 15, 2026
430623b
feat: 落地 v0-issue-11 node if 解析
HeDaas-Code Jun 15, 2026
17eb1b1
feat: 落地 v0-issue-12 修饰器解析
HeDaas-Code Jun 15, 2026
c9d0fe1
feat: 落地 v0-issue-13 Executor 骨架 + GameState + MemoryEventSink
HeDaas-Code Jun 15, 2026
7ff4312
feat: 落地 v0-issue-14 核心节点调度 + NEXT 跳转
HeDaas-Code Jun 15, 2026
af90762
feat: 落地 v0-issue-15 修饰器调度 + 块级作用域
HeDaas-Code Jun 15, 2026
abb67ab
feat: 落地 v0-issue-16 node if 打桩 + 跨块 ID 校验补全
HeDaas-Code Jun 15, 2026
12c2c6c
feat: 落地 v0-issue-17 core 进程入口 main.py
HeDaas-Code Jun 15, 2026
33a51ad
feat: 落地 v0-issue-18 GUI CLI 占位
HeDaas-Code Jun 15, 2026
1a76382
feat: 落地 v0-issue-19 chapter01 fixture + 端到端集成测试
HeDaas-Code Jun 15, 2026
1327ebb
feat(wiki): 对齐 v0 实测代码 + 新增 implementation-deviations
HeDaas-Code Jun 15, 2026
9f0ea8d
feat(wiki): CodeGraph 接入 + 4 条覆盖盲点
HeDaas-Code Jun 15, 2026
125f237
docs: v0-issue-20/21 HITL 验收 + ADR-0002 + audit 报告
HeDaas-Code Jun 15, 2026
c1844d9
feat(wiki): v0 闭环状态同步(22/22 closed + 182/182 测试)
HeDaas-Code Jun 15, 2026
9bdd340
docs: 重写 README 反映 v0 闭环状态
HeDaas-Code Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .codegraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ __pycache__/
.pytest_cache/
.venv/
venv/
.codegraph/
.codegraph/ # CodeGraph SQLite 索引(不在仓库内共享;每个开发者本地 init)
.obsidian/ # Obsidian 本地配置
216 changes: 216 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Neural Engine

> 中文文字游戏引擎 · v0 baseline 已完工

## 状态

| 维度 | 状态 | 证据 |
|---|---|---|
| v0 基础版引擎 | ✅ **已闭环** | v0-issue-1 ~ 19 全部落地(19 feat commit) |
| pytest | ✅ **182/182 PASSED** | `python3 -m pytest tests/ -q` |
| §8 MVP 表 | ✅ **18/18** | `tests/test_mvp_table.py` |
| §11 关键不变量 | ✅ **10/10** | `tests/test_invariants.py` |
| 端到端跑通路径 | ✅ **in → echo → end** | `tests/integration/test_echo_path.py` |
| GitHub Issues | ✅ **22/22 closed** | 含 19 AFK + 2 HITL + 1 父 issue |
| 实现偏差登记 | 4 条(owner 已接受) | [ADR-0002 §5](docs/adr/0002-v0-engine-implementation.md) |

> v0 阶段边界:引擎解析 + 核心调度 + CLI 占位 GUI 跑通;真跨进程 PyQt6 窗口(v0-issue-18 路径 A)**推迟到 v1**。

## 这是什么

一个面向中文创作者的文字游戏引擎。剧情写作者用 Markdown + 内嵌的 ` ```neon ` DSL 描述剧情节点,引擎进程解析并执行节点、广播事件给 GUI 进程,GUI 渲染文本 / 接收玩家输入。

**v0 设计核心**:
- 剧情格式 = Markdown + `neon` 代码块
- 节点 = `node start` ~ `node end` 的连续语句
- 控制流 = 隐藏变量 `NEXT` 引用 `next` 声明表项
- 跨块路由 = `id:endX:chapterYY` 块执行到 `node end` 时广播 `RouteEvt`
- 进程模型 = Engine(core 进程) + GUI(runtime 进程)双向 `multiprocessing.Queue`
- 修饰器 = `@style key:val, ...` 块内作用域、last-wins

## 快速跑通

### 安装

```bash
python3 -m pip install -r requirements-dev.txt
# 可选(v1 用):python3 -m pip install -r requirements-gui.txt
```

### 跑测

```bash
python3 -m pytest tests/ -q
# 期望:182 passed in ~9s
```

### 跑引擎(v0 CLI 占位 GUI)

```bash
# v0 入口:解析 + 调度 + CLI 占位渲染(不用 PyQt6)
python3 -m core.engine.main chapters/chapter01.md
```

GUI 进程是 `python3 -m runtime.gui.main`,由 `core.engine.main` 自动 spawn 启动;GUI 不可用时降级为 headless 模式(事件走 `EngineBus` 但无人渲染),日志里会写 `GUI not available, running headless`。

### 跑 v0 唯一端到端路径

```bash
python3 -m pytest tests/integration/test_echo_path.py -v
# 走 fixture tests/test_echo.md:in → echo → end
```

## 架构

### 双进程

```text
┌────────────────────┐ EngineBus ┌────────────────────┐
│ core/engine/main │ ◀──JSON dict──▶ │ runtime/gui/main │
│ - interpreter.py │ cmd_q / evt_q │ (v0: CLI 占位) │
│ - executor.py │ multiprocessing │ (v1: PyQt6 路径 A)│
│ - bus.py │ .Queue │ │
└────────────────────┘ └────────────────────┘
│ │
▼ ▼
chapters/*.md 章节 文本 / 输入框 / 富文本渲染
```

- 消息 schema:[`src/core/engine/protocol.py`](src/core/engine/protocol.py) — 3 条 `Cmd` + 6 条 `Evt`,全部 `@dataclass` + `to_dict` / `from_dict` JSON
- 传输层:[`src/core/engine/bus.py`](src/core/engine/bus.py) — `EngineBus` 封装 `multiprocessing.Queue`(真进程)/ `queue.Queue`(测试注入)

### 核心模块

| 模块 | 行数(实) | 职责 | 入口符号 |
|---|---|---|---|
| `src/core/engine/ast_nodes.py` | AST dataclass | 17 AST 节点 + 3 sentinels + `ParserError` | `Story`, `Block`, `Text`, `In`, `Echo`, `NextId`, `If`, `Branch`, `NextDecl`, `CallExpression` |
| `src/core/engine/protocol.py` | 进程协议 | 3 Cmd + 6 Evt 消息 dataclass | `LoadChapterCmd`, `UserInputCmd`, `TextEvt`, `PromptInputEvt`, `RouteEvt`, `ChapterEndEvt`... |
| `src/core/engine/bus.py` | 数据总线 | 双向 Queue + JSON 序列化 | `EngineBus` |
| `src/core/engine/interpreter.py` | 解析器 | 6 段流水线:fence → skeleton → meta → next → body → if | `extract_neon_blocks`, `parse_block_skeleton`, `parse_block_meta`, `parse_next_decls`, `parse_block_body`, `parse_if_stmt`, `parse_decorator` |
| `src/core/engine/executor.py` | 调度器 | 节点循环 + 修饰器 + 分支桩 + 跨块 ID 校验 | `Executor`, `GameState`, `EventSink`, `MemoryEventSink`, `MemoryInputSink` |
| `src/core/engine/main.py` | 进程入口 | 装配 + 加载 + GUI spawn + 降级 | `main()` |
| `src/runtime/gui/main.py` | GUI 入口 | v0 CLI 占位(v1 切 PyQt6) | `main()` |

> 行数 / 符号数来源:[`codegraph files`](.codegraph/) 索引(37 文件 / 470 nodes / 1566 edges)。

## 项目结构

```
.
├── src/
│ ├── core/
│ │ ├── decorators/ # 修饰器实现(@style 等,v0 解析 + 占位调度)
│ │ └── engine/ # 核心引擎 7 文件
│ │ ├── ast_nodes.py # AST dataclass
│ │ ├── bus.py # EngineBus
│ │ ├── executor.py # Executor + GameState + Sinks
│ │ ├── interpreter.py # 6 段解析流水线
│ │ ├── main.py # core 进程入口
│ │ └── protocol.py # Cmd / Evt schema
│ ├── editor/ # 剧情编辑器(v0 不实现)
│ └── runtime/
│ └── gui/ # GUI 进程(v0 CLI 占位 / v1 PyQt6)
├── tests/
│ ├── core/ # 单元测试(16 文件)
│ ├── integration/ # 端到端测试(2 文件)
│ ├── runtime/ # GUI 协议测试(1 文件)
│ ├── test_invariants.py # §11 10 条不变量守护(11 用例)
│ ├── test_mvp_table.py # §8 18 条 MVP 表逐条勾(19 用例)
│ └── test_skeleton_smoke.py # 包导入冒烟
├── chapters/
│ └── chapter01.md # v0 官方 fixture(ADR-0001 附录 A)
├── docs/
│ ├── adr/ # 架构决策记录
│ │ ├── 0001-v0-baseline-script-spec.md # v0 规范
│ │ └── 0002-v0-engine-implementation.md # v0 完工 + 4 条偏差登记
│ ├── prds/
│ │ └── 0001-v0-engine-implementation.md # v0 PRD(22 user stories)
│ ├── audit/
│ │ └── v0-invariant-audit.md # §11 + §8 验收报告
│ └── agents/ # Agent 协作说明
├── 工作Wiki/ # 项目 wiki(Obsidian vault)
│ ├── 00-index/
│ ├── 10-design/
│ ├── 20-architecture/
│ ├── 30-protocol/ # 含 implementation-deviations.md
│ └── 90-meta/
├── pyproject.toml
├── pytest.ini
├── requirements.txt
├── requirements-dev.txt
├── requirements-gui.txt
└── README.md
```

## 文档与设计

| 主题 | 路径 |
|---|---|
| v0 脚本规范 | [docs/adr/0001-v0-baseline-script-spec.md](docs/adr/0001-v0-baseline-script-spec.md) |
| v0 完工记录 + 4 条实现偏差 | [docs/adr/0002-v0-engine-implementation.md](docs/adr/0002-v0-engine-implementation.md) |
| v0 PRD(22 user stories) | [docs/prds/0001-v0-engine-implementation.md](docs/prds/0001-v0-engine-implementation.md) |
| §11 不变量审计 | [docs/audit/v0-invariant-audit.md](docs/audit/v0-invariant-audit.md) |
| 实现偏差详细分析 | [工作Wiki/30-protocol/implementation-deviations.md](工作Wiki/30-protocol/implementation-deviations.md) |
| Agent 协作约定 | [CLAUDE.md](CLAUDE.md) |
| 多上下文域文档 | [CONTEXT-MAP.md](CONTEXT-MAP.md) |

### v0 实现偏差摘要(4 条,owner 已接受)

完整登记见 ADR-0002 §5。

1. **D1-confirmed** · `decorator_state` 在 `run_block` **入口**清(不是 `End` 时清)—— owner 接受,理由是更稳妥
2. **D-NEW-1** · 分支项用 `CallExpression(kind, var)` 包装 echo/in —— owner 接受,理由是和块内 `In`/`Echo` 节点语义解耦
3. **D-NEW-2** · `ParserError.loc` 变可选 —— owner 接受
4. **D-main** · `core.engine.main` 在 GUI 不可用时降级为 headless 日志 —— owner 接受

### v0 已知未实现(推到 v1+)

- **PyQt6 GUI 窗口**(v0-issue-18 路径 A)
- **CodeGraph 4 个 internal helper 单测**(`EngineBus._drain/_close_queue`、`Executor._emit_decorator/_validate_target_ids`、`main._try_spawn_gui`)—— 当前 152 测试已间接覆盖,缺边界单测
- **GUI 跨进程联调**(v0 阶段 CLI 占位即满足 AC)
- **v0-issue-22** 待 owner 开启 v1

## 开发

### 跑单测

```bash
# 全部
python3 -m pytest tests/ -q

# 单文件
python3 -m pytest tests/core/test_executor_nodes.py -v

# 单用例
python3 -m pytest tests/test_mvp_table.py::test_mvp_18 -v

# 不变量守护(含 3 条 grep)
python3 -m pytest tests/test_invariants.py -v
```

### CodeGraph(推荐代码探索工具)

仓库已初始化 CodeGraph 索引(37 文件 / 470 nodes / 1566 edges,1.38 MB SQLite):

```bash
# 列出文件结构
codegraph files

# 看某符号的源码 + 调用者 + 被调用者
codegraph explore Executor
codegraph node EngineBus
codegraph callers run
codegraph callees parse_block_body
```

详见 [CLAUDE.md](CLAUDE.md) "Agent 工具链"节。

### 分支与 commit

- 分支:`feature/功能描述`、`fix/问题描述`、`chore/任务描述`
- Commit:中文简述,例 `feat: 添加存档系统`、`fix: 修复选项无法跳转的bug`
- 当前活跃分支:`cursor/setup-issues-v0-vertical-slices`(领先 remote 22+ commits)

## 许可

MIT(见 [pyproject.toml](pyproject.toml))
74 changes: 74 additions & 0 deletions chapters/chapter01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 第一章:雨夜

> 这是 v0 MVP 测试章节的源文件。
> 引擎 v0 实现完成后,应可正确解析并执行此文件。

本章为 v0 全语法演示章节。

```neon
id:start
# 入口块:单 next 简写,NEXT = ref(c1),直达下一节点
next:c1
node start
# 引擎开始执行——以下三行是普通文本行
雨夜。
雨声从破旧窗户的缝隙中渗入。
你坐在窗边,听着雨声。
# @style 修饰器:v0 阶段仅广播到 GUI,不真渲染
@style bgm:rain.mp3
# 等待用户输入,存到普通变量 p_mood
node in ->p_mood
# 回声:把 p_mood 推送给 GUI 显示
node echo p_mood
node end
```

```neon
id:c1
# 多 next 演示:必须带变量名,NEXT=null,待显式设置
t_a<-next:ca
t_b<-next:cb
node start
你听到门外传来两声敲门。
node in ->p_pick
# 多元条件:p_pick 是普通变量,对应 1/2
# 3 分支演示把 echo 当 next 变量名:执行 echo 节点(演示用,实际游戏不太会这么写)
node if p_pick [1:t_a,2:t_b,3:echo p_pick]
node end
```

```neon
id:ca
node start
# 切换音轨
@style bgm:storm.mp3
你打开门,雨中站着一个人。
node end
```

```neon
id:cb
node start
# 休止符:@style 裸 key 等价于清空 bgm
@style bgm
你没有开门。雨声渐小。
node end
```

```neon
id:end1:chapter02
# 路由标记:endX 后第三段是目标章节名
Comment on lines +58 to +60
# node end 时广播 route 事件,payload 含 chapter02
node start
故事停在这里。
node end
```

```neon
id:end2
# 普通结局:endX 后无章节名
# node end 时 NEXT=null,广播 chapter_end 事件
node start
测试结束。
node end
```
Loading