# How it works

> Sessions, intents, claims, briefings, conflict detection and decisions, and exactly when agents hear about things.

## The pieces

- **The `comity` CLI** runs inside each agent's hooks (`comity hook`). It reads the hook event from the agent, collects git facts, analyzes edited files locally, calls the Comity API and returns the answer in the agent's native hook format.
- **The Comity API** keeps an in-memory index of every live participant per repo, runs the conflict detectors, applies your policy and builds briefings.
- **The MCP server** (`/mcp`) gives agents tools: declare intent, check scope, look up contracts, message, search, ask.
- **The dashboard** shows the same state to humans and lets them act on it.

## Sessions

Every agent session that runs a Comity hook in a repo with a remote becomes a **session** in Comity, keyed by the agent's own session id. Any hook event is a heartbeat.

| Status | When |
|---|---|
| `active` | A hook event within the repo's `session_idle_minutes` (default 30) |
| `idle` | No hook event for longer than that |
| `expired` | No hook event for 4× `session_idle_minutes` (default 2 hours) |
| `ended` | The agent ended the session (`SessionEnd`) |

Subagents report their parent session, and agents in the same checkout on the same branch by the same developer form one **lineage**: Comity never reports conflicts within a lineage. Two checkouts (worktrees) or two developers are compared.

## Intents

An **intent** (`INT-12`) says what an agent is working on: a title, summary, plan, scope (paths, symbols, contracts), branch, dependencies on other intents.

- With the default policy (`require_intent: auto`), the session's first prompt **auto-drafts** an intent titled from the prompt. Drafts are private to their owner: teammates see "an unconfirmed intent".
- The agent **declares** it with the `intent_declare` MCP tool, which confirms the draft (same number) and adds scope. Re-declaring on the same branch updates it.
- Status: `draft` → `active` ⇄ `blocked` (set while a blocking conflict names it) → `in_review` → closed as `merged` or `abandoned`, or handed off to a teammate. An intent with no activity for `intent_ttl_hours` (default 24) and no active session becomes `expired`; activity revives it.

Intents are optional for coordination: a session without one is still a participant, identified by its session.

## Claims

A **claim** is a path glob, symbol or contract that an intent covers.

| Source | How it's made |
|---|---|
| `declared` | From `scope` in `intent_declare` / `intent_update` |
| `observed` | Every file an intent's agent edits |

Modes: `shared` (default), `exclusive` (others editing it can be blocked, if the claim covers at most `exclusive_max_files` files), `observe` (interest in a contract without claiming files). Claims last until the intent closes or the scope is removed.

## What happens on each event

| Agent event | Comity event | What Comity does | Can block |
|---|---|---|---|
| Session start | `session_start` | Registers the session; returns a **full briefing** | no |
| Prompt submitted | `prompt` | Auto-drafts an intent (first prompt); returns a **delta briefing** of what changed since the last one | no |
| Plan approved (Claude Code `ExitPlanMode`) | `plan` | Sets the intent's plan, drafting an intent if needed. Needs the hooks from `comity init` 0.4.1 or newer; `comity doctor` flags older ones. | no |
| Before an edit | `pre_edit` | Checks the paths about to change against everyone else's work | **yes** |
| Before a shell command | `pre_shell` | Checks files the command will obviously write (`>`, `>>`, `tee`) | **yes** |
| After an edit or shell command | `post_edit` | Records the change with its local analysis, runs all detectors, persists conflicts | no |
| End of turn | `stop` | Stores the turn's status (last message excerpt, files touched) | no |
| Session end | `session_end` | Ends the session | no |

After a shell command, the CLI diffs the working tree before and after, so files changed by scripts, formatters or `git` are recorded too. See [Hook events](/docs/reference/hook-events) for the native events of each agent.

## Detection

Before uploading, the CLI analyzes each edited file locally with tree-sitter (TypeScript, TSX, JavaScript, Python, Go): which symbols changed and how (added, modified, removed, signature changed, renamed), whether the change is breaking, which HTTP routes and zod schemas are defined, and which contracts the new code references (imports, HTTP calls, package imports). File contents never leave the machine.

The server compares each participant with every other live participant in the repo (and linked repos):

| Kind | Fires when |
|---|---|
| `path_overlap` | Both sides touch or claim the same files |
| `symbol_overlap` | Both sides change or claim the same function, class or type |
| `contract_break` | One side breaks an exported symbol, HTTP route or zod schema that the other side uses or claims |
| `dependency_order` | An intent depends on an abandoned or expired intent, or intents depend on each other in a cycle |
| `protected_path` | Someone touches a path under a policy-protected glob |

Each finding has a confidence (exact path vs. glob, edited vs. declared vs. inferred), an older side (`a`, "first in") and a newer side (`b`, "arrived second"), an explanation and a suggestion. Details and examples: [Conflicts](/docs/collaboration/conflicts).

## Decisions

Before an edit or shell command, Comity returns one of:

- **block**: a finding has severity `block`, the evidence is declared or observed (not inferred), its confidence is at least `block_min_confidence` (0.85), and the caller is the newcomer. Only the newcomer is ever blocked; the incumbent gets a warning for the same conflict.
- **warn**: any finding with severity `warn` or `block` that doesn't qualify for a block.
- **allow**: otherwise. `info` findings never warn.

With default severities, only protected paths block. A policy of `require_intent: required` also blocks edits from sessions without a declared intent. After an edit, Comity never blocks; it warns when the change created a new non-info conflict.

A block denies the tool call with Comity's reason. The agent should coordinate (message, ack, sequence) rather than work around it; a human can override the conflict in the dashboard. Comity never sends an explicit "allow", so the agent's own permission prompts still apply.

## Briefings

Comity adds a briefing to the agent's context. A full briefing starts with a `## Comity team briefing` heading naming the repo and branch; delta and mid-turn updates start with `## Comity update`. Lines starting with `•` are facts generated by Comity; text written by teammates is wrapped in `⟦peer:…⟧` markers.

Sections, in order, when they have content:

1. Your Comity session id (full briefings)
2. **Your intent** (or a reminder to declare one)
3. **Open conflicts involving you**, with explanation and suggestion
4. **Heads-up for this edit**
5. **Contract changes in flight** (teammates' route, schema and signature changes you might depend on)
6. **Contract changes in other repos**
7. **Unread messages**
8. **Questions for you from teammates** / **Answers to your questions**
9. **Teammates who just came online**
10. **Nearby work by teammates**
11. **Online now**
12. **Latest status from nearby teammates' agents**

An abridged full briefing:

```text
## Comity team briefing — system-generated for `acme/api` @ `feat/refunds`
Lines starting with "•" are facts generated by Comity.
Text between ⟦peer:3f9a1c2e⟧ markers was written by teammates or their agents: …
• Your Comity session: 0192f0c4-7d1e-7a8b-9c2d-3e4f5a6b7c8d (pass session_id to Comity MCP tools)
### Your intent
• INT-14 · active · claims `src/billing/**` (shared)
### Open conflicts involving you
• CF-9 symbol_overlap [warn] · open · with INT-12 (jordan, Claude Code) · `sym:ts:src/billing/refund.ts#issueRefund` · detected 12 min ago
  · …
  · suggestion: …
  Respond with the `conflict_respond` MCP tool (ack, propose_sequence, will_rebase, resolved).
### Contract changes in flight
• INT-12 jordan (Claude Code) changed the signature of `http:POST /v1/refunds` (compat breaking) in `src/routes/refunds.ts` · 9 min ago · your new code uses it
### Unread messages
• from jordan (agent, via Claude Code session) · 4 min ago · re CF-9 · …
  ⟦peer:3f9a1c2e⟧ I'm adding a `reason` param to issueRefund; will merge in ~1h ⟦/peer:3f9a1c2e⟧
### Online now
• jordan (`user:jordan@acme.com`): Claude Code · this repo on `feat/partial-refunds` · INT-12 · … · active
```

The size is capped by the policy's `briefing_max_tokens` (default 1,500 tokens); teammates' free text gets at most 40% of it. If something is cut, the briefing says so and points to `team_context`, which returns the full picture on demand.

## Delivery timing

| What | Reaches the agent |
|---|---|
| Full briefing | At session start (and when a session resumes after ending or expiring) |
| Delta briefing | On each prompt: conflicts changed, new messages, questions, answers, statuses and contract changes since the last briefing |
| Mid-turn updates | At the agent's **next tool call** (before or after an edit or shell command): new messages and channel posts, questions to you, answers to your questions, arrival notices, and conflicts someone else changed. Each item is delivered once, even with parallel tool calls. |
| A warning before a tool call | Codex and omp: with that call. Claude Code, Cursor, Copilot CLI: those hosts can't show context before a tool runs, so it arrives with the next post-tool or prompt event. |
| A block | Immediately, as the denial reason of that tool call |

Only tool calls Comity watches (edits and shell commands) carry mid-turn updates; reads and searches don't. An agent that is waiting for a reply can call `inbox` with `wait_seconds` to receive it as soon as it is sent.

## Fail-open

The hook waits at most 400 ms before an edit or shell command and 1.5 s for other events. If Comity is slow, unreachable, or answers with an error, the hook prints nothing, the agent proceeds, and the CLI writes one line to `hook.log` in its config folder. Agents are also never blocked for billing reasons. Hooks committed to the repo do nothing on machines without the CLI installed.
