# Conflict Resolution *What a conflict looks like inside Aura, and how to resolve it from the CLI or API.* ## Overview A conflict is not a failure. It is the merge engine telling you the truth: two humans made decisions that cannot be reconciled without a human's judgment. Aura's job is to raise the smallest possible conflict, on the smallest possible node, with all three candidate versions in hand — so the judgment is cheap and the blast radius is tiny. This page documents the anatomy of a conflict in Aura: its record structure, how it surfaces across CLI and API, and the full set of resolution verbs available. The [interactive conflict picker](/interactive-conflict-picker) covers the terminal UI in detail; this page is about the model underneath. > The best conflict is one a human resolves in under a minute. Shape the conflict to make that true. ## How It Works ### Conflicts are node-scoped, not file-scoped When Aura's merger detects that ours and theirs cannot be combined at some node, it emits a `Conflict` record attached to that node — not to the whole file. A file with ten cleanly-merged changes and one real disagreement surfaces exactly one conflict entry. Every other change has already been applied. The record contains enough to present, reason about, and resolve the conflict without re-parsing the file: ```text Conflict { id: "c_8f21", file: "src/auth.ts", node_path: "module/class(AuthService)/method(login)", kind: "function_declaration", reason: "modify/modify", ancestor: { text, ast_id, sha }, ours: { text, ast_id, sha, author, commit }, theirs: { text, ast_id, sha, author, commit }, children: [ sub-conflicts if any ], hints: [ "disjoint statements? try semantic-union" ], } ``` ### The reason field is authoritative Aura classifies every conflict. The top-level `reason` is one of: | Reason | Meaning | |---------------------|---------| | `modify/modify` | Both sides edited the same node differently. | | `modify/delete` | Ours modified a node theirs deleted (or vice versa). | | `insert/insert` | Both sides inserted different nodes at the same identity slot (e.g. two functions with the same name). | | `rename/rename` | Both sides renamed the same node to different names. | | `type/type` | Structural type mismatch (e.g. object vs array at the same JSON path). | | `move/modify` | One side moved the node, the other made a non-commutative edit at the old location. | | `anchor/anchor` | YAML anchor name collision with different targets. | | `parse/parse` | One side failed to parse; fell back to text merge with conflicts. | The reason drives hints and strategy suggestions, and it is stable — external tooling can branch on it. ### Conflicts compose into trees A single conflict can contain sub-conflicts. Example: both sides rewrote `login()`, and Aura recursed into the function body to see if the edits were disjoint. They were partially disjoint — three statements merged cleanly, two conflict on the same if-branch. The top-level conflict on `login()` is marked with `children: [...]`, and those children are leaf conflicts scoped to the if-branch. Resolving the children auto-resolves the parent; resolving the parent overrides the children. ### Resolution verbs Every conflict accepts the same verbs. The verb set is small on purpose: | Verb | Result | |--------------|--------| | `keep-ours` | Apply ours's version at this node; discard theirs's edit. | | `take-theirs`| Apply theirs's version; discard ours's. | | `keep-both` | Apply both side by side when the node kind allows it (sequence items, overloads, multiple top-level declarations). Raises an error when it does not (scalar values, single fields). | | `edit` | Open an editor on the merged text with standard conflict markers and accept whatever is saved. | | `revert` | Fall back to the ancestor version — useful when both sides were wrong. | | `defer` | Leave the conflict in place, write the file with merge markers, and let the user handle it outside Aura. | Each verb is an atomic operation. Resolutions are recorded in the merge session's audit log with the resolver's identity, timestamp, and the final node SHA. ### The merge session A merge is not a single command — it is a session. When you run `aura merge `, Aura: 1. Computes all conflicts upfront and writes them to `.aura/merge//conflicts.json`. 2. Applies all non-conflicting changes to the working tree. 3. Marks the session `in-progress` in the repo metadata. 4. Exits, or enters the [interactive picker](/interactive-conflict-picker) if `--interactive` is set. Subsequent commands (`aura conflicts list`, `aura resolve`, `aura merge continue`, `aura merge abort`) operate against this session. The session commits only when all conflicts are resolved. ## Examples ### A: Listing conflicts from the CLI ```bash $ aura conflicts list session: merge/feature-login -> main (14 min ago) status: 3 conflicts, 11 auto-merged c_8f21 src/auth.ts :: AuthService.login modify/modify c_8f22 src/auth.ts :: AuthService.logout modify/delete c_9a03 package.json :: /version modify/modify 14 files auto-merged. Use `aura resolve ` to resolve individually, or `aura merge --interactive` for the picker. ``` ### B: Inspecting a single conflict ```bash $ aura conflicts show c_8f21 CONFLICT c_8f21 file: src/auth.ts node: AuthService.login (method) reason: modify/modify --- ancestor ------------------------------------- async login(user: string, pass: string) { return this.db.verify(user, pass); } --- ours (HEAD, Alice, 2h ago) -------------------- async login(user: string, pass: string) { const hashed = await bcrypt.hash(pass); return this.db.verify(user, hashed); } --- theirs (feature/login, Bob, 40m ago) ---------- async login(user: string, pass: string): Promise { this.audit.track("login_attempt", user); return this.db.verify(user, pass); } hints: - both edits are disjoint statements inside login() - try: aura resolve c_8f21 --strategy semantic-union ``` ### C: Resolving non-interactively ```bash $ aura resolve c_8f21 --strategy semantic-union resolved c_8f21: semantic-union merged body contains: hashed-password guard + audit tracking $ aura resolve c_8f22 keep-ours resolved c_8f22: keep-ours (logout removed on theirs, retained on ours) $ aura resolve c_9a03 take-theirs resolved c_9a03: version=2.0.0 $ aura merge continue all 3 conflicts resolved. applying merge commit... merged: main <- feature/login (commit 1d4e2a3) ``` ### D: Programmatic resolution Every verb is available over the Aura API, which is what the CLI, MCP tools, and UI share. Pseudocode against the Rust API: ```text let session = MergeSession::load(repo, session_id)?; for conflict in session.conflicts() { match conflict.reason { Reason::ModifyDelete if conflict.ours_was_modified() => session.resolve(conflict.id, Verb::KeepOurs), Reason::ModifyModify if conflict.disjoint_statements() => session.resolve(conflict.id, Verb::Strategy("semantic-union")), _ => session.resolve(conflict.id, Verb::Defer), }?; } session.finalize()?; ``` The shape of this API is stable across the CLI, the MCP tool surface, and the library — the CLI is a thin wrapper. ### E: Git-style markers for `defer` When you `defer` a conflict, Aura writes a file with familiar markers so your editor or downstream tool can handle it: ```text <<<<<<< ours (AuthService.login) async login(user: string, pass: string) { const hashed = await bcrypt.hash(pass); return this.db.verify(user, hashed); } ||||||| ancestor async login(user: string, pass: string) { return this.db.verify(user, pass); } ======= async login(user: string, pass: string): Promise { this.audit.track("login_attempt", user); return this.db.verify(user, pass); } >>>>>>> theirs (feature/login) ``` The ancestor block (`|||||||`) is always included — this is the three-way "diff3" style. Aura tracks these markers: on the next `aura merge continue`, it re-parses and confirms you resolved them. ## Edge Cases **Conflicts across files.** Some operations (cross-file moves, package renames) surface as paired conflicts: one `move` in `a.ts`, one `insert` in `b.ts`. Resolving one auto-resolves the other when possible; otherwise both must be addressed. **Resolution that breaks the build.** Aura's resolver does not run your test suite. It does run `aura prove` on the resolved file to confirm the semantic graph still connects. A resolution that deletes a function referenced elsewhere raises a secondary **impact alert** (see `aura_live_impacts`), surfaced before the merge commit lands. **Partial resolutions.** You can resolve a subset of conflicts, run `aura merge continue --allow-unresolved`, and commit the partial state — but only to a branch, never to a protected branch. Unresolved conflicts are preserved in the session for later. **Abandoning a merge.** `aura merge abort` rewinds the working tree to pre-merge state using the AST-level snapshots taken at session start. No stale markers, no half-applied changes. **Downgraded conflicts.** A conflict initially flagged as `modify/modify` can downgrade if further inspection shows the edits are semantically equivalent after normalization (same logic, different variable names that both sides renamed consistently). Aura's normalizer runs once per conflict and is conservative — it never silently merges genuinely different logic — but it eliminates a meaningful class of false positives, particularly on projects with aggressive linting. **Audit trail.** Every conflict record, every resolution verb, and every `reveal` action on masked content is written to `.aura/merge//audit.jsonl` as an append-only log. The log is committed along with the merge commit when the session finalizes, giving future reviewers complete visibility into how conflicts were decided and by whom. **Concurrent resolutions.** On a team with Mothership Mode, two people can resolve different conflicts in the same session concurrently. The session log is append-only and linearized server-side; the CLI refuses to override a conflict that was resolved by someone else without `--force`. ## See Also - [Interactive conflict picker](/interactive-conflict-picker) - [Merge strategies](/merge-strategies) - [How AST merge works](/how-ast-merge-works) - [JSON deep merge](/json-deep-merge) - [YAML merge](/yaml-merge) - [.env merge](/env-merge) - [Limitations and edge cases](/limitations-and-edge-cases)