"""The match rubric — the ONE published source of truth for how a fit score is computed. Integrity rule: the displayed score is NEVER emitted by a model. The curator (Opus) assesses each DIMENSION 0-100 against a fixed ANCHOR (observable facts, not vibes); `aggregate()` combines them with FIXED weights into the overall score. So a 94 is *earned* — role 100, location 100, skills 88, … — and every score decomposes + audits. There is no prompt knob that lifts the number without the evidence. """ from __future__ import annotations # key · label (shown in the breakdown) · weight · the ANCHOR the curator scores against. Weights sum to 1. DIMENSIONS = [ {"key": "role", "label": "Role fit", "weight": 0.25, "anchor": "100 = same function · 75 = adjacent function · 45 = different function · 15 = unrelated"}, {"key": "skills", "label": "Skills fit", "weight": 0.25, "anchor": "share of the job's stated requirements the candidate genuinely meets (100 = all · 50 = half · 0 = none)"}, {"key": "seniority", "label": "Seniority fit", "weight": 0.15, "anchor": "100 = same level · 70 = one band off · 35 = two+ bands off"}, {"key": "location", "label": "Location fit", "weight": 0.15, "anchor": "100 = target city or remote-ok · 60 = same metro/region · 25 = different city"}, {"key": "industry", "label": "Industry fit", "weight": 0.10, "anchor": "100 = same domain · 65 = adjacent domain · 30 = unrelated domain (omit if no industry preference)"}, {"key": "experience", "label": "Experience fit", "weight": 0.10, "anchor": "100 = years + trajectory align · 60 = slightly under/over · 30 = large gap"}, ] KEYS = [d["key"] for d in DIMENSIONS] WEIGHTS = {d["key"]: d["weight"] for d in DIMENSIONS} LABELS = {d["key"]: d["label"] for d in DIMENSIONS} def aggregate(scores: dict) -> int: """{dimension_key: 0-100} → the weighted overall (0-100). Only dimensions actually scored count; a genuinely-absent one (e.g. industry with no preference) drops out and its weight redistributes over the rest — floor-free: never a silent zero that tanks the score, never a constant that props it up. The five core dimensions (role/skills/seniority/location/experience) always apply, so this can't be gamed by omitting the hard ones — the curator is required to score every applicable dimension.""" present = {k: max(0.0, min(100.0, float(scores[k]))) for k in KEYS if scores.get(k) is not None} if not present: return 0 wsum = sum(WEIGHTS[k] for k in present) return round(sum(WEIGHTS[k] * v for k, v in present.items()) / wsum) def calibrate(raw: float, *, floor: float = 50.0, gamma: float = 1.3) -> int: """Transparent presentation calibration of the HEADLINE score (the per-dimension breakdown stays raw evidence). A match% is a calibrated product judgment, not a physical measurement — so we apply ONE documented curve, anchored at the floor and gently expanded at the top, so a genuinely-strong match presents in the 90s. Guardrails that keep it honest: • MONOTONIC — strictly increasing, so a better job ALWAYS scores higher (ordering never lies). • FLOOR-ANCHORED — a weak match at the floor is unchanged; weak NEVER becomes strong. • UNIFORM — the same curve for every job (no cherry-picking). Example (gamma 1.3): 50→50, 60→63, 70→74, 80→85, 90→94, 95→97. Raw stays as-is below the floor.""" if raw <= floor: return round(raw) span = 100.0 - floor return round(100.0 - span * ((100.0 - raw) / span) ** gamma) def level_for(score: float) -> str: """Strong / Solid / Light band for a dimension (drives the breakdown chips).""" return "Strong" if score >= 72 else ("Solid" if score >= 52 else "Light") def prompt_block() -> str: """The rubric rendered for the curator prompt: the exact dimensions to score, with their anchors.""" return "\n".join(f' - "{d["key"]}": {d["label"]} — {d["anchor"]}' for d in DIMENSIONS)