Harmony
Harmony is Muse's conflict-resolution memory. Where Git's rerere is a mechanical exact-match cache keyed on raw bytes, Harmony is a four-tier intelligence layer: declarative policies fire first, then exact-replay from a content-addressed store, then domain-semantic similarity scoring, and finally escalation with a full audit trail. Every resolved conflict is learned; human-verified resolutions are trusted highest.
Overview
Every conflict that surfaces during muse merge is fingerprinted and run
through the four-tier engine. The engine returns one of three statuses:
| Status | Meaning |
|---|---|
"applied" | Resolution was found and written to the working tree automatically |
"proposed" | Candidate found but confidence is below auto-apply threshold — human confirms |
"escalated" | No resolution found; an EscalationRecord is created and the conflict waits |
When you resolve a conflict manually and commit, Harmony automatically learns the
outcome (confidence = 1.0, human_verified = true). That resolution
wins all future rankings for the same pattern.
# idiomatic workflow muse merge feature-x --json # ✔ [harmony] auto-resolved: config.py::MAX_CONNECTIONS ← Tier 2 replay # CONFLICT: src/server.py::timeout ← new — no prior pattern muse checkout --ours src/server.py muse code add src/server.py muse commit -m "merge: resolve timeout conflict" \ --agent-id claude-code --model-id claude-sonnet-4-6 --sign # ✅ harmony: recorded resolution for 'src/server.py::timeout' # next merge — same conflict auto-resolved muse merge feature-y --json # ✔ [harmony] auto-resolved: src/server.py::timeout
End-to-end scenario
This scenario shows a repo going from zero Harmony state to fully automated
conflict resolution in two merges. Two agents each edit
config.py::MAX_CONNECTIONS — the same key, different values.
Merge 1 — first time seeing this conflict
muse merge feature-a --json
{
"status": "conflict",
"auto_resolved": [],
"conflicts": [
{
"path": "config.py::MAX_CONNECTIONS",
"harmony_tier": null,
"status": "conflict",
"pattern_id": "a4f3c9d8e1b2...",
"note": "new pattern — no prior resolution"
}
]
}
Check what each side has, then pick a resolution:
# Ours: MAX_CONNECTIONS = 20 Theirs: MAX_CONNECTIONS = 50
# The feature branch value is higher — accept it
muse checkout --theirs config.py
muse code add config.py
muse commit -m "merge: accept feature-a MAX_CONNECTIONS=50" \
--agent-id claude-code --model-id claude-sonnet-4-6 --sign
[dev sha256:4e8b2f1a...] merge: accept feature-a MAX_CONNECTIONS=50
✅ harmony: recorded resolution for 'config.py::MAX_CONNECTIONS'
pattern_id: a4f3c9d8e1b2...
resolution_id: 7c1a9e3f4d82...
confidence: 1.0
human_verified: true
Inspect what Harmony now knows
muse harmony patterns --json | python3 -c "import sys,json; [print(p['pattern_id'], p['path']) for p in json.load(sys.stdin)['patterns']]"
a4f3c9d8e1b2... config.py::MAX_CONNECTIONS
muse harmony best --pattern-id a4f3c9d8e1b2... --json
{
"resolution_id": "7c1a9e3f4d82...",
"strategy": "human",
"outcome_blob": "sha256:b9e3f1a4c7d2...",
"human_verified": true,
"confidence": 1.0,
"applied_count": 0,
"rationale": "human commit on 2026-04-21"
}
Merge 2 — same conflict, now auto-resolved at Tier 2
muse merge feature-b --json
{
"status": "ok",
"auto_resolved": [
{
"path": "config.py::MAX_CONNECTIONS",
"harmony_tier": 2,
"status": "applied",
"resolution_id": "7c1a9e3f4d82...",
"confidence": 1.0,
"applied_count": 1
}
],
"conflicts": []
}
The merge completes with no conflicts. Harmony replayed the human-verified resolution
from Merge 1 and incremented applied_count. Each subsequent replay
further builds confidence that this is the right resolution for this pattern.
ConflictPattern
A ConflictPattern is the identity record for a conflict. Its
pattern_id is the lookup key for the entire resolution store.
| Field | Type | Description |
|---|---|---|
pattern_id | str (64 hex) | Content-addressed ID — SHA-256 of path + fingerprint combination |
path | str | Workspace-relative POSIX path (or file.py::Symbol for code domain) |
domain | str | Domain name: "code", "identity", "mist", … |
conflict_type | str | ConflictType constant or domain-defined string |
blob_fingerprint | str (64 hex) | SHA-256(sorted(ours_id, theirs_id)) |
semantic_fingerprint | str (64 hex) | Domain-provided via HarmonyPlugin.conflict_fingerprint(); falls back to blob_fingerprint |
ours_id | str | SHA-256 object ID of the "ours" blob |
theirs_id | str | SHA-256 object ID of the "theirs" blob |
description | dict | Domain-specific structured metadata about the conflict |
recorded_at | datetime (UTC) | When this pattern was first observed |
recorded_by | str | Agent ID or "human" |
Fingerprinting and pattern_id keying
Two fingerprints identify every conflict:
# blob fingerprint — commutative; order of ours/theirs doesn't matter lo, hi = sorted((ours_id, theirs_id)) blob_fp = SHA256({"ids": [lo, hi]}) # pattern_id — keyed on semantic_fp when a plugin provides it if semantic_fp != blob_fp: # cross-content replay: different bytes, same semantic shape → same pattern pattern_id = SHA256({"path": path, "semantic_fp": semantic_fp}) else: # no plugin — exact-replay mode only pattern_id = SHA256({"blob_fp": blob_fp, "path": path, "semantic_fp": semantic_fp})
semantic_fp != blob_fp, two conflicts with completely different byte
content but the same semantic shape share a single pattern and replay the
same resolution. Without a plugin, Harmony degenerates to exact-replay — Git rerere
semantics, but with richer storage and full provenance.
Resolution
A Resolution records what the correct answer was for a given
ConflictPattern. One pattern can accumulate many resolutions;
best_resolution() always returns the highest-quality one.
| Field | Type | Description |
|---|---|---|
resolution_id | str (64 hex) | Content-addressed; derived from the resolution's data |
pattern_id | str (64 hex) | Links back to the ConflictPattern this resolves |
strategy | str | ResolutionStrategy: "policy" "exact_replay" "semantic" "human" |
policy_id | str | null | Set when strategy == "policy" |
outcome_blob | str (sha256:…) | Object ID of the resolved state — written to working tree on replay |
resolved_by | AgentProvenance | Agent ID + model ID of whoever produced the resolution |
human_verified | bool | true when a human committed the resolution; always beats unverified in ranking |
confidence | float 0–1 | Engine's confidence in this resolution |
rationale | str | Human-readable reasoning for why this resolution was chosen |
resolved_at | datetime (UTC) | When resolution was recorded |
applied_count | int | Incremented atomically each time this resolution is replayed |
Ranking key
# best_resolution() sort — descending resolutions.sort( key=lambda r: (r.human_verified, r.confidence, r.applied_count), reverse=True ) # human_verified=True always wins regardless of confidence score # then highest confidence # then most frequently applied (battle-tested)
record_resolutions() twice
with the same outcome_blob produces exactly one resolution entry, never
duplicates.
Four tiers
The engine evaluates each conflict in order. The first tier that produces a result wins.
All thresholds are configurable via EngineConfig.
| Tier | Name | Fires when | Status returned |
|---|---|---|---|
| 1 | Policy | A Policy condition matches the pattern's conflict_type, domain, and path_pattern |
"applied" or "proposed" or "escalated" |
| 2 | Exact Replay | A saved Resolution exists for the pattern's pattern_id |
"applied" if confidence ≥ threshold; else "proposed" |
| 3 | Semantic Match | HarmonyPlugin.similarity() scores a related pattern above semantic_threshold |
Always "proposed" — never auto-applied |
| 4 | Escalate | No match in any tier | "escalated" + EscalationRecord written |
EngineConfig defaults
| Field | Default | Description |
|---|---|---|
auto_apply_threshold | 0.85 | Minimum confidence to auto-apply without human confirmation |
semantic_threshold | 0.70 | Minimum similarity score for Tier 3 to return a proposal |
max_proposals | 5 | Maximum number of Tier 3 semantic proposals returned |
Tier 1 detail — Policy evaluation order
Policies are evaluated in scope order: WORKSPACE → REPO →
DOMAIN → FILE. The first matching policy fires.
Actions that are PREFER_OURS or PREFER_THEIRS at
confidence ≥ auto_apply_threshold produce "applied";
below threshold they produce "proposed" with
requires_confirmation: true.
Tier 3 detail — semantic proposals are never auto-applied
Semantic matches are structural analogies — not identical content. Harmony always
requires confirmation for Tier 3 results because two semantically similar conflicts
may need different resolutions. The human confirmation step records the final outcome
and upgrades it to a Tier 2 exact-replay entry with human_verified = true.
muse harmony engine <pattern_id> --json muse harmony engine <pattern_id> --auto-apply-threshold 0.95 --json muse harmony engine <pattern_id> --auto-escalate --json
Policy
Policies are declarative rules that fire before any learned resolution is consulted. They are the fastest path — no fingerprint lookup required.
Policy fields
| Field | Type | Description |
|---|---|---|
policy_id | str (URL-safe, 1–128 chars) | Stable identifier; used as the filename in storage |
description | str | Human-readable explanation of the policy's intent |
when | PolicyCondition | Conditions that must all match (AND semantics) |
action | str | What to do when the policy fires |
confidence | float 0–1 | Used for ranking and min_confidence checks |
escalate_to | str | null | Agent ID or "human" — used when action is ESCALATE |
delegate_to | str | null | Agent ID — used when action is DELEGATE |
scope | str | PolicyScope: "workspace" "repo" "domain" "file" |
created_at | datetime (UTC) | Creation timestamp |
created_by | str | Agent ID or "human" |
PolicyCondition
All non-null fields must match. null means "any value".
| Field | Matching |
|---|---|
conflict_type | Exact string match against pattern.conflict_type; null = any |
domain | Exact string match against pattern.domain; null = any |
path_pattern | fnmatch glob against pattern.path; null = any |
min_confidence | Policy only fires if its own confidence ≥ min_confidence; null = no minimum |
PolicyAction values
| Action | Effect |
|---|---|
PREFER_OURS | Adopt the "ours" version of the conflicting file |
PREFER_THEIRS | Adopt the "theirs" version of the conflicting file |
ESCALATE | Create an EscalationRecord; notify escalate_to |
REQUIRE_HUMAN | Block merge until a human manually resolves |
DELEGATE | Route to the specialist agent in delegate_to |
# always take ours for generated lock files muse harmony policy-add \ --id lock-files-ours \ --action prefer-ours \ --path-pattern "*.lock" \ --confidence 1.0 # always prefer theirs for mist artifacts (immutable content-addressed blobs) muse harmony policy-add \ --id mist-theirs \ --action prefer-theirs \ --domain mist \ --path-pattern "artifacts/**" \ --confidence 0.9 # escalate conflicts in the auth module to a security specialist muse harmony policy-add \ --id auth-escalate \ --action escalate \ --path-pattern "src/auth/**" \ --escalate-to security-agent muse harmony policies --json muse harmony policy-remove --id lock-files-ours --json
Escalation
When Tier 4 fires — or a Policy with action ESCALATE or
REQUIRE_HUMAN fires — Harmony creates an EscalationRecord
and writes an audit entry. The conflict is blocked until the record is resolved.
| Field | Type | Description |
|---|---|---|
escalation_id | str (64 hex) | Deterministic: SHA-256(pattern_id + reason) |
pattern_id | str (64 hex) | The conflict pattern that triggered escalation |
reason | str | Human-readable explanation of why no auto-resolution was possible |
escalated_at | datetime (UTC) | When escalation was created |
escalated_by | AgentProvenance | Agent that triggered the escalation |
status | str | "open" or "resolved" |
resolved_at | datetime | null | When the escalation was closed |
resolved_by | AgentProvenance | null | Who closed it |
resolution_id | str | null | The Resolution that closed this escalation, if any |
muse harmony escalations --json # list all escalations
muse harmony escalations --status open --json # open only
muse harmony resolve-escalation <escalation_id> \
--resolution-id <resolution_id> --json # close with a resolution
Tier 4 → hub issue creation
When Tier 4 fires (or a Policy with action = ESCALATE), Harmony creates
an EscalationRecord locally and, if the escalation policy names
escalate_to, automatically opens a MuseHub issue with the conflict
details. The hub issue is the handoff signal — it surfaces in the repo's issue
tracker so a human or specialist agent can pick it up.
# Harmony calls this internally when escalation fires:
muse -C ~/ecosystem/musehub hub issue create \
--title "Harmony escalation: src/auth/tokens.py::validate_token" \
--body "**Pattern ID**: a4f3c9d8e1b2...\n**Reason**: no resolution found after 3 tiers\n**Conflict type**: concurrent_edit\n**Ours**: sha256:f3a7b9...\n**Theirs**: sha256:c2d8e1..." \
--label "harmony-escalation" \
--assignee security-agent \
--hub http://staging.musehub.ai \
--json
{
"number": 42,
"title": "Harmony escalation: src/auth/tokens.py::validate_token",
"status": "open",
"assignee": "security-agent",
"labels": ["harmony-escalation"],
"created_at": "2026-04-21T16:30:00Z"
}
When the specialist resolves the conflict and commits, Harmony marks the escalation as resolved and closes the hub issue:
# Resolve the conflict manually, then commit
muse code add src/auth/tokens.py
muse commit -m "resolve: auth token validation conflict (escalation #42)" \
--agent-id security-agent --model-id claude-sonnet-4-6 --sign
# ✅ harmony: recorded resolution for 'src/auth/tokens.py::validate_token'
# ✅ harmony: escalation d9b4c1... closed
# ✅ hub: issue #42 closed automatically
HarmonyPlugin interface
Domains plug into Harmony via two optional methods. Without a plugin, Harmony
degenerates to pure exact-replay — the semantic_fingerprint equals
the blob_fingerprint and Tier 3 never fires.
from typing import Protocol, runtime_checkable @runtime_checkable class HarmonyPlugin(Protocol): def conflict_fingerprint( self, path: str, ours_id: str, theirs_id: str, repo_root: Path, ) -> str: """Return a domain-semantic fingerprint for this conflict. If the fingerprint differs from the blob_fingerprint, conflicts with different byte content but identical semantic shape share one pattern and replay the same resolution (cross-content replay). Return blob_fingerprint to opt out of semantic keying. """ ... def similarity(self, fp_a: str, fp_b: str) -> float: """Score semantic similarity between two fingerprints in [0.0, 1.0]. Called by Tier 3. Scores above EngineConfig.semantic_threshold (default 0.70) are returned as proposals. Default plugin: returns 1.0 for identical, 0.0 for all others. """ ...
CodePlugin — shipped implementation
The code domain ships a CodePlugin that tokenizes Python source
using a normalized vocabulary and scores similarity with the Tanimoto coefficient
on token multisets.
# normalization rules applied before fingerprinting # Python keywords → preserved as-is # Identifiers → "ID" # String literals → "STR" # Number literals → "NUM" # Operators/punct → preserved # Comments/whitespace → stripped # Tanimoto similarity on multisets A, B # sim(A, B) = Σ min(A[t], B[t]) / Σ max(A[t], B[t]) # Two functions with different variable names but same control flow # score ~0.85 — above semantic_threshold; proposed but not auto-applied
Size guard: inputs larger than 512 KiB are truncated before tokenization. Fingerprint format: sorted space-separated token list with repetitions preserved (multiset encoding as a string).
Worked example — JsonConfigPlugin
A domain plugin for JSON config files. The semantic fingerprint is keyed on the set of top-level keys that differ, not the raw bytes. Two conflicts that change different values for the same key share one pattern and replay the same resolution.
import hashlib, json
from pathlib import Path
class JsonConfigPlugin:
"""HarmonyPlugin for JSON config files.
Fingerprints on which keys differ — not the values themselves.
Conflicts that change the same key for different reasons share
one pattern and replay the same resolution strategy.
"""
def conflict_fingerprint(
self,
path: str,
ours_id: str,
theirs_id: str,
repo_root: Path,
) -> str:
try:
ours = json.loads((repo_root / (path + ".ours")).read_text())
theirs = json.loads((repo_root / (path + ".theirs")).read_text())
except (FileNotFoundError, json.JSONDecodeError):
return _blob_fp(ours_id, theirs_id) # fall back to exact replay
changed_keys = sorted(
k for k in ours.keys() | theirs.keys()
if ours.get(k) != theirs.get(k)
)
payload = json.dumps({"path": path, "keys": changed_keys},
sort_keys=True).encode()
return hashlib.sha256(payload).hexdigest()
def similarity(self, fp_a: str, fp_b: str) -> float:
# Same changed-key set → same semantic pattern
return 1.0 if fp_a == fp_b else 0.0
def _blob_fp(ours_id: str, theirs_id: str) -> str:
lo, hi = sorted((ours_id, theirs_id))
return hashlib.sha256(json.dumps({"ids": [lo, hi]}).encode()).hexdigest()
Register the plugin by returning it from your domain's harmony_plugin()
method in the MuseDomainPlugin protocol:
class JsonConfigDomain:
domain_name = "json_config"
def harmony_plugin(self) -> JsonConfigPlugin | None:
return JsonConfigPlugin()
Auto-apply flow
Harmony integrates transparently with muse merge. The flow when
--harmony-autoupdate is enabled (the default):
- Merge engine detects conflicts → calls
harmony_auto_apply(root, conflict_paths, …) - For each conflict path: extract file path (handling
file.py::Symbolcode addresses), guard against path traversal, compute blob + semantic fingerprints. - Look up
best_resolution(root, pattern_id). - If found: restore
outcome_blobto working tree, incrementapplied_countatomically, mark as resolved. - If not found: record the pattern for future learning.
- Returns
(resolved_manifest, remaining_conflicts)— remaining conflicts go intoMERGE_STATE.jsonfor manual resolution. - On
muse commitafter manual resolution: Harmony records each remaining conflict's outcome athuman_verified = true,confidence = 1.0.
# disable harmony for one merge (e.g. intentional divergence) muse merge feature-x --no-harmony-autoupdate --json
checkout --ours/--theirs
clears conflict_paths in MERGE_STATE, but original_conflict_paths
is preserved through all writes so muse commit always has the full list to
learn from. You cannot accidentally lose learning opportunities by staging files.
Storage layout
All Harmony state lives under .muse/harmony/. Every file is JSON.
The layout is content-addressed: pattern IDs and resolution IDs are 64-char hex
strings used directly as directory/file names.
.muse/harmony/
policies/
<policy_id>.json ← one per policy; URL-safe alphanumeric name
patterns/
sha256/
<64-hex-pattern-id>/
pattern.json ← ConflictPattern record (max 32 KiB)
resolutions/
sha256/
<64-hex>.json ← one Resolution per file (max 16 KiB)
audit/
<YYYYMMDD>-<hex-slice>.json ← append-only; newest-first by filename sort
escalations/
sha256/
<64-hex>.json ← one EscalationRecord per file (max 16 KiB)
Size limits
| Entity | Limit |
|---|---|
| pattern.json | 32 KiB |
| Resolution record | 16 KiB |
| Policy record | 8 KiB |
| Audit entry | 4 KiB |
| Escalation record | 16 KiB |
| Semantic fingerprint | 4 KiB |
| Patterns scanned by list_patterns() | 100,000 |
| Policies loaded by list_policies() | 1,000 |
| Audit entries returned by list_audit() | 10,000 |
Audit log
Every engine action writes an append-only audit entry. Entries are independent JSON
files — never modified after creation. The tamper-evident property comes from
content-addressing: each audit_id is the SHA-256 of the entry's canonical
JSON, so any modification invalidates the ID.
Tamper-evidence mechanism
The audit_id is computed deterministically from the entry's own data —
not a random UUID. This means a verifier can always recompute the ID and compare it
against what is stored:
import hashlib, json
entry = {
"event_type": "resolution_applied",
"pattern_id": "a4f3c9d8e1b2...",
"resolution_id": "7c1a9e3f4d82...",
"policy_id": None,
"acted_by": {"agent_id": "claude-code", "model_id": "claude-sonnet-4-6"},
"occurred_at": "2026-04-21T16:00:00Z",
"metadata": {"confidence": 1.0, "applied_count_after": 1},
}
canonical = json.dumps(entry, sort_keys=True, separators=(",", ":"))
audit_id = hashlib.sha256(canonical.encode()).hexdigest()
# audit_id is the filename: .muse/harmony/audit/20260421-a4f3c9.json
A concrete audit entry as written to disk:
{
"audit_id": "8f3a1ce29b7d4510f2a6e8c9d0b1a3f4e5c6d7e8f9a0b1c2d3e4f5a6b7c8d9",
"event_type": "resolution_applied",
"pattern_id": "a4f3c9d8e1b2...",
"resolution_id": "7c1a9e3f4d82...",
"policy_id": null,
"acted_by": { "agent_id": "claude-code", "model_id": "claude-sonnet-4-6" },
"occurred_at": "2026-04-21T16:00:00Z",
"metadata": { "confidence": 1.0, "applied_count_after": 1 }
}
To verify the log is intact, recompute SHA-256(canonical_json) for each
entry and check it matches the stored audit_id. Any entry that has been
modified will produce a different hash and fail the check.
AuditEventType constants
| Event type | When it fires |
|---|---|
"pattern_recorded" | A new ConflictPattern was observed and stored |
"resolution_saved" | A Resolution was written (manual commit or engine proposal accepted) |
"resolution_applied" | A Resolution was auto-applied from the store (Tier 2 replay) |
"escalation_recorded" | Tier 4 or a Policy with ESCALATE action fired |
"escalation_resolved" | An open EscalationRecord was closed |
"pattern_forgotten" | A pattern was removed (GC or explicit forget) |
"policy_saved" | A Policy was created or updated |
"policy_removed" | A Policy was deleted |
"gc_run" | muse harmony gc completed |
"clear_run" | muse harmony clear wiped all patterns |
muse harmony audit --json muse harmony audit --limit 50 --json
Each entry carries: audit_id, event_type,
pattern_id, resolution_id, policy_id,
acted_by, occurred_at, and metadata
(event-specific structured data). Fields not relevant to a given event type are null.
CLI reference
All commands accept --json.
| Task | Command |
|---|---|
| List conflict patterns | muse harmony patterns --json |
| List resolutions for a pattern | muse harmony resolutions --pattern-id <id> --json |
| Best resolution for a pattern | muse harmony best --pattern-id <id> --json |
| Find semantically similar patterns | muse harmony similar --pattern-id <id> --json |
| Run engine on a pattern | muse harmony engine <pattern_id> --json |
| Engine with custom threshold | muse harmony engine <id> --auto-apply-threshold 0.95 --json |
| Add a policy | muse harmony policy-add --id <id> --action prefer-ours --path-pattern "*.lock" |
| List policies | muse harmony policies --json |
| Remove a policy | muse harmony policy-remove --id <id> --json |
| View audit log | muse harmony audit --json |
| View open escalations | muse harmony escalations --status open --json |
| Close an escalation | muse harmony resolve-escalation <id> --resolution-id <rid> --json |
| Forget a pattern | muse harmony forget --pattern-id <id> --json |
| GC unresolved stale patterns | muse harmony gc --age-days 90 --json |
| Clear all patterns | muse harmony clear --json |
| Disable harmony for one merge | muse merge <branch> --no-harmony-autoupdate |
Idiomatic Harmony workflow
# 1. Merge — Harmony auto-applies saved resolutions muse merge feature-x --json # ✔ [harmony] auto-resolved: config.py::MAX_CONNECTIONS # CONFLICT: src/server.py::timeout # 2. Inspect what Harmony knows muse harmony patterns --json muse harmony resolutions --pattern-id <id> --json # 3. Resolve remaining conflicts manually muse checkout --ours src/server.py muse code add src/server.py # 4. Commit — Harmony records the resolution (confidence=1.0, human_verified=true) muse commit -m "merge: resolve timeout conflict" \ --agent-id claude-code --model-id claude-sonnet-4-6 --sign # 5. Next time the same conflict recurs → Tier 2 auto-resolved muse merge feature-y --json # ✔ [harmony] auto-resolved: src/server.py::timeout