# Sense A `compute()` function that samples or derives external data. The only first-class citizen in nerve. ## Contract Each sense module (`src/index.ts`) must export: ```ts export { snapshots as table } from "./schema.ts"; // drizzle table for runtime to insert into export async function compute(): Promise> { ... } // pure, no args ``` **Function Signature & Input Schema:** - `compute()` is **parameterless** — no direct inputs, environment variables available - No database access within compute — runtime provides isolated execution context - Must be pure function (no side effects, no external API calls) **Return Value Contract (current engine):** - `compute(state)` returns `Promise<{ state: S; trigger: SenseTrigger | null }>` where `SenseTrigger = { command: string }`. - `trigger: null` → persist state only; no shell command - `trigger: { command }` → persist state; worker runs the command with `shell: true` after a successful compute - Workflows are **not** started from `trigger`; use CLI / daemon IPC (`nerve workflow trigger`, etc.). **Error Handling & Serialization:** - Exceptions caught by worker, logged as errors (state unchanged) - State must be JSON-serializable (persisted to `data/senses/.json`) - Invalid `trigger` shapes fail IPC validation when the worker sends `compute-result` **Timeout & Scheduling Semantics:** - Timeout priority: explicit config → AbortSignal → DEFAULT_TIMEOUT_MS (30s) - Enforced via `Promise.race()` with timeout promise - Grace period can trigger `process.exit(1)` after timeout (kills worker group) - Interval translation: YAML config values used directly as milliseconds in `setInterval()` - Jitter control: throttle mechanism prevents rapid-fire, single deferred trigger per throttle window ## Config (nerve.yaml) ```yaml senses: cpu-usage: group: system # senses in same group share a worker throttle: 10s # min interval between computes timeout: 30s # max compute duration grace_period: 5s # wait before first compute interval: 30s # periodic trigger (optional) on: [disk-pressure] # trigger on signals from other senses (optional) ``` ## Manual Trigger Context **`nerve sense trigger `** sends IPC message to running daemon. The compute context is initialized as follows: - **SQLite Database**: Opened in **read-write mode** at `data/senses/.db` - **Migrations**: All `*.sql` files in `senses//migrations/` applied in lexicographic order - **Environment**: Inherits daemon process environment (no special secrets injection) - **Arguments**: No runtime arguments or mock inputs supported — `compute()` is always pure function with no parameters - **Isolation**: Runs in forked child process (worker) with full filesystem access within user permissions - **Persistence**: Runtime automatically calls `db.insert(table).values(result.signal)` if compute returns non-null signal