# Conflicts

> Every conflict kind Comity detects, with examples, severities, statuses, actions, false positives and overrides.

A **conflict** (`CF-n`) is overlapping or incompatible work between two participants: two agents' intents or sessions, an agent and a pushed branch, or an agent and a protected-path rule. Comity detects conflicts from what agents actually edit (and what they declare), persists them, briefs both sides, and closes them when the overlap goes away.

## Kinds

| Kind | Dashboard label | Default severity | Can block |
|---|---|---|---|
| `path_overlap` | Same files | `info` | only exclusive claims |
| `symbol_overlap` | Same code | `warn` | no |
| `contract_break` | Contract break | `warn` | only for high/frozen contracts |
| `dependency_order` | Dependency order | `warn` | no |
| `protected_path` | Protected path | `block` | yes |

`textual_merge` and `duplicate_work` exist in the API's enums and policy, but no detector produces them.

### path_overlap

Both sides touch or claim the same files. Confidence depends on how specific the match is: an exact path edited by both (0.9) is stronger than two overlapping globs in declared scopes (0.3–0.5).

> INT-12 (jordan, claude_code, feat/partial-refunds) and INT-14 (maya, codex, feat/refunds) overlap on 2 path(s): src/billing/refund.ts, src/billing/types.ts.
>
> *Suggestion:* Narrow scope to avoid the shared paths, or coordinate via message_send between INT-12 and INT-14; if both must change them, agree on an order (conflict_respond propose_sequence).

Path overlap is `info` by default: it appears in briefings and the dashboard but doesn't warn. It **blocks** when one side holds an **exclusive** claim on the path (declared with `mode: "exclusive"`, or the path matches the policy's `exclusive_by_default`, in which case whoever touched it first holds it), the claim covers at most `exclusive_max_files` files, and the other side is about to edit it:

> INT-12 (jordan, …) holds an exclusive claim on db/schema.prisma covering db/schema.prisma; INT-14 (maya, …) is about to edit it.

### symbol_overlap

Both sides change, or claim, the same function, class, method or type. A signature change or removal on one side against a modification on the other scores higher than two compatible edits; overlap through a containing class or in test files scores lower.

> INT-12 (jordan, claude_code, feat/partial-refunds) and INT-14 (maya, codex, feat/refunds) both work on 1 symbol(s): sym:ts:src/billing/refund.ts#issueRefund. INT-12 changes the signature of sym:ts:src/billing/refund.ts#issueRefund; INT-14 modifies it.
>
> *Suggestion:* Sequence the work: INT-14 rebases after INT-12 merges (conflict_respond will_rebase), split the symbol's work, or coordinate via message_send to INT-12.

### contract_break

One side makes a breaking change to a contract the other side uses or has claimed:

- an exported function, class or type whose signature changed incompatibly or that was removed,
- an HTTP route that was removed, moved or whose signature changed (method, path, handler, schemas),
- an exported zod schema that lost or retyped a field (adding an optional field is compatible).

"Uses" means the other side's new code references it: an import, an HTTP call to the route (`fetch`, axios, ky, got, `HttpClient`, Python requests/httpx/aiohttp), or a package import from another repo. A claim on the contract (`scope.contracts` or `symbols`) also counts, with lower confidence.

> INT-12 (jordan, claude_code, feat/partial-refunds) changes the signature of http:POST /v1/refunds (breaking, criticality normal); INT-14 (maya, codex, feat/refunds) adds 1 reference(s) to it in web/src/api/refunds.ts.
>
> *Suggestion:* INT-14 can build against the new signature and declare the dependency (intent_update depends_on_add INT-12), or ask jordan to keep a compatible shim until it merges (message_send).

A route whose handler changed but whose signature didn't is reported as a non-blocking contract break, "behavior/response changed", when the other side calls it.

Contract breaks escalate to `block` only for contracts whose criticality is in the policy's [`block_contract_break_when_criticality`](/docs/admin/policies#block_contract_break_when_criticality) (default `high`, `frozen`). Contracts have criticality `normal`, and there is currently no way to raise it, so contract breaks warn.

**Supported code.** TypeScript, TSX, JavaScript, Python and Go for symbols and signatures. HTTP routes for Express, Koa, Hono, Fastify, Elysia and similar routers, Next.js `app/**/route.ts` and `pages/api/**`, FastAPI, Flask, Sanic, Quart, Starlette (not Go). Zod schemas in TypeScript and JavaScript.

**Across repos.** When a repo's route or exported package symbol breaks and an agent in another linked repo uses it, the conflict is reported to both, prefixed "Cross-repo: the contract is in X, the code that uses it in Y." Cross-repo conflicts never block. Linked repos are controlled by [`cross_repo_contracts`](/docs/admin/policies#cross_repo_contracts) and projects.

### dependency_order

Intents declare dependencies with `depends_on`. Comity reports:

- depending on an intent that was **abandoned** or **expired**:

  > INT-14 (maya, …) depends on INT-12 (jordan, …), which is abandoned.
  >
  > *Suggestion:* Remove the dependency (intent_update depends_on_remove INT-12) or take over the work it was meant to deliver.

- a **cycle** of dependencies among live intents:

  > Dependency cycle among 2 participants: INT-12 → INT-14. None of them can merge first.

### protected_path

A path matches a [`protected_paths`](/docs/admin/policies#protected_paths) glob and the person whose agent touches it isn't one of the rule's `user:<email>` owners. Blocks by default (an exact path match has confidence 1; a declared glob that merely overlaps the protected glob has 0.5 and only warns).

> db/migrations/0042_refunds.sql (edit) by INT-14 (maya, codex, feat/refunds) falls under protected path `db/migrations/**` (owners: user:diego@acme.com); the user is not an owner.
>
> *Suggestion:* Ask an owner (user:diego@acme.com) or an admin to approve (conflict_respond request_override), or move the change outside `db/migrations/**`.

List owners as `user:<email>`. Comity has no teams, so other owner forms (such as `team:<name>`) are rejected when you save the policy.

## Severity and blocking

Each kind starts at the severity in the policy's [`severity`](/docs/admin/policies#severity) map. A finding is **blocking** only when severity is `block`, the evidence is declared or observed (not inferred), and confidence is at least `block_min_confidence` (0.85); otherwise a `block` finding is downgraded to `warn`. Only the newcomer (the side that arrived second) is blocked, and only before an edit or shell command. See [How it works → Decisions](/docs/how-it-works#decisions).

## Statuses

| Status | Meaning |
|---|---|
| `open` | New; needs attention |
| `acknowledged` | Someone on a side said they've seen it (`ack`) |
| `resolving` | A side is sequencing or rebasing (`propose_sequence`, `will_rebase`) |
| `resolved` | Done: marked resolved, the overlap went away (`evidence_gone`), one side merged/abandoned/ended, or every side marked it a false positive |
| `overridden` | A human overrode it with a reason |
| `stale` | Both sides are gone |

`open`, `acknowledged` and `resolving` are **live**. A resolved or stale conflict **reopens** automatically if the same overlap comes back; an overridden one reopens if the evidence changes. When a session without an intent declares one, its conflicts move to the intent and keep their numbers.

A contract break stays open for the dependent side after the changer merges, because the dependent still has to adapt, unless both share the same checkout.

## Actions

Agents act with `conflict_respond`; people with the buttons in the dashboard's conflict drawer or `POST /v1/conflicts/:ref/actions`. Only participants (and owners/admins) can act.

| Action | Status after | Use it when |
|---|---|---|
| `ack` | `acknowledged` | You've seen it and are coordinating |
| `propose_sequence` | `resolving` | You propose an order (say which in `note` or a message) |
| `will_rebase` | `resolving` | You'll rebase after the other side merges |
| `resolved` | `resolved` | You've fixed it |
| `false_positive` | see below | It isn't a real conflict |
| `request_override` | unchanged | You need a human to approve working through a block |

```json
{ "conflict": "CF-9", "action": "will_rebase", "note": "Rebasing after INT-12 merges." }
```

Acting on a closed conflict returns `409`.

## False positives

- An **agent's** `false_positive` mutes the conflict for its own side only: that agent stops being warned or blocked by it. The conflict resolves as `false_positive` once every side has muted it (immediately when both sides belong to one developer).
- A **person's** **False positive** in the dashboard (participant, owner or admin) resolves it at once.
- **Feedback** (`POST /v1/conflicts/:ref/feedback`, or **Useful** / **False positive** in the dashboard) records a verdict (`useful`, `false_positive`, `too_late`) used for tuning and the time-saved estimate.

To silence a whole class of noise, add paths to [`ignore_paths`](/docs/admin/policies#ignore_paths) (generated files, lockfiles) or lower a kind's severity.

## Overrides

An override lets both sides continue despite a conflict, including a blocking one. Only people, never agents:

1. An agent (or person) calls `request_override`. Comity sends a system message, "Override requested for CF-n", to everyone whose role is in the policy's `override.allowed_roles` (default owners and admins).
2. One of them opens the conflict in the dashboard, clicks **Override…**, and gives a reason (3–2,000 characters; optional when the policy sets `override.require_reason: false`). API: `POST /v1/conflicts/:ref/override` with `{"reason": "…"}`.

The conflict becomes `overridden`, the reason is stored encrypted and shown in the audit log, and the agent is no longer blocked by it. If the evidence changes later, the conflict reopens.

## Time saved

The dashboard's **Est. time saved** counts conflicts caught before merge, in minutes per conflict weighted by severity. Overridden, false-positive and too-late conflicts, and conflicts between two agents of one developer, count zero.
