"""Muse Emotion-Diff Engine — compare emotion vectors between two commits. Answers: "How did the emotional character of a composition change between two points in history?" An agent composing a new section uses this to detect whether the current creative direction is drifting from the intended emotional arc, and to decide whether to reinforce or contrast the mood. Two sourcing strategies are supported: 1. **Explicit tags** — ``emotion:*`` tags attached via ``muse tag add``. When both commits carry an emotion tag, their vectors are looked up from the canonical :data:`EMOTION_VECTORS` table and compared directly. 2. **Inferred** — When one or both commits lack an emotion tag, the engine infers a vector from available musical metadata (tempo, commit metadata) stored in the :class:`~maestro.muse_cli.models.MuseCliCommit` row. Full MIDI-feature inference (mode, note density, velocity) is tracked as a follow-up; the current implementation uses tempo and tag-derived proxies. Boundary rules -------------- - Must NOT import StateStore, executor, MCP tools, or handlers. - Must NOT import live streaming or SSE modules. - May import ``muse_cli.{db, models}``. """ from __future__ import annotations import logging import math from dataclasses import dataclass from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from maestro.muse_cli.models import MuseCliCommit, MuseCliTag logger = logging.getLogger(__name__) # --------------------------------------------------------------------------- # Emotion vector catalogue # --------------------------------------------------------------------------- #: Canonical 4-D emotion vectors keyed by ``emotion: