"""The alteration engine: a knob vector -> a layered RenderPlan (design §4, §5). Reconciled slice (2026-06-07): the Right axis is a DISCRETE selection of a pre-baked, flow-stabilized restyle variant (not a continuous blend), the Left axis carries its knob LEVEL so a runtime annotation track can pick which labels show, and a frozen `Calibration` parameterizes the knob->strength curves so they can be tuned by eye in the simulator and baked into DEFAULT_CALIBRATION. Layers compose per §4.2: - Substrate: ColorGrade (Dark/Light mood, center = identity §5) + a pre-baked Right restyle variant. - Overlay: AnalyticalOverlay (Left), composited on top at runtime. Left and Right stack (different layers); Dark/Light are the two poles of one mood grade. See docs/superpowers/specs/2026-06-07-reconciled-simulator- alteration-slice-design.md. """ from __future__ import annotations from dataclasses import dataclass from hef.selection import Coordinate KNOB_MAX = 4 # knob full-scale (0..4) def _clamp(x: float, lo: float, hi: float) -> float: return max(lo, min(hi, x)) @dataclass(frozen=True) class Calibration: """Tunable knob->strength curves (settled by eye in the sim, then baked). - mood_gain: scales the signed Dark/Light tone (result clamped to [-1, 1]). - overlay_gain: scales the Left overlay intensity (clamped to [0, 1]). - right_variant_map: knob value (0..4) -> pre-baked Right variant index. """ mood_gain: float = 1.0 overlay_gain: float = 1.0 right_variant_map: tuple = (0, 1, 2, 3, 4) # The LOCKED calibration (session 0010, 2026-06-07) — settled by eye in the # simulator, closing the open session-0006 knob->strength decision. Convention: # every experience knob runs 0 = off .. 4 = max, and equal Dark/Light = identity # (raw footage); there is no "centered at 2 = no push" coordinate. # - mood_gain = 1.0: full Dark/Light knob reaches the full mood grade. By-eye # evidence (POC renders + sim, after the dark-grade fix this session) shows # full tilt is peaceful on every axis, so no softening is warranted. # - overlay_gain = 1.0: full Left = opacity 1.0; the HUD is legible, not # overwhelming, sitting above the grade. # - right_variant_map linear: the 5 knob notches map 1:1 onto the 5 discrete # pre-baked Right strengths (0 = raw base). # These are unity/linear by deliberate choice, not as placeholders. The curve # stays parameterized so a future re-bake or a different feel is one edit away; # test_default_calibration_is_locked guards the values from drifting silently. DEFAULT_CALIBRATION = Calibration( mood_gain=1.0, overlay_gain=1.0, right_variant_map=(0, 1, 2, 3, 4), ) @dataclass(frozen=True) class ColorGrade: """Mood-axis grade (§5). `tone` is signed: >0 warm yellow->white (light), <0 cool blue->black (dark), 0 = identity (raw, ungraded).""" tone: float @property def is_identity(self) -> bool: return self.tone == 0.0 @dataclass(frozen=True) class AnalyticalOverlay: """Left axis (§4.1): analytical HUD/annotation, composited on top at runtime. `level` is the Left knob (0..4); a runtime annotation track uses it to pick which labels appear. `intensity` 0..1 is the overlay opacity/strength.""" level: int intensity: float @dataclass(frozen=True) class Restyle: """Right axis (§4.1): selects a pre-baked, flow-stabilized restyle variant. `variant` is a discrete index (0 = raw base, no restyle).""" variant: int @dataclass(frozen=True) class RenderPlan: """The full layered alteration for one knob vector (§4.2).""" grade: ColorGrade overlay: AnalyticalOverlay restyle: Restyle @property def is_identity(self) -> bool: """True when the plan leaves the neutral base un-altered.""" return ( self.grade.is_identity and self.overlay.level == 0 and self.restyle.variant == 0 ) def _overlay_intensity(left: int, cal: Calibration) -> float: return _clamp(cal.overlay_gain * left / KNOB_MAX, 0.0, 1.0) def _right_variant(right: int, cal: Calibration) -> int: return cal.right_variant_map[right] def _mood_tone(dark: int, light: int, cal: Calibration) -> float: return _clamp(cal.mood_gain * (light - dark) / KNOB_MAX, -1.0, 1.0) def plan_alteration( coord: Coordinate, calibration: Calibration = DEFAULT_CALIBRATION ) -> RenderPlan: """Map a knob vector to its layered RenderPlan (design §4).""" return RenderPlan( grade=ColorGrade(tone=_mood_tone(coord.dark, coord.light, calibration)), overlay=AnalyticalOverlay( level=coord.left, intensity=_overlay_intensity(coord.left, calibration), ), restyle=Restyle(variant=_right_variant(coord.right, calibration)), ) def render_plan_to_dict(plan: RenderPlan) -> dict: """JSON-serializable form for the simulator API.""" return { "grade": {"tone": plan.grade.tone}, "overlay": {"level": plan.overlay.level, "intensity": plan.overlay.intensity}, "restyle": {"variant": plan.restyle.variant}, "is_identity": plan.is_identity, }