"""Heuristic coordinate proposer — a deterministic, rule-based DRAFT seed. The four curatorial coordinates are a human act (design §11: no automatic ML coordinate tagging). This proposer only suggests a starting point written as review_status='proposed'; a human blesses every coordinate before selection. There is no ML model here — just archive priors (brain plane) and keyword nudges (mood plane), grounded in the §8 sourcing→axis map. """ from __future__ import annotations from dataclasses import dataclass from typing import Protocol from hef.selection import Coordinate COORD_MIN, COORD_MAX = 0, 4 # Brain plane (left = verbal/spoken/educational, right = music/wordless/abstract). _ARCHIVE_PRIORS = { "librivox": (4, 0), # spoken readings "internet_archive": (3, 1), # educational / industrial / Prelinger "musopen": (0, 4), # classical music "fma": (0, 4), # music "nasa": (0, 4), # wordless awe "freesound": (0, 4), # abstract field recordings } _ARCHIVE_DEFAULT = (2, 2) # Mood plane keywords (substring match so "thunderstorm" trips "storm"). _DARK_WORDS = ( "storm", "night", "noir", "requiem", "minor", "war", "funeral", "decay", "death", "grief", "dusk", "winter", "mourning", "shadow", ) _LIGHT_WORDS = ( "sunrise", "dawn", "garden", "spring", "joy", "hope", "major", "bright", "bloom", "summer", "smile", "light", "morning", ) def _clamp(v: int) -> int: return max(COORD_MIN, min(COORD_MAX, v)) @dataclass class Signals: title: str description: str source_archive: str mode: str duration_s: int @dataclass class Draft: coordinate: Coordinate rationale: str class Proposer(Protocol): def propose(self, signals: Signals) -> Draft: ... class HeuristicProposer: def propose(self, signals: Signals) -> Draft: left, right = _ARCHIVE_PRIORS.get( signals.source_archive, _ARCHIVE_DEFAULT ) text = f"{signals.title} {signals.description}".lower() dark_hits = [w for w in _DARK_WORDS if w in text] light_hits = [w for w in _LIGHT_WORDS if w in text] dark = _clamp(len(dark_hits)) light = _clamp(len(light_hits)) coordinate = Coordinate(_clamp(left), _clamp(right), dark, light) # Rationale: name the dominant brain prior + any mood keywords. brain = ( f"{signals.source_archive} → " + ("strong left" if left >= right else "strong right") ) mood_bits = [] if dark_hits: mood_bits.append(f"{dark_hits[0]!r} → dark") if light_hits: mood_bits.append(f"{light_hits[0]!r} → light") rationale = "; ".join([brain, *mood_bits]) return Draft(coordinate=coordinate, rationale=rationale)