Collaboration
Conflicts
Every conflict kind Comity detects, with examples, severities, statuses, actions, false positives and overrides.
On this page
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 (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 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 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 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.
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 |
{ "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_positivemutes the conflict for its own side only: that agent stops being warned or blocked by it. The conflict resolves asfalse_positiveonce 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 (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:
- 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'soverride.allowed_roles(default owners and admins). - 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/overridewith{"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.