From 208447d3e5dda291a5633e3c9e5fdf7cc5ce2f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Sun, 19 Apr 2026 03:39:17 +0000 Subject: [PATCH] docs: engine spec + test cases documentation (#3) --- docs/engine-spec.md | 85 ++++++++++++++++++++++++ docs/test-cases.md | 154 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 docs/engine-spec.md create mode 100644 docs/test-cases.md diff --git a/docs/engine-spec.md b/docs/engine-spec.md new file mode 100644 index 0000000..6f00bbf --- /dev/null +++ b/docs/engine-spec.md @@ -0,0 +1,85 @@ +# Pulse Engine 目录规范 + +> `~/.upulse/engine/` — Pulse 的"用户态"代码空间,独立 git 仓库。 + +## 架构 + +``` +~/.upulse/engine/ # 独立 git repo +├── package.json # @upulse/engine, 依赖 @uncaged/pulse +├── tsconfig.json +├── templates/ # Scaffold 模板(meta coder 参考) +│ ├── workflow.ts.tmpl +│ └── workflow.test.ts.tmpl +├── src/ +│ └── workflows/ +│ ├── ping-pong.ts # 示例 workflow +│ ├── ping-pong.test.ts +│ ├── werewolf.ts # 业务 workflow +│ ├── werewolf.test.ts +│ └── roles/ +│ ├── meta-gate.ts # Gate: 准入检查(无 LLM) +│ ├── meta-gate.test.ts +│ ├── meta-coder-cursor.ts # Coder: Cursor Agent 写代码 +│ ├── meta-checker.ts # Checker: 验证 scope + build + unit test +│ ├── meta-tester.ts # Tester: E2E lifecycle 验证 +│ └── meta-promoter.ts # Promoter: commit + push + code_rev +└── docs/ +``` + +## 核心概念 + +### Engine vs 核心包 + +| | Engine (`~/.upulse/engine/`) | 核心包 (`packages/pulse/`) | +|---|---|---| +| 谁改 | Meta workflow(agent 自主改) | 人工 / subagent | +| 内容 | 业务 workflow + meta roles | 框架代码 | +| 版本控制 | engine repo git hash = `code_rev` | pulse repo | +| 部署 | promote event → daemon 热加载 | npm publish | + +### code_rev + +Engine repo 的 **git commit hash** 就是 `code_rev`。 + +- 每次 promoter commit 后,commit hash 成为新的 `code_rev` +- Guard state 按 `code_rev` 隔离(同一 guard + 同一 key,不同版本有独立 state) +- 回滚 = 切回旧 `code_rev`,旧 state 还在 + +### Meta Workflow 流程 + +``` +START → gate → coder → checker → tester → promoter → END + ↑ | | + └── fail ──┘── fail ──┘ +``` + +| Role | 职责 | LLM | +|------|------|-----| +| **gate** | 准入检查:任务描述够不够详细、是否在 engine scope | ❌ | +| **coder** | 用 Cursor Agent 写代码,参考 templates/ | ✅ Agent | +| **checker** | 验证文件 scope + `bun run build` + `bun test` | ❌ | +| **tester** | E2E lifecycle 验证:import → temp store → tick → quiescence | ❌ | +| **promoter** | `git commit` + `git push` + 输出 `codeRev` | ❌ | + +### Daemon 加载机制 + +1. Daemon 启动时 `tryLoadFromEngine`,优先加载 engine 目录下的 workflow/role +2. Engine 文件 import 用 `@uncaged/pulse`(npm link 到核心包) +3. Promote event 触发 daemon 重新加载 + +## 开发新 Workflow + +1. 参考 `templates/workflow.ts.tmpl` 创建 `src/workflows/{name}.ts` +2. 参考 `templates/workflow.test.ts.tmpl` 创建配套测试 +3. `bun test` 确认通过 +4. Git commit + push + +或者通过 meta workflow 自动生成:向 daemon 提交任务 → gate → coder → checker → tester → promoter → 自动完成。 + +## 约束 + +- Workflow 文件只放 `src/workflows/` 下 +- Meta role 文件只放 `src/workflows/roles/` 下 +- Import 用 `@uncaged/pulse`,不用相对路径到核心包 +- Commit author: `小橘 ` diff --git a/docs/test-cases.md b/docs/test-cases.md new file mode 100644 index 0000000..1e5c44c --- /dev/null +++ b/docs/test-cases.md @@ -0,0 +1,154 @@ +# Pulse 测试用例文档 + +> 所有已验证的测试场景,含 unit test 和 e2e 场景描述。 + +## 测试统计 + +- **核心包** (`packages/pulse/src/`): 350 tests +- **Engine** (`~/.upulse/engine/`): 14 tests +- **总计**: 364 tests, 0 fail + +--- + +## 一、Guard 系统 + +### 1.1 Guard 注册与 State 管理 + +| 场景 | 描述 | 文件 | +|------|------|------| +| 注册 guard 写入 guard_defs 表 | `registerGuard` 持久化定义到 SQLite | `guard-projection.test.ts` | +| 从表读 guard 定义 | `listGuardDefs` 纯从 `guard_defs` 表读,不依赖内存缓存 | `guard-projection.test.ts` | +| Guard state 初始值 | 首次 check 时 state 从 `initial_value` 开始 | `guard-projection.test.ts` | +| Guard state 转换 | check 通过后 transition 更新 state | `guard-projection.test.ts` | +| Guard 拒绝非法事件 | check 不通过时抛出 `GuardViolationError` | `guard-projection.test.ts` | + +### 1.2 Guard 版本化(code_rev 维度) + +| 场景 | 描述 | 文件 | +|------|------|------| +| 同 guard 不同 code_rev 独立 state | `(guard_name, key, code_rev)` 三元组主键,不同版本互不干扰 | `guard-projection.test.ts` | +| 新版本从 initial_value 开始 | code_rev 切换后,state 不存在 → 从 initial_value 重建 | `guard-projection.test.ts` | +| 回滚续写旧 state | 切回旧 code_rev 时,旧 state 还在,从 last_event_id 增量 | `guard-projection.test.ts` | + +### 1.3 GuardProjection(pulse#9) + +| 场景 | 描述 | 文件 | +|------|------|------| +| 子 Guard 过滤 | subagent 注册的 guard 只在该 subagent 上下文生效 | `guard-projection.test.ts` | +| Guard 表达式缓存 | JSONata 编译结果缓存,第二次 check 不重新编译 | `guard-projection.test.ts` | + +--- + +## 二、Workflow 核心(Adapter) + +### 2.1 基本生命周期 + +| 场景 | 描述 | 文件 | +|------|------|------| +| START → roles → END | `__start__` event 触发 moderator 状态机,依次执行 role,写 `__end__` | `workflow-rule-adapter.test.ts` | +| Role 输出存 CAS | role 的 content 存入 objects/,event 只存 hash | `workflow-rule-adapter.test.ts` | +| Meta 存入 event | role 的 meta 作为 JSON 存入 event.meta 字段 | `workflow-rule-adapter.test.ts` | +| Moderator 路由 | moderator 根据 role + meta 决定下一步 | `workflow-rule-adapter.test.ts` | +| 多 topic 并行 | 不同 key 的 workflow 独立运行,互不干扰 | `workflow-rule-adapter.test.ts` | + +### 2.2 Lifecycle Guard + +| 场景 | 描述 | 文件 | +|------|------|------| +| 正常生命周期 | unknown → active(start)→ ended(end)| `workflow-rule-adapter.test.ts` | +| Abort 阻止重启 | aborted 状态的 topic 不能再 `__start__` | `workflow-rule-adapter.test.ts` | +| 双重保护 | event-based lastRole 检查(第一层)+ guard state(第二层)| `workflow-rule-adapter.test.ts` | + +### 2.3 Abort(pulse#10) + +| 场景 | 描述 | 文件 | +|------|------|------| +| Abort 写 `__abort__` event | abort 后 moderator 不再调度该 topic | `workflow-rule-adapter.test.ts` | +| Abort 后不能 end | `__abort__` 后写 `__end__` 被 guard 拒绝 | `workflow-rule-adapter.test.ts` | +| Abort 后不能重启 | 同一 key 再次 `__start__` 被 guard 拒绝 | `workflow-rule-adapter.test.ts` | + +### 2.4 Chain 过滤(信息防护) + +| 场景 | 描述 | 文件 | +|------|------|------| +| Role 只看同 topic 消息 | chain 按 key 过滤,不同 topic 的消息不可见 | `workflow-rule-adapter.test.ts` | +| Role 只看已完成消息 | 当前 role 不看自己的 event(防幻觉) | `workflow-rule-adapter.test.ts` | + +--- + +## 三、Gate Role(pulse#11) + +| 场景 | 描述 | 文件 | +|------|------|------| +| 任务描述足够通过 | ≥20 字符、有明确动词 → pass | `meta-gate.test.ts` | +| 空/短描述拒绝 | 不足 20 字符 → reject | `meta-gate.test.ts` | +| 核心包任务拒绝 | 提到 `packages/pulse/src` 等核心路径 → reject | `meta-gate.test.ts` | + +--- + +## 四、Meta Workflow E2E + +### 4.1 成功场景 + +| 场景 | 描述 | 事件链 | +|------|------|--------| +| 完整 happy path | gate pass → coder → checker pass → tester pass → promoter → END | Event #90→#98→#100→#101→#102→#103 | + +### 4.2 失败场景 + +| 场景 | 描述 | 事件链 | +|------|------|--------| +| Checker 失败回退 coder | 文件 scope 违规或 build 失败 → 回退 coder 重试 | Event #88→#89 | +| Tester 失败回退 coder | E2E lifecycle 未到 END → 回退 coder 重试 | — | + +### 4.3 Abort 场景 + +| 场景 | 描述 | 事件链 | +|------|------|--------| +| 手动 abort | 写 `meta.__abort__` 终止 workflow | Event #104→#105 | + +### 4.4 Gate 拒绝场景 + +| 场景 | 描述 | 事件链 | +|------|------|--------| +| 不合格任务 | 描述过短或涉及核心包 → gate reject → END | — | + +--- + +## 五、Daemon 容错 + +| 场景 | 描述 | 文件 | +|------|------|------| +| GuardViolationError 不 crash | guard 拒绝时 warn 日志,daemon 继续运行 | `workflow-daemon.ts` | +| Tick 异常不 crash | 其他异常也 catch + 日志,不停 daemon | `workflow-daemon.ts` | + +--- + +## 六、Engine Workflow(`~/.upulse/engine/`) + +### 6.1 ping-pong + +| 场景 | 描述 | 文件 | +|------|------|------| +| START → pong → END | 最简 workflow,验证 engine 加载机制 | `ping-pong.test.ts` | + +### 6.2 werewolf + +| 场景 | 描述 | 文件 | +|------|------|------| +| 狼人杀多角色交互 | 多 role 状态机验证 | `werewolf.test.ts` | + +--- + +## 七、Checker 增强 + +| 场景 | 描述 | 文件 | +|------|------|------| +| 文件 scope 验证 | `git diff --name-only HEAD` 只允许 engine 目录改动 | `meta-checker.ts` | +| Build 验证 | `bun run build`(tsc --noEmit)通过 | `meta-checker.ts` | +| Unit test 验证 | `bun test` 通过 | `meta-checker.ts` | +| 实际改动 vs 声称改动 | 对比 coder meta.filesChanged 与 git diff | `meta-checker.ts` | + +--- + +_最后更新: 2026-04-19 — 小橘 🍊(NEKO Team)_