How Regente works.

Regente is a filesystem-only coordination engine for a fleet of coding agents working in one repo. There's no network in the hot path and no orchestrator process — just a shared, locked source of truth the CLI, the agents (over MCP), the HTTP API, and the dashboard all read and write.

One engine, one source of truth

All coordination state lives in a per-repo .regente/ directory: agents and presence (status.json), claims, the handoff queue and fence counters (claims.json), roles and lanes (roles.json), and append-only logs for events, messages, and handoffs (events.jsonl, messages.jsonl, handoffs.jsonl). Every mutation takes a cross-process lock and writes atomically, so cooperating agents do not lose claims or presence records while they use Regente — that invariant is the whole point of a coordination layer.

Join & presence

An agent joins the workspace and keeps a heartbeat. Claude Code, Codex, and Gemini CLI can use repo-local hooks to join automatically; Cursor is wired over MCP by regente install and joins on its first Regente tool call; Aider and OpenCode attach through manual MCP join until native hooks are available. The fleet is whatever's actually running, coordinating as equals. Regente joins the agents you already run; it never spawns or replaces them.

MCP-attached agents don't have to remember to heartbeat: every Regente MCP tool call refreshes the caller's presence (throttled to once per 20 seconds by default; REGENTE_AUTO_HEARTBEAT_INTERVAL_MS overrides). The explicit heartbeat tool is only needed to update task or state text. Hook-joined agents already heartbeat via hooks; both paths behave the same.

Each agent carries a derived presence field, online or offline, computed from heartbeat age (an 8-minute window) and self-declared stops, with last_seen_at alongside. The state field stays the agent's last self-reported work state; it says what the agent claims to be doing, not whether it's alive. list_agents is the single roster: it returns the workspace agents with total/online/offline counts, and Regente keeps no separate team-member list.

Claims & fences

Before editing, an agent claims the file. A claim is advisory to tools that never call Regente, but participating adapters enforce it: a second agent that reaches for a held file is blocked pre-merge. Each grant returns a fence — a per-path counter that only ever increases, across both release and reclaim. The fence is the proof of ownership: a crashed holder that comes back and presents an old fence is provably stale and is rejected, so it can't release a file out from under the agent that now owns it. Re-claiming a file you already hold is idempotent.

You usually don't claim by hand. For hook-guarded agents (Claude Code's PreToolUse hook, Gemini CLI's BeforeTool hook), the live edit guard auto-claims an unclaimed file at the first real edit. The claim is marked auto-claim and is released automatically when that session ends, so in practice the first editor becomes the owner and a second live editor's edit is blocked at edit time. Disable it by setting "auto_claim": false in .regente/config.json. Codex has no pre-tool hook; its edits are recorded at commit time by the pre-commit guard, with no auto-claim and no edit-time block. Cursor attaches via MCP only (no hook): agents claim with claim_path, and check_edit shows recent editors.

Regente also records who edited each file (a bounded one-hour history in .regente/recent-edits.json). When an agent edits a file that a different online agent edited within the last 30 minutes (config key recent_edit_warn_minutes), the guard warns and emits a conflict.detected event with type interleaved-edit, deduped per agent pair and path (one warning per 10 minutes). The check_edit MCP tool returns a recent_editors list. Offline editors never trigger the warning.

Symbol-level claims (claim a function, not a file)

A claim can cover a whole file or a single symbol within it — a function, class, or method. Two agents editing two different functions of the same file can hold separate symbol claims; only a claim on the same symbol does. A whole-file claim still covers every symbol (and vice-versa), so you can't sneak a symbol edit past a file holder. Claims display as path#symbol:

regente symbols api.js                          # list claimable functions/classes + line ranges
regente claim api.js --symbol login --agent alice
	regente claim api.js --symbol logout --agent bob   # same file, different function → separate symbol claims

A zero-dependency heuristic parser maps common JS/TS and Python symbols; it is intentionally conservative and does not try to be a full compiler. If a symbol can't be parsed or mapped reliably, Regente safely falls back to file-level coordination — never weaker.

Conflicts: block vs territory

Regente compares active claims and flags these kinds of overlap:

SeverityWhen
⛔ same-symbol Two agents claim the same function/class/method. The second claim is refused and queued for auto-handoff — pre-merge, before a line is lost.
⛔ same-file Two agents claim the same file (or a whole-file claim meets a symbol claim on it). Blocked and queued, same as above.
⚠ territory A claim overlaps a directory another agent holds, or reaches into another role's lane. Regente warns; it doesn't block — you decide.
⚠ semantic Two agents hold different but coupled files (one imports the other). Advisory — see below.
⚠ interleaved-edit An agent edits a file that a different online agent edited within the last 30 minutes. The guard warns; it doesn't block, and offline editors never trigger it.

Semantic coupling (dependency-aware)

File claims stop two agents overwriting the same file. But agents on different files can still break each other when those files are coupled — e.g. api.js imports ./db and someone else is rewriting db.js. Regente scans actively-claimed files for cross-references (import / require / #include) and, when two different agents hold files that reference each other, surfaces a soft semantic conflict so you coordinate the interface. It's read-only, advisory, and never blocks. It matches intra-repo relative imports; external packages (e.g. react) are ignored.

The auto-handoff queue

A lock would tell the blocked agent "no" and leave it stuck. Regente queues it instead and returns an actionable resolution — who holds the file, your position in line, and other lanes you own so you can work elsewhere. The instant the holder releases, the file is handed to the next agent in line automatically, carrying a fresh fence. Claim → block → queue → release → auto-owned, with no human in the loop.

Because MCP can't push, the grant is delivered in-band: the waiting agent learns it owns the file by polling check_handoffs on its own next turn (carrying a cursor) — no dashboard required.

Messages and handoffs can be linked to a task: post_message and create_handoff accept a task_id, and list_messages can filter by task or agent. Direct messages reach hook-wired agents inside their session: the heartbeat hook injects them on the recipient's next prompt or tool use, no polling loop required. They also carry receipts, delivered_at (stamped the first time the recipient fetches them) and read_at (stamped when the recipient acks with ack_message), so a sender can tell undelivered from delivered-but-unacked — the dashboard's Messages view renders that state as not-delivered / delivered / read. Room messages carry no ack state.

Dead-holder reclaim (no deadlocks, no zombie claims)

If a holder goes dark — no heartbeat for 15 minutes — a sweep can reclaim its contested files (the ones another agent is waiting on) and hand them to the next waiter. To avoid surprising a long-running live edit, Regente checks whether the holder PID is still alive and requires a fresh waiter to age before reclaiming; when the holder has been dark for twice the stale window, that grace is skipped and the waiter is granted on the next tick.

Claims also cannot outlive their agent. Orphaned auto-claims (a session that died without a clean end) are released as soon as their holder is dark, and every claim expires once its holder has been dark past claim_expiry_minutes (default 60; set 0 in .regente/config.json to disable). An actively working agent never trips this: every hook event and every MCP tool call refreshes its presence — only a genuinely gone session loses its files. Queued waiters expire on the same window, so a release never grants a file to a session that's gone, and a task assigned to (or in flight with) a gone agent reverts to todo (a task.unassigned event, reason assignee-gone) instead of reading as work in progress. Combined with fencing, a crashed agent does not wedge the fleet, and a resurrected one can't clobber the new owner. Run it on demand with regente sweep; the desktop app and daemon sweep automatically every 30 seconds. For roster hygiene, regente prune drops agent records that have been dark for days and hold nothing.

Roles & lanes

Each agent takes exactly one role at a time, and a role can own file/path lanes (globs). When an agent reaches into another role's lane, Regente raises a territory warning before the edit lands. Presets come with sensible default lanes; any custom role starts empty and you set its lanes with --paths.

Preset roleDefault lanes
frontend public/**, src/components/**, src/pages/**, **/*.css
backend server/**, src/api/**, src/services/**
tests tests/**, **/*.test.*, **/*.spec.*
docs docs/**, **/*.md
infra .github/**, *.config.*, package.json, Dockerfile
feature — (no preset lanes; set with --paths)
debugger — (no preset lanes; set with --paths)
reviewer — (no preset lanes; read-mostly)

Assigning a new role drops the agent from its previous lane. Agents can set their own role over MCP with assign_role. See the CLI reference for regente role / regente roles.

Zero-config lanes. You don't have to declare any of this. If an agent never sets a role, Regente infers its lane from the files it actually claims — the deepest directory common to its claims in an area, once it has touched at least two files there. Inferred lanes power the same advisory territory warning (“you're in alice's working area”) and never block. Explicit roles always win; inference just fills the gap. See them with regente lanes (or the list_lanes MCP tool), and regente lanes --apply turns them into real roles.

Shared context (the “why”)

Coordination isn't only about who holds a file — it's about why. Agents can record a decision, finding, warning, or note tied to specific files or a task; other agents read it, and the relevant entries are auto-delivered through check_handoffs when an agent picks up a coupled file or task. So a choice one agent makes (“switched auth to JWT”) reaches the next agent that touches the same code, instead of being silently rediscovered. Context can be signed and verified like claims, and it's bounded to keep the log lean.

regente context add "Switched auth to JWT — sessions don't scale here." \
  --agent alice --kind decision --paths server/api.js
regente context --path server/api.js        # read the why for a file

Signed attribution & the event log

Everything every agent does appends to a durable event log. When an agent acts through the MCP bridge, it signs each claim and message with a per-session Ed25519 key (Node's built-in crypto — still zero-dependency). Regente keeps bounded key history per agent, so normal MCP/session restarts do not make older signed events look tampered:

regente verify        # Signed events: 42/50 · verified: 42 · failed: 0

Honest scope: new events are hash-chained and per-entry signed when agents act through MCP; legacy unchained events are reported separately by regente verify. Actions taken through the bare CLI are unsigned by design (the CLI is you, locally). This signed, cross-tool record of who-did-what is the substrate the paid governance tier is built on.

Local-first & privacy

Core coordination is local-first. The .regente/ state is stored locally, and Regente adds no hosted coordination server of its own. Optional planner calls send your goal plus limited workspace planning context to the LLM provider you configure. Telemetry is strictly opt-in — a snapshot is transmitted only if you set both REGENTE_TELEMETRY=1 and REGENTE_TELEMETRY_URL.

When enabled, the payload is counts and public tool identifiers only (how many agents, distinct tools, conflicts surfaced, auto-handoffs, active days…) plus a one-way hashed workspace id. It never includes file paths, file contents, agent or task names, messages, your username, hostname, cwd, or pid. See the exact payload that would ever be sent with regente metrics --json.