build_tools.name_selector.policy ================================ .. py:module:: build_tools.name_selector.policy .. autoapi-nested-parse:: Policy evaluation logic for name candidates. This module contains the core evaluation function that scores a name candidate against a name class policy. It implements the ✓/~/✗ scoring model defined in the Name Class Matrix. Scoring Model ------------- - **Preferred (✓)**: Feature present → +1 score - **Tolerated (~)**: Feature present → 0 score (neutral) - **Discouraged (✗)**: Feature present → Reject (hard) or -10 (soft) The evaluation considers only features that are TRUE in the candidate. Features that are FALSE do not contribute to the score (absence is neutral). Evaluation Modes ---------------- **Hard Mode** (default): Any discouraged feature present causes immediate rejection. The candidate is not scored further. **Soft Mode**: Discouraged features apply a -10 penalty instead of rejection. Useful for exploring edge cases or when flexibility is needed. Usage ----- >>> from build_tools.name_selector.policy import evaluate_candidate >>> from build_tools.name_selector.name_class import NameClassPolicy >>> >>> policy = NameClassPolicy( ... name="first_name", ... description="Test", ... syllable_range=(2, 3), ... features={"ends_with_vowel": "preferred", "ends_with_stop": "discouraged"}, ... ) >>> candidate = { ... "name": "kali", ... "features": {"ends_with_vowel": True, "ends_with_stop": False}, ... } >>> admitted, score, details = evaluate_candidate(candidate, policy, mode="hard") >>> admitted True >>> score 1 >>> details["preferred_hits"] ['ends_with_vowel'] Attributes ---------- .. autoapisummary:: build_tools.name_selector.policy.PREFERRED_SCORE build_tools.name_selector.policy.TOLERATED_SCORE build_tools.name_selector.policy.DISCOURAGED_PENALTY Functions --------- .. autoapisummary:: build_tools.name_selector.policy.evaluate_candidate build_tools.name_selector.policy.check_syllable_count Module Contents --------------- .. py:data:: PREFERRED_SCORE :value: 1 .. py:data:: TOLERATED_SCORE :value: 0 .. py:data:: DISCOURAGED_PENALTY :value: -10 .. py:function:: evaluate_candidate(candidate, policy, mode = 'hard') Evaluate a name candidate against a name class policy. Scores the candidate based on which of its TRUE features match preferred, tolerated, or discouraged designations in the policy. Parameters ---------- candidate : dict Candidate dictionary with "name", "features", and optionally "syllables". Features must be a dict[str, bool]. policy : NameClassPolicy The policy to evaluate against. mode : {"hard", "soft"}, optional Evaluation mode. "hard" rejects on any discouraged feature. "soft" applies a -10 penalty instead. Default: "hard". Returns ------- tuple[bool, int, dict] - admitted: True if candidate passes policy, False if rejected - score: Numeric score (higher is better) - details: Evaluation details for debugging Details dict structure: - preferred_hits: list[str] - Preferred features that are TRUE - tolerated_hits: list[str] - Tolerated features that are TRUE - discouraged_hits: list[str] - Discouraged features that are TRUE - rejection_reason: str | None - Reason for rejection (if any) Examples -------- >>> # Candidate with preferred feature >>> candidate = {"name": "kali", "features": {"ends_with_vowel": True}} >>> admitted, score, details = evaluate_candidate(candidate, policy) >>> admitted, score (True, 1) >>> # Candidate with discouraged feature (hard mode) >>> candidate = {"name": "kalt", "features": {"ends_with_stop": True}} >>> admitted, score, details = evaluate_candidate(candidate, policy, mode="hard") >>> admitted False >>> details["rejection_reason"] 'ends_with_stop' Notes ----- Only TRUE features are evaluated. If a feature is FALSE in the candidate, it does not contribute to the score regardless of its policy designation. This means "discouraged" means "discouraged when present", not "required to be absent". .. py:function:: check_syllable_count(candidate, policy) Check if a candidate's syllable count is within policy range. Parameters ---------- candidate : dict Candidate dictionary with "syllables" key (list of syllable strings). policy : NameClassPolicy The policy with syllable_range constraint. Returns ------- bool True if syllable count is within range, False otherwise. Examples -------- >>> policy = NameClassPolicy(..., syllable_range=(2, 3)) >>> check_syllable_count({"syllables": ["ka", "li"]}, policy) True >>> check_syllable_count({"syllables": ["ka"]}, policy) False