Developer Docs Harmony
PHASE 05

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.

Learning loop — Harmony records patterns on every merge and replays resolutions automatically. The more merges your repo processes, the fewer conflicts reach Tier 3 or Tier 4.

Overview

Every conflict that surfaces during muse merge is fingerprinted and run through the four-tier engine. The engine returns one of three statuses:

StatusMeaning
"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

bash merge feature-a into dev
muse merge feature-a --json
json output
{
  "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:

bash inspect conflict, choose theirs, stage, commit
# 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
text commit output — Harmony learns
[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

bash inspect stored pattern and resolution
muse harmony patterns --json | python3 -c "import sys,json; [print(p['pattern_id'], p['path']) for p in json.load(sys.stdin)['patterns']]"
text output
a4f3c9d8e1b2... config.py::MAX_CONNECTIONS
bash inspect the best resolution for this pattern
muse harmony best --pattern-id a4f3c9d8e1b2... --json
json output
{
  "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

bash merge feature-b — same conflict path, different blob IDs
muse merge feature-b --json
json output — no manual intervention needed
{
  "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.

FieldTypeDescription
pattern_idstr (64 hex)Content-addressed ID — SHA-256 of path + fingerprint combination
pathstrWorkspace-relative POSIX path (or file.py::Symbol for code domain)
domainstrDomain name: "code", "identity", "mist", …
conflict_typestrConflictType constant or domain-defined string
blob_fingerprintstr (64 hex)SHA-256(sorted(ours_id, theirs_id))
semantic_fingerprintstr (64 hex)Domain-provided via HarmonyPlugin.conflict_fingerprint(); falls back to blob_fingerprint
ours_idstrSHA-256 object ID of the "ours" blob
theirs_idstrSHA-256 object ID of the "theirs" blob
descriptiondictDomain-specific structured metadata about the conflict
recorded_atdatetime (UTC)When this pattern was first observed
recorded_bystrAgent 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})
When 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.

FieldTypeDescription
resolution_idstr (64 hex)Content-addressed; derived from the resolution's data
pattern_idstr (64 hex)Links back to the ConflictPattern this resolves
strategystrResolutionStrategy: "policy" "exact_replay" "semantic" "human"
policy_idstr | nullSet when strategy == "policy"
outcome_blobstr (sha256:…)Object ID of the resolved state — written to working tree on replay
resolved_byAgentProvenanceAgent ID + model ID of whoever produced the resolution
human_verifiedbooltrue when a human committed the resolution; always beats unverified in ranking
confidencefloat 0–1Engine's confidence in this resolution
rationalestrHuman-readable reasoning for why this resolution was chosen
resolved_atdatetime (UTC)When resolution was recorded
applied_countintIncremented 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)
Idempotent learning — calling 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.

TierNameFires whenStatus 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

FieldDefaultDescription
auto_apply_threshold0.85Minimum confidence to auto-apply without human confirmation
semantic_threshold0.70Minimum similarity score for Tier 3 to return a proposal
max_proposals5Maximum number of Tier 3 semantic proposals returned

Tier 1 detail — Policy evaluation order

Policies are evaluated in scope order: WORKSPACEREPODOMAINFILE. 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

FieldTypeDescription
policy_idstr (URL-safe, 1–128 chars)Stable identifier; used as the filename in storage
descriptionstrHuman-readable explanation of the policy's intent
whenPolicyConditionConditions that must all match (AND semantics)
actionstrWhat to do when the policy fires
confidencefloat 0–1Used for ranking and min_confidence checks
escalate_tostr | nullAgent ID or "human" — used when action is ESCALATE
delegate_tostr | nullAgent ID — used when action is DELEGATE
scopestrPolicyScope: "workspace" "repo" "domain" "file"
created_atdatetime (UTC)Creation timestamp
created_bystrAgent ID or "human"

PolicyCondition

All non-null fields must match. null means "any value".

FieldMatching
conflict_typeExact string match against pattern.conflict_type; null = any
domainExact string match against pattern.domain; null = any
path_patternfnmatch glob against pattern.path; null = any
min_confidencePolicy only fires if its own confidence ≥ min_confidence; null = no minimum

PolicyAction values

ActionEffect
PREFER_OURSAdopt the "ours" version of the conflicting file
PREFER_THEIRSAdopt the "theirs" version of the conflicting file
ESCALATECreate an EscalationRecord; notify escalate_to
REQUIRE_HUMANBlock merge until a human manually resolves
DELEGATERoute 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.

FieldTypeDescription
escalation_idstr (64 hex)Deterministic: SHA-256(pattern_id + reason)
pattern_idstr (64 hex)The conflict pattern that triggered escalation
reasonstrHuman-readable explanation of why no auto-resolution was possible
escalated_atdatetime (UTC)When escalation was created
escalated_byAgentProvenanceAgent that triggered the escalation
statusstr"open" or "resolved"
resolved_atdatetime | nullWhen the escalation was closed
resolved_byAgentProvenance | nullWho closed it
resolution_idstr | nullThe Resolution that closed this escalation, if any
bash list and resolve escalations
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.

bash the exact call Harmony makes to create the hub issue
# 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
json hub response
{
  "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:

bash after manual resolution — close the escalation and 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.

python domains/json_config/harmony_plugin.py
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:

python wiring the plugin into the domain
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):

  1. Merge engine detects conflicts → calls harmony_auto_apply(root, conflict_paths, …)
  2. For each conflict path: extract file path (handling file.py::Symbol code addresses), guard against path traversal, compute blob + semantic fingerprints.
  3. Look up best_resolution(root, pattern_id).
  4. If found: restore outcome_blob to working tree, increment applied_count atomically, mark as resolved.
  5. If not found: record the pattern for future learning.
  6. Returns (resolved_manifest, remaining_conflicts) — remaining conflicts go into MERGE_STATE.json for manual resolution.
  7. On muse commit after manual resolution: Harmony records each remaining conflict's outcome at human_verified = true, confidence = 1.0.
# disable harmony for one merge (e.g. intentional divergence)
muse merge feature-x --no-harmony-autoupdate --json
Original conflict paths preservedcheckout --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

EntityLimit
pattern.json32 KiB
Resolution record16 KiB
Policy record8 KiB
Audit entry4 KiB
Escalation record16 KiB
Semantic fingerprint4 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:

python how audit_id is computed
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:

json .muse/harmony/audit/20260421-a4f3c9.json
{
  "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 typeWhen 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.

TaskCommand
List conflict patternsmuse harmony patterns --json
List resolutions for a patternmuse harmony resolutions --pattern-id <id> --json
Best resolution for a patternmuse harmony best --pattern-id <id> --json
Find semantically similar patternsmuse harmony similar --pattern-id <id> --json
Run engine on a patternmuse harmony engine <pattern_id> --json
Engine with custom thresholdmuse harmony engine <id> --auto-apply-threshold 0.95 --json
Add a policymuse harmony policy-add --id <id> --action prefer-ours --path-pattern "*.lock"
List policiesmuse harmony policies --json
Remove a policymuse harmony policy-remove --id <id> --json
View audit logmuse harmony audit --json
View open escalationsmuse harmony escalations --status open --json
Close an escalationmuse harmony resolve-escalation <id> --resolution-id <rid> --json
Forget a patternmuse harmony forget --pattern-id <id> --json
GC unresolved stale patternsmuse harmony gc --age-days 90 --json
Clear all patternsmuse harmony clear --json
Disable harmony for one mergemuse 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