feat(rfc-001): 回应小墨 review — runtime 注入 db/peers、migration rollback、drizzle config、schema 新鲜度

This commit is contained in:
2026-04-22 05:54:56 +00:00
parent 8a3937f0e1
commit 643c353cc3
+54 -2
View File
@@ -113,22 +113,50 @@ compute 拿到的 db 实例是 Drizzle 包装过的,查询全部 type-safe:
```typescript
// senses/cpu-usage/index.ts
import { db } from './schema'
import { samples } from './schema'
export async function compute(): Promise<number | null> {
// db 和 peers 由 engine runtime 注入,不需要用户创建
export async function compute(
db: DrizzleDB,
peers: Readonly<Record<string, DrizzleDB>>
): Promise<number | null> {
const load = os.loadavg()[0]
await db.insert(samples).values({ ts: Date.now(), value: load })
return load
}
```
**Cross-sense 类型安全读取:** compute 通过 `peers` 参数拿到其他 Sense 的只读 db 实例。类型来自 import 对方的 schema:
```typescript
// senses/active-tasks/index.ts
import { tasks } from './schema'
import { samples } from '../cpu-usage/schema' // 只导入类型定义
export async function compute(
db: DrizzleDB,
peers: Readonly<Record<string, DrizzleDB>>
): Promise<TaskSummary | null> {
// 自己的 db:读写
const activeTasks = await db.select().from(tasks).where(...)
// peer 的 db:只读,类型安全
const cpuLoad = await peers['cpu-usage'].select().from(samples).orderBy(desc(samples.ts)).limit(1)
return { activeTasks, cpuLoad }
}
```
**为什么用 Drizzle 而不是手写 SQL migration:**
Nerve 的 Sense 开发者是机器上的 Coding Agent(通过 Nerve 自身的 Workflow 自举开发)。Agent 行为的正确性应靠确定性工具保证,不应依赖概率模型的"自律"。Drizzle 让 agent 只写一个东西(`schema.ts`),migration 和类型都是机械派生,消除了 TS 与 SQL 不一致的风险。
**引擎职责:** 运行时只执行 migration SQL(`drizzle migrate`),不依赖 drizzle-kit。生成 migration 是开发时(workflow role action)的事。
**Migration rollback:** Drizzle 不支持 down migration,但 Sense 的 SQLite 是派生数据——坏了删 `.db` 文件,engine 重启时自动重跑 migration 重建。不需要 rollback 机制。
**drizzle.config:** 不需要每个 Sense 各一份。Engine runtime 统一参数化——根据 sense name 确定 `.db` 路径和 `migrations/` 目录,调用 `drizzle-kit generate` 时动态传入。Sense 开发者只写 `schema.ts`
**Schema 新鲜度:** Engine 启动时不校验 `schema.ts``migrations/` 是否同步。这是开发时的职责——创建/修改 Sense 的 Workflow 负责跑 `drizzle-kit generate` 并提交 migration。运行时只管执行已有的 migration SQL。
#### Sense 不知道 Workflow
Sense 只感知世界并产出 Signal,永远不关心"谁在听"或"听了之后要做什么"。Sense 不引用 Workflow、不返回 `ThreadStart`、不知道自己的 Signal 会触发什么动作。
@@ -307,6 +335,30 @@ systemd / pm2
worker_thread 的隔离是假的——用户代码的 `process.exit()`、native module segfault、OOM 都会杀死主进程。只有进程边界才是真正的隔离墙。
#### Worker 架构:Engine Runtime + 用户代码
Worker 不是用户代码直接跑的进程,而是 **engine 提供的 runtime 加载用户代码**。用户代码是被 import 的模块,不是入口。
```
nerve-engine (kernel)
└─ nerve worker sense --group system
├─ runtime bootstrap(engine 代码)
│ ├─ 建立 IPC
│ ├─ 读 nerve.yaml,找到 group 里有哪些 sense
│ ├─ 对每个 sense:打开 .db → 跑 migration → drizzle 包装
│ ├─ 构建 peers 只读连接
│ └─ 发 { type: 'ready' }
└─ 用户代码(被 import 进来)
├─ cpu-usage/schema.ts ← 纯类型定义
└─ cpu-usage/index.ts ← compute 函数,拿到注入的 db
```
这意味着:
- **用户代码不操心基础设施**——IPC、db 初始化、migration 都是 runtime 的事
- **compute 函数签名简单**——engine 注入 `db`(自己的)和 `peers`(只读),用户只写业务逻辑
- **隔离天然成立**——用户代码跑在 engine 控制的沙箱里,crash 只影响同 group
#### Sense Worker
Sense 按 group 分组,同 group 共享一个 worker 进程。用户决定隔离粒度。