docs: update all README files to match actual code

Rewrite documentation across all packages to reflect current
architecture, APIs, and CLI commands.

- README.md: fix reflex examples, add store package, update config
- core/README.md: add Sense→workflow routing, IPC types
- daemon/README.md: complete module table, crash recovery, createKernel
- cli/README.md: add workflow/sense/store subcommands
- store/README.md: new file documenting LogStore/BlobStore

Fixes #95
This commit is contained in:
2026-04-24 21:47:37 +00:00
parent d4dcd9722f
commit a7e6caf6e7
5 changed files with 230 additions and 79 deletions
+68 -44
View File
@@ -7,28 +7,31 @@ Nerve is a lightweight daemon that continuously observes external state through
## Core Concepts
```
External World → Sense → Signal → Reflex → Workflow → Log
↑ ↑
"what to observe" "what to do"
External World → Sense ─┬→ Signal → Reflex → Sense (scheduled compute)
└→ Workflow (Sense return with workflow directive) → Log
```
| Concept | Metaphor | Role |
|---------|----------|------|
| **Sense** | 👁️ Perception | A `compute()` function that samples or derives data. Each sense has its own SQLite database. |
| **Reflex** | ⚡ Reaction | Declarative trigger — interval-based, event-driven, or both. Connects senses to actions. |
| **Signal** | 📡 Notification | Emitted when a sense returns non-null. Other reflexes can listen for signals. |
| **Workflow** | 🔧 Action | Stateful multi-step execution with Roles (actors) and a Moderator (coordinator). |
| **Reflex** | ⚡ Reaction | Declarative rules that **only schedule Sense computes** (interval and/or `on` signal names). Reflex YAML cannot reference workflows. |
| **Signal** | 📡 Notification | Emitted when a sense returns a non-null value that is routed as a normal signal (see Sense → Workflow below). Other reflexes can listen via `on`. |
| **Workflow** | 🔧 Action | Stateful multi-step execution with Roles and a Moderator. Started from a Sense return value or from CLI/daemon IPC—not from reflex YAML. |
| **Log** | 📝 Record | Immutable audit trail. Queryable by senses, but **cannot** trigger reflexes (prevents feedback loops). |
Three extension points, fully orthogonal — a Sense doesn't know when it runs, a Reflex doesn't know what it computes, a Workflow doesn't know why it was triggered.
**Sense → Workflow:** if `compute()` returns a plain object with a string field `workflow` in the form `name|maxRounds|prompt` (only the first two `|` delimit name and rounds; the rest is the prompt), the engine starts that workflow and **does not** emit a Signal for that return. `workflow: null` or `""` means “emit a signal” and strip the key from the payload. Invalid `workflow` strings are treated like a normal signal (directive stripped). See `@uncaged/nerve-core` `routeSenseComputeOutput` / `parseSenseWorkflowDirective`.
Three extension points for **what / when / multi-step action** — reflexes never replace Sense-driven workflow launches.
## Packages
| Package | Description |
|---------|-------------|
| [`@uncaged/nerve-core`](./packages/core) | Shared types and config parser |
| [`@uncaged/nerve-daemon`](./packages/daemon) | The observation engine — kernel, sense runtime, reflex scheduler, workflow manager |
| [`@uncaged/nerve-cli`](./packages/cli) | CLI tool (`nerve`) — init, start, stop, logs, query |
| [`@uncaged/nerve-core`](./packages/core) | Shared types, config parser, Sense→workflow routing, daemon IPC protocol |
| [`@uncaged/nerve-store`](./packages/store) | Append-only log SQLite, JSONL archive, CAS blob store, workflow run rows |
| [`@uncaged/nerve-daemon`](./packages/daemon) | Kernel, workers, signal bus, reflex scheduler, workflow manager, file watcher, IPC |
| [`@uncaged/nerve-cli`](./packages/cli) | CLI (`nerve`) — init, validate, daemon, dev, logs, sense, store, workflow |
## Quick Start
@@ -70,15 +73,17 @@ nerve logs # view logs
## Configuration
`nerve.yaml` declares senses, reflexes, and workflows:
`nerve.yaml` declares senses, reflexes (sense-only), optional workflows (concurrency), and optional engine `max_rounds`:
```yaml
max_rounds: 100 # default moderator cap (e.g. CLI workflow trigger)
senses:
cpu-usage:
group: system # senses in the same group share a worker process
throttle: 10s # min interval between computes
timeout: 30s # max compute duration
gracePeriod: 5s # wait before first compute after startup
grace_period: 5s # wait before first compute after startup
reflexes:
- kind: sense
@@ -86,10 +91,6 @@ reflexes:
interval: 30s # periodic trigger
on: [disk-pressure] # also trigger on signals from other senses
- kind: workflow
workflow: cleanup
on: [disk-pressure] # start a workflow when signal fires
workflows:
cleanup:
concurrency: 1
@@ -97,43 +98,66 @@ workflows:
code-review:
concurrency: 3
overflow: queue
maxQueue: 20
max_queue: 20
```
YAML must **not** include `workflow:` under `reflexes` — the parser rejects it. Declare workflows under `workflows:` and start them from Sense `compute()` or `nerve workflow trigger`.
**Example — Sense starts a workflow** (`senses/disk-pressure/compute.ts`):
```typescript
export async function compute() {
const full = await diskNearlyFull();
if (!full) return null;
return {
path: "/data",
workflow: "cleanup|10|Disk partition nearly full", // name|maxRounds|prompt
};
}
```
## Architecture
```
┌─────────────────────────────────────────────────────────┐
│ Kernel │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ Worker │ │ Worker │ │ Worker (1 per
│ (group A)│ │ (group B)│ │ (group C)│ group)
│ sense-1 │ │ sense-3 │ │ sense-5 │ │
│ │ sense-2 │ │ sense-4 │ │ │ │
│ └─────────┘ └────┬─────┘ └────┬─────┘
└──────────────┼──────────────┘
┌──────────────┐
│ Signal Bus │
│ └─────────────┘
──────────────────
│ Reflex Scheduler │
└────────┬─────────
┌───────────────────
Workflow Manager │──→ Log Store (SQLite)
└───────────────────
└─────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────
│ Kernel
│ ┌──────────────┐ watches nerve.yaml / senses / workflows
│ │ File Watcher ├──────────────────────────────────────────┐
└──────────────┘ │
┌──────────────┐ CLI ↔ newline JSON (trigger-workflow, │ │
│ │ Daemon IPC │ trigger-sense, list-senses) │ │
│ └──────┬───────┘
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ Worker │ │ Worker │ │ Worker │ (1 per
│ (group A)│ │ (group B)│ │ (group C)│ group)
│ sense-1 │ sense-3 │ sense-5 │
│ │ sense-2 │ │ sense-4 │ │
└────┬─────┘ └─────────└────┬─────┘
│ │
└────────────────────────────┘
│ ┌──────────────┐
│ Signal Bus │
│ └─────────────
│ │
│ ┌──────────────────
│ │ │ Reflex Scheduler│ │
│ │ └────────┬─────────┘ │
│ │ ▼ │
│ │ ┌───────────────────┐ │
│ └───────────────────►│ Workflow Manager │──→ @uncaged/nerve-store │
│ └───────────────────┘ (logs.db, …) │
└────────────────────────────────────────────────────────────────────────┘
```
- **Worker processes** — one per sense group, forked by the kernel. Isolated compute execution.
- **Worker pool** — one child process per sense group; isolation between groups.
- **Signal Bus** — in-memory pub/sub for signal distribution.
- **Reflex Scheduler** — interval timers + signal subscriptions, with throttle/coalesce.
- **Workflow Manager** — concurrency control (drop/queue), thread lifecycle tracking.
- **Log Store** — WAL-mode SQLite via `node:sqlite`, with archival and retention policies.
- **Workflow Manager** — concurrency (drop/queue), per-workflow workers, crash recovery.
- **File watcher** — hot reload for config, sense modules, and workflow modules.
- **Daemon IPC** — Unix domain socket; used by the CLI when the daemon is running.
- **Log / blob storage** — implemented in `@uncaged/nerve-store` (WAL SQLite, JSONL archive, CAS blobs).
## Tech Stack