Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Conflict Detection

When supervisor mode is active, the broker runs an in-process conflict detector that watches for three failure shapes across parallel agents. Detected conflicts surface as [conflict-detector]-tagged agent.feedback delivered to the involved agents, and — for unresolved in-flight shapes — as agent.question escalations to the supervisor inbox. The detector is automatic; you only opt out by setting [supervisor] enabled = false (or tuning the knobs in [supervisor.conflict]).

Contents

The Three Failure Shapes

git-paw’s own managed bookkeeping — everything under a worktree’s .git-paw/ directory (config, bundled scripts, session state) — is excluded from the overlap computation for forward and in-flight conflicts. Every worktree shares this untracked scaffolding, so its presence in modified_files is not evidence of a code conflict: two agents both reporting .git-paw/config.toml never fabricates a warning, while a genuine source overlap alongside it (src/a.rs present in both) still conflicts on the source path.

Forward conflict

Two agents publish agent.intent payloads whose files arrays overlap. The conflict is forward because it surfaces before either agent commits — intent is the early-warning channel.

Trigger. Any non-empty intersection between two active intents (an intent stays active until its valid_for_seconds TTL expires or the agent publishes a fresh intent, which replaces it). Each pair is warned once; the warning can fire again only after one side’s intent changes its file set or expires.

Action. Each agent receives an agent.feedback with the [conflict-detector] tag in its errors[0], naming the peer and listing the overlapping paths. Neither side blocks; the receiving agents decide how to retract or reshape their plans.

Region granularity (v0.6.0). When both intents declare regions for a shared file (function / class / block / range — see Declaring regions), the detector triggers only when the declared regions intersect, and the warning names the intersecting regions. When either side omits regions it falls back to the file-level trigger above, preserving v0.5.0 behaviour. Cross-kind comparisons (a named region vs a line range) intersect conservatively and append a hint.

Toggle. [supervisor.conflict] warn_on_intent_overlap (default true). When false, the detector still records intents (so in-flight classification and ownership detection keep working) but no agent.feedback fires for forward shape.

In-flight conflict

Two agents’ current agent.status modified_files sets overlap on the same file. The conflict is in-flight because both sides are already writing.

Trigger. Any non-empty intersection between one agent’s latest agent.status modified_files and another agent’s latest agent.status modified_files (each new status replaces that agent’s previous set). agent.artifact does not feed this check, and declared intents only decide how an unresolved overlap is classified (below).

Action. Both agents receive [conflict-detector]-tagged agent.feedback. The detector starts a window_seconds timer; if no side retracts before it elapses, the detector classifies the overlap from the two agents’ declared intent regions for the shared file and acts:

  • True collision — the declared regions intersect (same named region, same insertion anchor, overlapping line ranges, or a conservative cross-kind match), or either side declared no regions for the file (a file-level intent, or no active intent at all). The detector escalates to the supervisor inbox via agent.question (see Supervisor Inbox Routing).
  • Additive overlap — both agents declared regions for the file and the region sets are disjoint (well-separated hunks or differently named regions). The detector downgrades to a single informational [conflict-detector]-tagged agent.feedback delivered to both agents (“shared file, additive — resolve at merge”) and does not escalate to the human. The overlap is still recorded: the triple is marked decided so it neither re-escalates nor re-emits the downgrade on later ticks, and it clears only when one agent stops touching the file — so an additive overlap is never silently dropped.

The downgrade requires both sides to have declared regions; whenever the region data is insufficient to prove the hunks are disjoint, the detector escalates, preserving the conservative default.

Window. [supervisor.conflict] window_seconds (default 120).

Escalations persist across commits. The overlap is as volatile as modified_files: an agent commits, the watcher publishes an empty set, and the overlap vanishes for a tick or two before the file reappears. The decision — that this pair and this file have already been put to a human — is not volatile, and the detector keeps it separately from the live overlap view. So a commit does not re-escalate a conflict that has already been escalated, and it does not rewind the window timer for one that has not: an overlap first seen 100 seconds ago still escalates at the 120-second mark, not 120 seconds after the commit. A decision is discarded only once its conflict is genuinely over — the overlap has been continuously absent for the escalation window (at least 60 seconds) — or when the two agents’ declared regions for the file describe a materially different overlap.

Acknowledging an escalation. Once you have adjudicated an escalation, record it so the detector stops asking:

.git-paw/scripts/sweep.sh conflict-ack <agent-a> <agent-b> <file> "optional note"

This publishes a [conflict-ack]-tagged agent.feedback from supervisor to both agents. A note you pass rides along as a second errors entry so the agents see your reasoning; if you pass none, the helper supplies a default one — agents are taught that errors are problems to fix, and a bare tag line would read as a cryptic error.

While the acknowledgement stands, no further agent.question is emitted for that pair and file — but the detector keeps tracking the overlap rather than dropping it, so a later escalation can still be built on it. Acknowledging suppresses the question, not the fact. (No view currently renders the in-flight tracker, so “keeps tracking” means exactly that: the detector’s own state, not something you can read off the dashboard.)

The acknowledgement is invalidated automatically when the two agents’ region declarations for that file change materially — judged by the same intersection rules that drive the additive-versus-true classification above. A conflict that genuinely changes shape can therefore escalate again: an agent re-declaring agent.intent with a range that now overlaps its peer’s, or widening its claim from a named region to the whole file. An intent merely expiring is not a change — otherwise every settled conflict would re-open one TTL later.

What the supervisor-only rule does and does not do. The detector honours an acknowledgement only when the carrying agent.feedback has from = "supervisor". That filter stops an agent’s ordinary feedback from being mistaken for an adjudication. It is not an authentication boundary: the broker does not authenticate senders, from is self-asserted, and sweep.sh is present in every worktree — so an agent that set out to do so could publish conflict-ack for a conflict it is party to. What limits the damage is visibility, not prevention: an acknowledgement published through the helper reaches both named agents, and every acknowledgement — however it was published — stays in the broker message log. (The detector honours one whatever its envelope agent_id, so a hand-rolled one need not have been delivered to both; the log is what you audit.) Sender authentication is part of the v0.18.0 broker-authority work.

Ownership violation

An agent’s modified_files (from its agent.status) touches a file that another agent has claimed in its active agent.intent. Ownership is intent-based: a file is owned by whichever agent’s non-expired intent declares it.

Trigger. Any path in the agent’s modified_files that lies outside its own active intent (or the agent has no active intent) and inside another agent’s active intent.

Action. The violator receives agent.feedback naming the touched file and the agent whose intent claims it; each violator/file pair is warned once. When [supervisor.conflict] escalate_on_violation = true (default), the supervisor inbox also receives a follow-up agent.question so a human can decide whether to override the boundary or block the work.

Exemption. The shared spec-task-tracking artifact — the tasks file git-paw directs every agent to update with its own checkbox (a Spec Kit specs/<feature>/tasks.md or the OpenSpec tasks.md) — is exempt from ownership-violation detection. git-paw’s own writeback protocol has every agent tick its own line in that single shared file, so a writeback there is expected coordination, not a collision. The exemption only covers that designated task-tracking artifact; ownership violations on source files are still reported.

The [conflict-detector] Tag

Every auto-emitted agent.feedback from the detector carries a single errors entry that starts with a fixed tag:

{
  "type": "agent.feedback",
  "agent_id": "feat-auth",
  "payload": {
    "from": "supervisor",
    "errors": [
      "[conflict-detector] forward conflict: agent feat-api also intends to modify: src/auth/middleware.rs"
    ]
  }
}

The [conflict-detector] prefix distinguishes detector output from human-typed supervisor feedback. Agents (and dashboards) that filter or classify feedback can match on the tag without parsing payload semantics.

One further tag travels the other way. An agent.feedback from supervisor whose errors entry reads [conflict-ack] <agent-a> <agent-b> <file> is read by the detector as an acknowledgement of that conflict (see In-flight conflict); sweep.sh conflict-ack shapes it for you. The file path runs to the end of its errors entry, so the tag line carries nothing after the path — add any prose as a separate entry. The detector never emits [conflict-ack] itself.

The detector publishes from the "supervisor" agent ID (the from field in the payload) — same source as human-authored supervisor feedback — because routing and display logic already specialise on the supervisor identity. The tag, not the source, is the discriminator.

Supervisor Inbox Routing

When an in-flight conflict has not resolved within window_seconds and is classified a true collision (see In-flight conflict), the detector escalates to the supervisor by publishing an agent.question addressed to the supervisor (not to either of the conflicting agents). Additive overlaps are downgraded to an agent.feedback and never land in this inbox. The orchestrator (your own agent session outside the tmux session) sees the question in its broker inbox and can:

  1. Type a reply, which the supervisor skill forwards to both involved agents via tmux send-keys (the same dual-write pattern documented in Agent Coordination § Supervisor Acknowledgement).
  2. Resolve the conflict directly by editing one agent’s intent or pausing the offending agent until the other side commits.

Ownership-violation escalations follow the same routing. Forward conflicts never escalate — they are advisory.

Interaction with the Filesystem Watcher

The broker’s filesystem watcher polls git status --porcelain in each worktree every ~2 seconds and publishes agent.status (with a fresh modified_files array) whenever the set of reported paths — modified, staged, or untracked — changes. The conflict detector consumes these auto-published status messages, so in-flight conflicts surface as soon as edits land on disk — no manual agent.status curl required from the agent.

The watcher only reads git status — it never stages or commits — so the detector sees overlaps within one poll of a file being modified, even before git add or git commit. This is the mechanism that makes “in-flight” meaningfully earlier than the post-commit hook’s agent.artifact { status: "committed" }.

Configuration Knobs

All knobs live under [supervisor.conflict] in .git-paw/config.toml:

[supervisor.conflict]
window_seconds = 120
warn_on_intent_overlap = true
escalate_on_violation = true
FieldDefaultDescription
window_seconds120Seconds to wait before escalating an unresolved in-flight conflict to the supervisor inbox via agent.question.
warn_on_intent_overlaptrueForward-conflict feedback toggle. When false, intents are still tracked but no agent.feedback is emitted on intent overlap.
escalate_on_violationtrueOwnership-violation escalation toggle. When false, the violator still receives agent.feedback, but no follow-up agent.question lands in the supervisor inbox.

The [supervisor.conflict] table is fully optional. Setting [supervisor] enabled = false (or omitting the section) disables the detector subsystem entirely — no auto-emitted feedback fires regardless of the values above. See Configuration → Conflict detector tuning for the canonical field reference.