Worker mode deploys Nerve as a task-focused autonomous agent. Give it a job description, and it researches the domain, creates its own skills and cron jobs, then operates in a plan-approve-execute loop — all with human oversight.
How to run a worker well —
TASK.mdanatomy and hard rules, plan-approve-execute discipline, the task state machine, memory tiers and running several workers — is covered in Worker Best Practices.
# Clone and install
git clone https://github.com/ClickHouse/nerve.git nerve
cd nerve
uv sync
source .venv/bin/activate
# Decide how Nerve runs here, start it, get the setup page and token
nerve initnerve init asks only about this machine (server or Docker, paths, port and
bind address), starts Nerve and prints the setup page with a setup token. The
worker is configured on that page:
- Claim — your username and password (or keep it passwordless), plus the token
- Mode: choose Worker, and tick what it should do: review pull
requests, triage issues, fix issues and open pull requests, keep CI
healthy, dependency and security updates, first response to alerts (needs
an alert source; a Slack channel is the first-class one today), channel
digests and answers, research and reports, docs and release notes, or
Custom… in your own words; optionally add anything it must know or
never do. All of it is optional: the conversation sorts out the details.
This writes the worker templates (
SOUL.md,AGENTS.md,TOOLS.md,MEMORY.md),TASK.mdfrom its template with your choice in place (the onboarding conversation fills the rest), andconfig/cron/system.yamlwithskill-extractor,skill-reviserandmachine-maintenanceon andtask-planneroff (memory-maintenanceis always on; the onboarding conversation turnstask-planneron when the worker should plan through the approval loop) - Model — an Anthropic API key, a Claude subscription token, the Claude Code login found on the server, or AWS Bedrock; test it, pick a model
- Operator — who runs this worker, in which time zone it should plan its day, and quiet hours if it should keep any (off for a worker by default); Memory, Channels and Automation are optional and can be skipped. Sources and external agents are not offered to a worker
- Review and finish — the server check-up, then Restart Nerve (the mode's memory settings are read at startup) or Finish. The ending reads "Your worker is ready" with Start the conversation, which opens the onboarding conversation
The same choice from a terminal is nerve init --terminal, which asks for the
mode and an optional task description itself and does not start the daemon
(nerve start afterwards). Its worker path has no cron step, so it writes
skill-extractor, skill-reviser and machine-maintenance with enabled: false too (task-planner is
off on every path until onboarding enables it): set enabled: true in
config/cron/system.yaml (or override the job by id in jobs.yaml) and nerve reload. Unattended installs set NERVE_MODE=worker and
NERVE_TASK="…" for nerve init --non-interactive (see
Unattended installation). On these two
paths, a worker with a task description sets itself up without a person at its
first start (see Without the browser); without one, it
waits for its onboarding conversation in the web UI.
After setup in the browser, you set the worker up by talking to it, in the onboarding conversation (see Setup → The onboarding conversation). The setup page's Finish opens it, or the next start of the daemon when a restart comes first. When setup is complete and nobody pressed Finish, the banner in the web UI offers to start it. Until onboarding is done, TASK.md keeps the onboarding marker (the <!-- nerve:onboarding pending … --> comment on its second line). The agent's hidden instruction holds what you chose on the Mode step (each ticked item with what it does, the connection it needs and the follow-up questions it raises), the full Worker Best Practices guide and the rules below. The agent speaks first: with work chosen it offers no menu, restates the work in one sentence, says what it studies and sketches the setup it has in mind before it asks anything; with nothing ticked, or only rules, it shows one form that asks which work applies, with four common kinds of work as checkboxes and Other… for work in your own words. A free-text note (from an older setup page or nerve init --terminal) that describes the work counts as chosen work; one with only rules or only repositories gets the form. Then:
- Explore and ask: one background explorer clones the repositories into
<workspace>/repos/and reads them, without installing, building or running tests (build and test, CI, contribution and review rules, the volume of the work, risks and missing access); once its findings are in, the agent asks you, in question cards with its recommendation first, only what neither the explorer nor setup can tell (a first card asks only for missing repositories or channels, so the explorer can start). It works from the follow-up questions of each item you ticked (which repositories, what a review may do, the label vocabulary, and so on) and asks once what the items share: the account it acts as on the code host, what waits for your approval, where results go. What does not fit into two rounds becomes a default in the proposal; your rules ("Anything it must know or never do?") become hard rules and approval boundaries - Propose the setup as a plan: cron jobs with schedule and
run_ifgate, whethertask-plannerplans through the approval loop (it ships disabled for workers; the agent turns it on by overriding the job by id injobs.yaml, with atasksgate scoped to the worker's tag), task statuses (the built-inbacklogandplan_review, plus a status for each other kind of wait), tags, skills, theTASK.mdsections, what it needs from you (including a connection a chosen item needs that the instance lacks) and the approval boundaries. Apply setup approves it, Edit lets you change the text and approve your version, and Change something… sends your words back and the agent revises the plan. It is instructed to change nothing before you approve - Apply what you approved: fills
TASK.mdin place (every_(onboarding fills this in: …)_placeholder and example line, keeping the headings and what you chose, the### ⛔ HARD RULE: … [operator directive YYYY-MM-DD]shape for the non-negotiables, the Actions transition table, and the closing "Read the full guide" link), adds the jobs to<workspace>/config/cron/jobs.yaml, creates statuses, skills and the tasks only you can do, and saves what it learned to memory - Summarize: lists every change and the next steps for you
- Mark onboarding done: calls
onboarding_done, which also removes the marker line fromTASK.md
Until onboarding is done or skipped, a banner in the web UI sends you back to the conversation. A TASK.md from an older install (the raw # Task form, no marker) is rewritten into the template's shape with its text under "Task description (as given)" when the conversation opens; such a file counts as onboarded once it has a ## Mission heading. A TASK.md without its marker counts as onboarded.
A worker set up by nerve init --terminal or nerve init --non-interactive with a task description sets itself up without a person, once, at the first start of the daemon with a model credential (see Setup → A worker set up without the browser). One session gets your description, the full worker guide and these steps:
- Research: fetches what the description names, clones the repositories, and looks at the CI systems and APIs involved
- Fill
TASK.mdin place, to the same rules as above - Create skills for the procedures the worker repeats
- Configure cron jobs in
jobs.yaml, with gates, and decide ontask-planner - Create initial tasks for what only you can do (credentials, access, open decisions)
- Walk through every change: saves what it learned to memory, then notifies you (priority
high) with every change and numbered next steps - Mark onboarding done: calls
onboarding_done, which removes the marker line fromTASK.md
While the session runs, the banner in the web UI links to it. With an empty task description, the worker waits for its onboarding conversation in the web UI instead.
After onboarding, the worker operates on its configured schedule. You can change the setup at any time by asking it in a chat.
The task file lives at workspace/TASK.md and is injected into every session's system prompt. It is the worker's mission brief. What each section holds, and how to write rules that hold up, is in Worker Best Practices → TASK.md Anatomy.
Before onboarding. The Mode step, nerve init --terminal and nerve init --non-interactive write the worker template with what you chose or described, if anything, in place. The Mode step writes its choice as one comment line Nerve reads (the ticked items' ids) and one line each for people, each left out when empty: Work: (the ticked items), Also: (your own words) and Must know or never do:; the terminal paths write your task description as a plain paragraph. The comment on the second line marks the file as not onboarded yet. The example lines the template carries under each heading are left out here:
# Task
<!-- nerve:onboarding pending: the onboarding conversation fills this file in; this line goes when onboarding is done. -->
## Task description (as given)
<!-- nerve:work ci-health -->
Work: Keep CI healthy
Also: Hunt down flaky tests: when a test fails intermittently, investigate the root cause and propose a fix.
Must know or never do: Never touch PRs or issues you did not open unless addressed.
## Identity
_(onboarding fills this in: which account acts, exact git author, fork/remote names)_
## Mission
_(onboarding fills this in: one paragraph ending in the quality bar)_
## Capabilities
_(onboarding fills this in: what this worker is expected to be able to do, and what it may never dismiss; at most 10 lines)_
## Scope
_(onboarding fills this in: repos, CI, data sources, and the one exhaustive reference doc or skill; pointers, no credentials)_
## Hard rules
_(onboarding fills this in: the non-negotiables, each its own heading with a marker and provenance; as few as you can defend)_
## Triggers
_(onboarding fills this in: what creates work, one line each: scan crons, inbox and mentions, human directives)_
## Actions
_(onboarding fills this in: the status state machine as a transition table, then an anti-patterns list)_
| From | Event | To | Who holds the ball |
|---|---|---|---|
## Approval Required
_(onboarding fills this in: what needs a human, what is autonomous, where decisions are recorded)_
## References
_(onboarding fills this in: endpoints with auth mode (never passwords), schema columns, canonical filters, label vocabulary; the long form lives in `TOOLS.md`)_
---
Read the full guide: https://github.com/ClickHouse/nerve/blob/main/docs/worker-best-practices.mdAfter onboarding. The onboarding conversation has filled every section in place and kept the headings and the description, and onboarding_done has deleted the marker line:
# Task
## Task description (as given)
<!-- nerve:work ci-health -->
Work: Keep CI healthy
Also: Hunt down flaky tests: when a test fails intermittently, investigate the root cause and propose a fix.
Must know or never do: Never touch PRs or issues you did not open unless addressed.
## Identity
You are `ci-bot`. Git author `CI Bot <ci-bot@example.com>`; fork `ci-bot/repo-a`
(remote `fork`); upstream `org/repo-a` (remote `origin`). Every public action
(commit, push, comment) uses this account and no other.
## Mission
Watch CI on `org/repo-a`, find flaky tests and regressions, fix them at the
source, and tell maintainers what they need to know. Fewer, better PRs beat
many quick ones.
## Capabilities
You are a developer on `repo-a` with the full source, a warm debug build
(`skill_get build-and-test`) and read access to the failure database. Do not
dismiss a failure as too deep, "infra noise" or someone else's code. If a tool
is missing, name it, then install it or flag the gap.
## Scope
- Repository: `org/repo-a` on GitHub · CI: GitHub Actions · tests: pytest
- Failure DB: endpoint and auth mode in `TOOLS.md` § Failure DB (read-only user)
- CI internals: `docs/ci-internals.md` in the repo, or the `ci-db-query` skill
## Hard rules
### ⛔ HARD RULE: builds and tests run in the FOREGROUND as ONE call [operator directive 2026-03-02]
One synchronous Bash call with a `timeout` above the expected duration that
captures the producer's exit code: `make test > build.log 2>&1; rc=$?;
tail -50 build.log; echo RC=$rc`. Never `$?` after a pipe; never
background-and-exit.
### ⛔ HARD RULE: never touch PRs or issues you did not open unless addressed [operator directive 2026-03-02]
Being right does not make it welcome. Track external fixes; do not comment.
## Triggers
- `ci-scan` cron (every 30 min): new main-branch failures → tasks tagged `source:ci-main`
- `pr-ci` cron (`github_pr_activity` gate): CI finished on an own PR → CI results comment
- Human directives (chat, task notes tagged `human-directive`) → outrank everything
## Actions
| From | Event | To | Who holds the ball |
|---|---|---|---|
| `pending` | a run claims it | `in_progress` | this worker |
| `in_progress` | PR pushed, CI running | `waiting_ci` | CI |
| `waiting_ci` | CI green, review requested | `on_review` | reviewer |
| `in_progress` | someone else's fix PR exists | `monitoring_external` | the other author |
| any waiting | human comment or directive | `pending` + `needs-response` | this worker |
| `on_review` | merged | `verifying` → `done` | post-merge window |
Anti-patterns: pushing a cleanly waiting task back to `pending` (burns a run);
a waiting status with no PR and no posted question (a phantom block).
## Approval Required
- Opening a public issue · anything irreversible or external → human approval
- Classifying a test as flaky · the CI results comment on an own PR · notifying
→ autonomous, recorded in the task note
- Self-raised work → only with a passing `## Impact` section
## References
- Failure DB columns: `job, test, status, log_url`; failures `status IN ('failed','errored')`
- Labels: `bugfix`, `flaky-test`
- Toolchain pin lives in `ci/toolchain.lock`: read it, never quote it
---
Read the full guide: https://github.com/ClickHouse/nerve/blob/main/docs/worker-best-practices.mdStatuses in the Actions table other than the built-in pending, in_progress, done and deferred are created once with task_status_create. You can edit TASK.md at any time. Changes take effect on the next session.
Workers operate through a structured cycle:
Cron detects issue → Agent proposes plan → Notification sent
→ Human reviews (approve / decline / revise) → Implementation session runs
In detail:
- Detection — A cron job (the built-in
task-planner, when onboarding enabled it, or a custom monitoring cron) identifies something actionable - Task creation — The agent creates a task describing the issue (
task_create) - Plan proposal — The agent researches the issue, explores the codebase, and writes a plan (
plan_propose) - Notification:
plan_proposemoves the task toplan_reviewand sends one notification with links to the plan and the task - Review — You review the plan in the web UI:
- Approve → An implementation session spawns automatically with the plan as instructions
- Decline → The related task is closed (moved to
done) with your feedback recorded as the note; reopen or recreate the task if you want a fresh plan - Revise → Your feedback is sent back to the planner session (context preserved), which proposes a revised plan
- Implementation — The approved plan executes in a dedicated session with full tool access
- Verification — Implementation session runs checks, tests, reports results
A revision goes back to the session that proposed the plan — for the planner's plans, the same persistent planner session — so the planning context is preserved across revision rounds.
Workers create their own skills during onboarding and refine them over time.
How skills work:
- Skills are plain Markdown files in
workspace/skills/<name>/SKILL.md - The agent reads skill descriptions in every session's system prompt
- Full skill content is loaded on demand via
skill_get - Skills can include reference docs (
references/), scripts (scripts/), and assets (assets/)
Automated skill lifecycle:
skill-extractor(every 12h) — watches for repeated workflows in conversations and completed tasks, proposes new skills via task+planskill-reviser(weekly) — reviews existing skills for accuracy, completeness, and quality, proposes revisions
When a skill-related plan is approved, an impl-<id> session is spawned like for any plan; its prompt tells it to apply the plan (a full SKILL.md) with skill_create or skill_update and close the task with task_done.
Each worker needs its own config directory (-c), state directory
(NERVE_HOME), workspace and port. nerve init asks for the workspace and the
port; the other two come from the command line and the environment:
# Worker 1: CI monitor
NERVE_HOME=~/.nerve-ci-monitor nerve -c ~/nerve-ci-monitor init --port 8901
# → open the setup page it prints, choose Worker, tick what it should do
# Worker 2: PR reviewer
NERVE_HOME=~/.nerve-pr-reviewer nerve -c ~/nerve-pr-reviewer init --port 8902Keep NERVE_HOME set (and pass -c) for every later nerve command that
addresses that worker. Each worker has:
- Separate config files (
config.yaml,config.local.yaml) - Separate databases and logs (its
NERVE_HOME) - Separate workspace (its own skills, tasks, memory)
- Separate cron jobs and schedules
- Separate web UI port (
gateway.port, chosen atnerve init)
Workers don't share state. They're fully independent processes.
Run nerve doctor to verify the worker is healthy:
nerve doctor
# [OK] Config loaded
# [OK] Database schema: v14
# [OK] Cron jobs: 3/4 enabled, 1 overridden in jobs.yaml
# [OK] Proxy: running on port 8317
# [OK] Setup checklist completeWhile setup is unfinished the last line reads [WARN] Setup checklist incomplete (steps: provider, profile) — finish at /setup; nerve status
prints the same count with the page's address. Neither prints the setup token
after the claim.
The web UI provides:
- Cron logs — see every job run with status, duration, and output
- Task manager — track open tasks and their plans
- Plan review — approve, decline, or revise proposed plans
- Notification center — all alerts and questions from the worker
- Session history — browse all agent sessions (onboarding, planning, implementation)
Workers communicate through the notification system:
notify— status updates, completion alerts, issues foundask_user— questions that need decisions (rendered as buttons in web UI and Telegram)
Priority levels: urgent, high, normal, low. Quiet hours are optional and off for a worker by default (quiet_start and quiet_end empty in <workspace>/config/settings.yaml; see Configuration → Quiet Hours). When set, they record the human's quiet window but gate nothing the agent sends; notify is not held back by them, so have the worker apply quiet hours itself for anything below urgent (see Personal Best Practices → Notifications).
nerve logs # Tail the daemon log
nerve status -f # Status + follow logsCron run history is stored in the cron_logs SQLite table and visible in the web UI under Cron Logs. Each run records job ID, timestamps, status (success/error/missed), and output.
| Personal | Worker | |
|---|---|---|
| Purpose | Full-featured assistant for one human | Task-focused autonomous agent |
| Workspace files | SOUL.md, IDENTITY.md, USER.md, MEMORY.md, AGENTS.md, TOOLS.md | SOUL.md, TASK.md, MEMORY.md, AGENTS.md, TOOLS.md |
| Memory categories | Life-oriented (relationships, finances, health, travel) | Operational (patterns, procedures, decisions, approvals) |
| Default crons | inbox-processor, task-planner, machine-maintenance, memory-maintenance | skill-extractor, skill-reviser, machine-maintenance, memory-maintenance (task-planner off until onboarding enables it) |
| Quiet hours | 02:00–08:00 | none |
| Sync sources | Telegram, Gmail, GitHub | None by default (can add custom sources) |
| Channels | Web UI + Telegram bot | Web UI (+ Telegram if configured) |
| Onboarding | A conversation with the agent | A conversation with the agent; set up without the browser with a task description, the worker sets itself up |
| Primary workflow | Conversational (user asks, agent responds) | Plan-driven (detect → plan → approve → execute) |