Documentation menu
Concepts and coordination
The main objects#
| Object | Meaning |
|---|---|
| Daemon | One local process owns agent processes, state, messages and the dashboard. Commands start it on demand. |
| Group | A team with a repository, goal, coordination policy and optional budget/settings. |
| Session | One named agent conversation/process inside a group. Its ID is group/name. |
| Worktree | A separate Git checkout for one session, created under the nid state directory. |
| Integration branch | nid/, where completed, accepted task commits accumulate. Groups created before the renames keep their terminid/ or pawmux/ branches. |
| Board | Shared tasks, notes, locks, agent status and messages. |
Under the default lead policy, the first session leads and subsequent sessions
are workers. peers leaves agents without a designated lead. Roles guide the
coordination prompts; they do not change a vendor's model or account permissions.
Sessions normally have separate worktrees; --shared instead uses the main checkout.
Task numbers are scoped to a group; question and approval IDs are database-wide.
A task may be open, claimed, review, blocked, done or cancelled.
Dependencies must be completed before a task is claimable. File locks are advisory
and expire: they coordinate teammates but do not prevent filesystem writes.
How it works#
You start a group of agents on a goal. nid runs a small daemon (started on demand) that
owns every agent session, gives each agent its own git worktree and branch, and injects the
nid MCP server so agents can coordinate through a shared board: tasks, notes, locks and
messages. When an agent marks a task done, nid commits its work and merges it into the group's
integration branch nid/; teammates pick that work up when they claim a task or sync.
You watch, steer, approve and finally land the integrated result into your checkout.
The board: tasks, notes, locks and messages#
Agents coordinate only through the nid MCP tools. Under the lead policy the lead turns the
goal into tasks (with dependencies and owners); idle workers are offered claimable tasks
automatically, and finishing a task notifies the lead and unblocks its dependents.
Agent side (MCP tools, server name nid):
| Tool | Purpose |
|---|---|
board_read | goal, team, tasks, notes and locks; called first (later reads show what changed; full=true for all) |
expand | the full original of a compressed block |
task_create | create a task (title, detail, deps, owner) |
task_claim | claim a task; also merges teammates' finished work into the worktree |
task_update | done (with summary: commits and integrates), blocked, open (release), cancelled |
lock_acquire / lock_release | soft-lock files, directories or globs (with a TTL) |
board_note | post a note, decision, blocker or finding |
msg_send | message a teammate, * (everyone) or human |
status_set | one-line status shown to the team and the human |
sync | merge the integration branch into the worktree |
ask_human | ask the human a question and wait for the answer; see Questions |
If integrating a finished task conflicts with teammates' work, nid merges the integration branch into the agent's worktree, leaves conflict markers, and asks the agent to resolve them and mark the task done again.
Human side:
nid board [group] [-w] # tasks, locks, latest notes, messages and questions for you
nid task add <title...> [-g group] [-d detail] [--deps 1,2] [-o session] [--minutes n]
nid task set <n> [-g group] [-s status] [-o owner] [--summary text] [--title text] [-d detail] [--minutes n]
nid task ls [group] # task numbers (#1, #2, ...) are per group
nid note <text...> [-g group] [-k note|decision|blocker|finding]
nid inbox [group] # messages agents sent to you, plus pending questions
nid send <session|group> <text...> # message one session, or everyone in a group
nid attach <session> # follow a session and chat (/stop /interrupt /approve /deny)
nid attach --native <session> [--force] # work in the agent's own TUI on the same conversation
nid watch [group] [-v] # stream all activity in a group (alerts ring the bell)
nid top [group] # live dashboard, refreshes every 2s; q or Ctrl-C quits
nid logs [session|group] [-f] [-n 40] [-v]
nid status [--json] # one-screen summary across all groups
nid stop|resume|interrupt <session|group>
nid top is a full-screen view of one group: each session's state, current task, cost and
tokens, task counts, pending approvals and questions, and the last 15 activity lines. Off a
terminal (piped) it prints a single frame and exits.
nid watch and nid top ring the terminal bell and highlight the line when something needs
you: an approval request, a question from ask_human, or a lead posting a DONE: decision.
Agent-to-agent messages start new turns automatically. To avoid runaway loops, a session pauses
after 60 automatic turns in a row (NID) until you send it something.
If an agent process exits unexpectedly after running for more than a minute, nid resumes the
session once (the vendor's resume path, so the conversation continues) and tells the agent to
check its interrupted work. A second crash within 10 minutes stops the session and notifies you
in nid inbox. Turn this off with --no-auto-restart or autoRestart: false in nid.yaml.
nid also records each agent's process id: if the daemon itself dies, the next daemon start
kills agent processes it left behind before marking their sessions stopped.
nid daemon restart (e.g. after upgrading nid) stops the daemon gracefully, starts a new
one and resumes exactly the sessions that were running, printing ✓ resumed g/ or
✗ g/ for each (exit code 1 if any failed). Stopped sessions stay stopped; agents
that were mid-turn are told to check their interrupted work. The list is saved to
$NID first, so rerunning an interrupted restart still resumes them.
Integration: reviewing and landing work#
nid diff [session|group] [--stat] # a session's work vs the integration branch, or the group's integrated work
nid land [group] # merge nid/<group> into the current branch of your checkout
nid rm <group> [--keep-branches] # stop the group, delete its sessions, worktrees and branches
# (lessons go to project memory + an archive; --purge deletes all)
GitHub pull requests and CI#
nid land web --pr # push nid/web to origin and open/reuse a PR
nid land web --pr --base main --draft # choose base and open as a draft
nid pr checks web # turn failing CI checks into tasks
nid pr checks web --watch 2 # poll every 2 minutes in the daemon
nid pr checks web --stop-watch
PR landing pushes the integration branch and leaves your checkout's branch unchanged. The generated body includes the goal, tasks, authors, reviewers, reported cost and input/output tokens. An existing open PR for the same head/base is reused. The default base is the GitHub repository's default branch. The PR link is saved in group settings and shown on the dashboard.
nid uses the gh CLI when it is installed and logged in to the repository's host. Otherwise
(no gh, or gh auth status fails for that host) it uses the GitHub REST API with GH_TOKEN or
GITHUB_TOKEN from the daemon's environment; with neither, gh's own error is shown. Tokens and
URL credentials are redacted from error messages. Git push uses your existing
Git credentials. HTTPS and SSH origin URLs work, including GitHub Enterprise hosts.
CI checks include GitHub check runs and legacy commit statuses. Failed checks become open
tasks containing the head commit and log URL; repeat polls deduplicate by check name and
commit, including tasks already completed. Pending, cancelled and skipped checks do not
create tasks. Teammates are offered the new work, and the lead (or human inbox) is notified.
After agents fix failures, run land --pr again to push the updated integration branch.
Use Manage group → Land changes → Open a GitHub pull request to choose a base and draft status. The group's Pull request panel shows its link, CI results, Check CI, and Watch CI / Stop CI watch controls. Background watches resume automatically after daemon restart; stop them explicitly or remove the group. The watch interval, saved PR and CI tasks persist. Watches report fetch errors in the panel and retry at the next interval.
Review gate#
Turn on peer review and every finished task is reviewed by another agent before it reaches the integration branch (any policy):
groups:
web:
review: auto # off (default) | auto | reviewers
reviewers: [codex, oc] # with review: reviewers (a list alone implies it)
sessions: [claude, codex, opencode:oc]
or at runtime: nid group set web review=auto / nid group set web 'reviewers=["codex"]'.
When the author marks a task done, nid commits the work but does not integrate it: the task moves
to status review and a review task "Review #N ..." is created with the diff (truncated; the full one
is nid diff <author>). The reviewer is
reviewers: listed running sessions other than the author, round-robin and preferring an idle session (a busy listed session can be assigned if none are idle);auto: an idle session of a different agent vendor than the author (Claude reviews Codex, Codex reviews Claude...), else any other idle session.
The same reviewer keeps the task across rounds. If nobody can review, the review task stays open for
anyone but the author to claim, and the lead (or you) is notified. The reviewer answers with the
review tool, which integrates the reviewed commit exactly like a normal done
(a merge conflict goes back to the author, and integrates without a second review once resolved), or
review, which hands the task back to its author (claimed) with the notes
as a message, in the task detail and on the board. Only the assigned reviewer or the lead may answer.
You can override at any time:
nid review ls [group] # tasks waiting in review and their reviewers
nid review <group> <task> approve [-m notes] # integrate now
nid review <group> <task> changes -m "notes" # send back to the author
The dashboard shows review tasks first on the group board with Approve / Request changes buttons.
Budgets#
A group can have a cumulative spending cap in USD. By default it counts billed spend only:
nid spawn claude codex --budget 20 # at spawn time (or budgetUsd in nid.yaml)
nid budget <group> 50 # raise or set it later
nid budget <group> off # remove the cap
nid groups # SPENT and BUDGET columns
When the cap is reached, nid stops starting automatic turns (agent and nid messages) for that
group and posts one notice to your inbox. Messages you send yourself (nid send, attach) are
still delivered. Raising or removing the budget resumes delivery of the queued messages.
Billed spend vs subscription estimates#
Agents report a price for every turn, but that is not always money spent. Claude Code reports
total_cost_usd even when it runs on a Pro/Max subscription: there it is the API-equivalent price
of the work, an estimate. nid books each turn's cost by how the session ran at the time:
- billed: the session ran in API mode (
billing: api, paid per token through an API key); - estimated (shown as
est.): any other session, i.e. a subscription plan.
Agents that report tokens but no price (Codex, opencode and other ACP agents) get an API-equivalent
estimate instead: the turn's uncached input, cache reads, cache writes and output tokens at the
model's list price (built-in table as of a fixed date, overridable with
undefined in ~/). These are always est., even in
API mode, because they are nid's arithmetic, not the vendor's invoice. Long-context surcharges,
batch and priority tiers are not applied, so treat them as a floor. Unknown models get no guess. An ACP
agent that reports its own session cost (usage in USD) is booked with that price instead.
Everywhere spend appears, the two are shown apart: nid ls, status, groups and top print
$1.10, $4.20 est. or $1; the dashboard marks estimates with "est." (hover
for the explanation); PR bodies have Billed and Est. on subscription columns. Budgets measure
billed spend unless the group sets budgetIncludesEstimates: true (useful to cap how much of a
plan's usage window a group may burn). Spend recorded before nid knew the difference (older
databases) counts as billed.
Where an agent reports its plan's usage window (Claude's five-hour/weekly windows, Codex's
primary/secondary limits), nid keeps the latest report at any level: nid limits lists the
% used and reset time per session, and the dashboard's session page shows it as Plan usage.
Questions from agents (ask_human)#
An agent can call ask when it needs a decision only you can
make. The question appears in nid inbox, nid board, nid ls and nid questions,
and the agent waits (state waiting) for up to timeout (default 30):
nid questions [group] [-a] # pending (or all) questions
nid answer <id> <text...> # the agent receives your answer and continues
If nobody answers in time, the agent is told to proceed with its best judgment. A question can be answered only once; late answers to timed-out questions are rejected.
Messages during a turn#
Agents receive nid messages (from you, teammates and nid itself) as a new turn when they are
idle, and while they work as [nid] notices appended to the result of their next nid tool
call. The system prompt asks every agent to call status_set every few minutes on long tasks so
these notices arrive.
Agents with no way to add input to a running turn (the ACP agents, opencode and kimi) can work for a
long time without touching a nid tool, so messages would wait until the turn ends. For them, a
message that is still undelivered NID (default 3; 0 turns this off) into a
turn interrupts the turn: the session's log shows interrupting the turn to deliver N waiting
message(s), and the next turn starts with a note telling the agent its turn was interrupted, the
messages, and an instruction to continue its work. A session waiting on an approval or an
ask_human question is never interrupted. Claude Code and Codex are not preempted.
ACP and PTY agents also get one reminder per claimed task when a turn ends while they still hold
it: "Your turn ended while you still hold #N ... call task_update(N, status="done", ...)". (opencode
tends to finish the work, say so in text and stop without task_update, which left tasks claimed
forever.)