"""Small write/decision boundary for the journal MVP. Not a policy engine.""" from __future__ import annotations from conversation_signals import similar_enough class PolicyError(Exception): def __init__(self, code: str, message: str, status_code: int = 400): super().__init__(message) self.code = code self.message = message self.status_code = status_code ENTRY_ORIGINS = {"user_edit", "accepted_draft", "restore"} def require_explicit_generate(explicit: bool) -> None: if not explicit: raise PolicyError("generate_not_explicit", "Journal Drafts entstehen nur auf ausdrückliche Aktion.") def consolidation_offer(conversations: list[dict] | int) -> bool: """Offer merge only when conversations look narratively compatible. User decides.""" if isinstance(conversations, int): return False return similar_enough(conversations) def source_conversation_ids(explicit_ids: list[str] | None, day_conversation_ids: list[str]) -> list[str]: """Missing selection never silently merges multiple conversations.""" known = [item for item in day_conversation_ids if item] if explicit_ids: chosen = [item for item in explicit_ids if item in known] if not chosen: raise PolicyError("unknown_conversation", "Keine der angegebenen Conversations gehört zu diesem Tag.") return chosen if not known: raise PolicyError("no_source_dialogue", "Ohne Dialog gibt es nichts zu erzeugen.") return [known[-1]] def draft_must_not_touch_entry() -> None: return None def require_origin(origin: str) -> str: if origin not in ENTRY_ORIGINS: raise PolicyError("invalid_origin", "origin muss user_edit, accepted_draft oder restore sein") return origin