Documentation menu

Concepts and coordination

The main objects#

ObjectMeaning
DaemonOne local process owns agent processes, state, messages and the dashboard. Commands start it on demand.
GroupA team with a repository, goal, coordination policy and optional budget/settings.
SessionOne named agent conversation/process inside a group. Its ID is group/name.
WorktreeA separate Git checkout for one session, created under the nid state directory.
Integration branchnid/<group>, where completed, accepted task commits accumulate. Groups created before the renames keep their terminid/<group> or pawmux/<group> branches.
BoardShared 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/<group>; 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):

ToolPurpose
board_readgoal, team, tasks, notes and locks; called first (later reads show what changed; full=true for all)
expandthe full original of a compressed block
task_createcreate a task (title, detail, deps, owner)
task_claimclaim a task; also merges teammates' finished work into the worktree
task_updatedone (with summary: commits and integrates), blocked, open (release), cancelled
lock_acquire / lock_releasesoft-lock files, directories or globs (with a TTL)
board_notepost a note, decision, blocker or finding
msg_sendmessage a teammate, * (everyone) or human
status_setone-line status shown to the team and the human
syncmerge the integration branch into the worktree
ask_humanask 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_AUTO_TURN_LIMIT) 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/name or ✗ g/name: reason 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_HOME/restart.json 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_approve(task, notes?) 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_request_changes(task, notes), 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 ~/.nid/config.yaml). 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_update.cost 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.10 + $4.20 est.; 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_human(question, timeout_minutes?) 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_minutes (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_PREEMPT_MINUTES (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.)