feat(skills): add bundled merge-reconciler skill for neutral multi-agent conflict resolution

Adds skills/autonomous-ai-agents/merge-reconciler — a bundled skill teaching
a neutral third-party agent to resolve git merge conflicts between two
agents' branches: gather both diffs + intents, classify each hunk
(disjoint-intent / same-question-different-answer / superseded), resolve
under an impartiality contract, verify, and hand back a per-hunk summary.
Procedure was live-tested end-to-end against a real conflict fixture.

Includes contract tests (tests/skills/test_merge_reconciler_skill.py) and a
kanban docs cross-reference (en + zh-Hans): assign a third neutral profile a
reconciliation card with both conflicted cards as parents.
This commit is contained in:
Teknium
2026-08-10 01:19:25 -07:00
parent 8edcdd1a80
commit 34b6bbb96a
4 changed files with 273 additions and 0 deletions
@@ -0,0 +1,154 @@
---
name: merge-reconciler
description: "Neutral third-party resolution of agent merge conflicts."
version: 1.0.0
author: Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [Multi-Agent, Git, Merge-Conflict, Kanban, Arbitration]
related_skills: [hermes-agent]
---
# Merge Reconciler
Resolve a git merge conflict between two AGENTS' branches as an impartial third
party. Agents resolving conflicts against a peer's work reliably either
overwrite the peer or abandon their own change — they lack the peer's context
and are biased toward their own side. This skill is the fix: a neutral
reconciler that receives both diffs plus both sides' stated intents and
produces a merged result, like a merge-queue arbiter.
## When to Use
- Two agent branches/worktrees collide during a parallel campaign (kanban
engineering pipeline, parallel-PR wave, multi-worktree refactor).
- `git merge` or `git rebase` halts on conflicts between two agents' work and
neither original agent should self-adjudicate.
- Do NOT use for conflicts within a single agent's own work, or for trivial
lockfile/generated-file conflicts (regenerate those instead).
## Prerequisites
- A repo checkout containing the halted merge, or the two branch names plus
permission to run the merge yourself.
- Both sides' intent sources: kanban completion summaries (`terminal` running
`hermes kanban show <task-id>`), PR bodies, or at minimum each branch's
commit messages.
- The project's build/test command, if one exists.
## How to Run
**Standalone** — a human (or agent) invokes this skill inside the conflicted
repo: load the skill, then follow the Procedure top to bottom.
**Spawned neutral agent** — the preferred shape in multi-agent campaigns:
- `delegate_task`: spawn a subagent whose task message contains the repo path,
both branch names, and both sides' intent summaries verbatim, plus an
instruction to follow this skill.
- Kanban-native: create a reconciliation card assigned to a **third profile**
(not either worker's profile) with BOTH conflicted cards linked as parents —
`kanban_create(title="reconcile branch-a x branch-b", assignee="reconciler",
parents=["t_a", "t_b"])`. The parent links carry both sides' completion
summaries into the reconciler's context automatically; the card body should
name the repo path and the two branches.
## Quick Reference
| Hunk class | Definition | Resolution |
|---|---|---|
| disjoint-intent | The two changes serve different goals and can coexist | Combine both |
| same-question-different-answer | Both sides answered one design question differently | Pick ONE per stated intents; surface the decision |
| superseded | One side's premise no longer holds after the other's change | Keep the surviving side; note why |
Impartiality contract: never favor the side that spawned you; touch ONLY
conflicted regions (no drive-by edits); every design-question pick must appear
explicitly in the hand-back summary.
## Procedure
### 1. Gather both sides
- Run via `terminal`: `git status` (confirm the conflicted state and list
conflicted files), `git merge-base <A> <B>`, then for each side
`git log --oneline <base>..<side>` and `git diff <base>..<side> -- <file>`
for every conflicted file. In a halted merge, `HEAD` is one side and
`MERGE_HEAD` is the other.
- Collect each side's intent: `hermes kanban show <task-id>` for completion
summaries/metadata, or the PR body, or the commit messages from the log
above. Write down one sentence of intent per side before touching any file.
- Done when: you can state both intents in your own words and have both diffs
for every conflicted file.
### 2. Classify every conflicted hunk
- Open each conflicted file with `read_file` and locate each
`<<<<<<<`/`=======`/`>>>>>>>` block.
- Assign each hunk exactly one class from the Quick Reference table, judging
by the stated intents — not by which change looks nicer.
- If a single hunk contains multiple independent decisions (e.g., new logic
that combines cleanly PLUS a styling/rounding choice both sides answered
differently), decompose it into sub-decisions and classify each one.
- A single file often mixes classes: one hunk may be a design collision while
a neighboring hunk is disjoint. Classify per hunk, not per file.
- Done when: every hunk has a written class and a one-line rationale.
### 3. Resolve under the impartiality contract
- Edit each hunk with `patch` (or `write_file` for whole-file rewrites):
- disjoint-intent → merge both changes so each intent is fully served.
- same-question-different-answer → pick the answer that best serves the
STATED intents (e.g., an intent of "strict validation" beats "quick
default" if the task required correctness). Never split the difference
into a hybrid neither side asked for.
- superseded → keep the surviving side; delete the dead premise.
- Never favor the side that spawned you. If intents genuinely tie, escalate
(block the kanban card / report back) rather than guess.
- Change nothing outside conflict markers — no formatting, renames, or
opportunistic fixes.
- `git add` each resolved file via `terminal`.
- Done when: `search_files` finds no `<<<<<<<` markers in the repo and every
resolved file is staged.
### 4. Verify
- Run the project's build/tests via `terminal`; at minimum import/execute the
touched modules. Both intents must be observable in the merged behavior
(e.g., side A's new semantics AND side B's disjoint addition both present).
- Complete the merge: `git commit` (the default merge message plus a body
listing hunk decisions is fine).
- Done when: verification passes and the merge commit exists.
### 5. Hand back
- Produce a completion summary naming EVERY hunk decision:
`file:lines — class — which side(s) kept — rationale`. For every
same-question-different-answer hunk, state the design question and the
answer you picked so a human can veto it — never bury a design call.
- Kanban: `kanban_complete(summary=...)`. Standalone: print the summary.
- Done when: the summary is delivered and lists all hunks.
## Pitfalls
- **Self-favoring**: if you were spawned by one of the conflicting agents,
you are structurally biased — state this and weigh the other side's intent
deliberately. Prefer the third-profile shape so this never arises.
- **Splitting the difference** on a design collision produces a hybrid nobody
designed; pick one answer and surface it.
- **Per-file classification**: files usually mix hunk classes; classifying a
whole file as one class silently drops a disjoint change.
- **Drive-by edits** make the merge unreviewable and steal decisions from the
original agents.
- **Missing intents**: commit messages alone can be thin; prefer kanban
completion summaries or PR bodies. If neither side's intent is recoverable,
escalate instead of guessing.
## Verification
- `git status` shows a clean tree on the target branch with a merge commit.
- No conflict markers remain (`search_files` pattern `<<<<<<<`).
- Build/tests pass; both sides' intents are demonstrably present or the
dropped one is explicitly named in the summary.
- The hand-back summary enumerates every hunk with class and rationale.
+102
View File
@@ -0,0 +1,102 @@
"""Contract checks for the bundled merge-reconciler skill asset.
Reads only the SKILL.md markdown asset — no .py source reads, no network.
"""
import re
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
SKILL_PATH = (
REPO_ROOT
/ "skills"
/ "autonomous-ai-agents"
/ "merge-reconciler"
/ "SKILL.md"
)
REQUIRED_SECTIONS = [
"## When to Use",
"## Prerequisites",
"## How to Run",
"## Quick Reference",
"## Procedure",
"## Pitfalls",
"## Verification",
]
def _frontmatter_and_body():
content = SKILL_PATH.read_text(encoding="utf-8")
assert content.startswith("---"), "SKILL.md must open with frontmatter"
m = re.search(r"\n---\s*\n", content[3:])
assert m, "frontmatter must close with ---"
fm_text = content[3 : m.start() + 3]
body = content[m.end() + 3 :]
fm = {}
for line in fm_text.splitlines():
km = re.match(r"^(\w[\w-]*):\s*(.*)$", line)
if km:
fm[km.group(1)] = km.group(2).strip().strip('"')
return fm, body
def test_skill_file_exists():
assert SKILL_PATH.is_file()
def test_frontmatter_required_fields():
fm, _ = _frontmatter_and_body()
for field in ("name", "description", "version", "author", "license", "platforms"):
assert field in fm, f"missing frontmatter field: {field}"
assert fm["name"] == "merge-reconciler"
def test_description_hardline():
fm, _ = _frontmatter_and_body()
desc = fm["description"]
assert len(desc) <= 60, f"description is {len(desc)} chars; hardline is 60"
assert desc.endswith("."), "description must end with a period"
assert desc.count(".") == 1, "description must be one sentence"
def test_required_sections_present_in_order():
_, body = _frontmatter_and_body()
positions = []
for section in REQUIRED_SECTIONS:
idx = body.find(section)
assert idx != -1, f"missing section: {section}"
positions.append(idx)
assert positions == sorted(positions), "sections out of required order"
def test_body_size_within_bundled_norms():
content = SKILL_PATH.read_text(encoding="utf-8")
lines = content.count("\n") + 1
assert 80 <= lines <= 260, f"SKILL.md is {lines} lines; expected ~100-200"
def test_references_native_hermes_tools():
_, body = _frontmatter_and_body()
for tool in ("`terminal`", "`read_file`", "`patch`", "`delegate_task`"):
assert tool in body, f"body must reference native tool {tool}"
def test_classification_taxonomy_present():
_, body = _frontmatter_and_body()
for cls in (
"disjoint-intent",
"same-question-different-answer",
"superseded",
):
assert cls in body, f"missing hunk class: {cls}"
def test_impartiality_contract_stated():
_, body = _frontmatter_and_body()
assert "never favor" in body.lower()
assert "drive-by" in body.lower()
def test_no_machine_local_paths():
content = SKILL_PATH.read_text(encoding="utf-8")
assert "/home/" not in content
@@ -933,6 +933,19 @@ EOF
The remediation worker spawns with the original card's summary and metadata (changed files, decisions) already in context, plus the fresh evidence you put in the body.
### Reconciling colliding worker branches
In engineering pipelines (P1/P2 with worktrees), two workers' branches can
conflict when merged. Don't let either worker self-adjudicate — the colliding
agent lacks its peer's context and reliably overwrites the other side or
abandons its own. Instead, create a reconciliation card assigned to a **third,
neutral profile** with **both** conflicted cards linked as parents: the parent
links carry both sides' completion summaries into the reconciler's context, so
it receives both diffs *and* both intents. The bundled
[`merge-reconciler` skill](https://github.com/NousResearch/hermes-agent/blob/main/skills/autonomous-ai-agents/merge-reconciler/SKILL.md)
gives that worker the full procedure: classify each conflicted hunk, resolve
impartially, verify, and hand back a summary naming every decision.
## Multi-tenant usage
When one specialist fleet serves multiple businesses, tag each task with a tenant:
@@ -689,6 +689,10 @@ EOF
修复 worker 启动时,原卡片的 summary 和 metadata(改动的文件、决策)已在其上下文中,再加上你写入正文的新证据。
### 调解冲突的 worker 分支
在工程流水线(使用 worktree 的 P1/P2)中,两个 worker 的分支合并时可能发生冲突。不要让任一 worker 自行裁决 —— 发生冲突的 agent 缺乏对方的上下文,往往会覆盖对方的改动或放弃自己的改动。正确做法是:创建一张调解卡片,指派给**第三个中立配置文件**,并把**两张**冲突卡片都链接为其父任务:父任务链接会把双方的完成摘要带入调解者的上下文,使其同时获得双方的 diff *和*双方的意图。内置的 [`merge-reconciler` 技能](https://github.com/NousResearch/hermes-agent/blob/main/skills/autonomous-ai-agents/merge-reconciler/SKILL.md) 为该 worker 提供完整流程:对每个冲突块分类、公正地解决、验证,并在交回摘要中说明每一项决定。
## 多租户使用
当一个专家团队为多个业务提供服务时,为每个任务添加租户标签: