This repository has been archived on 2026-06-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
nerve/.github/copilot-instructions.md
T
xiaoju fc7fc9158c docs: update all docs/conventions for stateful sense, remove stale refs
Phase 4 of RFC #308: Stateful Sense refactor.

- CLAUDE.md: updated diagram, tables, examples (no more Signal)
- Cleaned stale Signal Bus / DrizzleDB / _signals / retention refs
  across READMEs, .cursor rules, copilot instructions, .knowledge
- Removed drizzle-orm from core package.json (no longer used)
- Updated pnpm-lock.yaml

Refs #308
2026-05-01 10:09:01 +00:00

5.3 KiB

Nerve Coding Conventions

Core Concepts

External World → Sense(state) → { newState, workflow? } → Workflow → Log
                  ↑                                     ↑
              "what to observe"                  "what to do"

Nerve is a lightweight observation engine daemon for autonomous agents. It continuously observes external state, reacts to changes via declarative rules, and orchestrates multi-step workflows.

Key Terms

Concept What it is
Sense A stateful compute(state) function. Returns new state and an optional workflow trigger. State persisted as JSON. Scheduling (interval, on) is configured per sense in nerve.yaml.
Workflow A stateful multi-step execution. Contains Roles (actors with side effects) and a Moderator (pure router). Each instance is a Thread with a unique runId.
Log Immutable audit trail. Records executions, state transitions, errors. Cannot trigger senses or workflows — prevents feedback loops.
Engine The kernel orchestrating everything. Holds Process Manager, Workflow Manager, Sense Scheduler. Never loads user code directly — all user code runs in isolated Workers.
Daemon The nerve-daemon package — engine runtime. Runs as a background process.

Architecture Rules

  • Two orthogonal extension points: Sense (what to observe + when), Workflow (what to do)
  • Process isolation: One worker per Sense group (long-lived), one per Workflow type (on-demand). Workers never talk to each other.
  • Causality is one-directional: External world → Sense(state) → Workflow (when triggered) + Log. Logs are the end of the chain.

Language & Paradigm

Functional-first

Use function + type, not class + interface.

// ✅ Good
type WorkflowLaunch = {
  senseName: string;
  workflowName: string;
  ts: number;
};

function recordWorkflowLaunch(senseName: string, workflowName: string): WorkflowLaunch {
  return { senseName, workflowName, ts: Date.now() };
}

// ❌ Bad — no class, no interface
class WorkflowLaunch implements IWorkflowLaunch { ... }

Rules

Rule Description
type over interface All type definitions use type
function over class Pure functions + closures, no class
No this Functions must not depend on this context
No inheritance No extends, implements, abstract
Composition over inheritance Use function composition
Immutability first Use Readonly<T>, as const, avoid mutation
No optional properties Use T | null instead of ?: — see below

Exceptions

Classes are allowed when:

  • Required by a third-party library (e.g. Drizzle's sqliteTable)
  • Error subclasses (class NerveError extends Error)

No Optional Properties

Never use ?:. All nullable fields must be explicit T | null.

// ✅ Good
type SenseConfig = {
  group: string;
  throttle: string | null;
  timeout: string | null;
};

// ❌ Bad
type SenseConfig = {
  group: string;
  throttle?: string;
  timeout?: string;
};

For mutually exclusive fields, use discriminated unions:

// ✅ Good
type WorkflowConfig =
  | { concurrency: number; overflow: "drop" }
  | { concurrency: number; overflow: "queue"; maxQueue: number };

Modules & Exports

  • Always named exports, never default exports
  • One module = one responsibility, filename = purpose

Naming

Type Style Example
Files kebab-case sense-scheduler.ts
Types PascalCase SenseScheduler
Functions/variables camelCase createSenseScheduler
Constants UPPER_SNAKE MAX_RETRY_COUNT
Generics Single letter or descriptive T, TValue

Error Handling

  • Use Result type for expected failures
  • throw only for unrecoverable bugs (programmer errors)
  • No try-catch for flow control
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };

Async

  • Always async/await, never .then() chains

No Dynamic Import

Do NOT use await import() in production code. Always use static top-level import.

Exceptions (must include a comment):

  1. sense-runtime.ts — user module paths known only at runtime
  2. workflow-worker.ts — user module paths known only at runtime

Test files (__tests__/**) are exempt.

Toolchain

Tool Purpose
pnpm Package manager
TypeScript Type checking (strict mode)
Biome Lint + format (replaces ESLint + Prettier)
tsup Bundling

Commands

pnpm run check      # biome check (lint + format)
pnpm run format     # biome format --write
pnpm run build      # full build
pnpm test           # run tests

Monorepo Structure

nerve/
  packages/
    core/           # @nerve/core — shared types and utils
    cli/            # @nerve/cli — CLI entry point
    daemon/         # @nerve/daemon — engine runtime
  docs/             # RFCs, conventions
  • core is the shared layer; cli and daemon both depend on it
  • cli and daemon must NOT depend on each other

Commit Convention

<type>(<scope>): <description>

type: feat | fix | refactor | docs | chore | test
scope: core | cli | daemon | rfc-001 | ...