Three fixtures, and a guardrail that runs before the model.

The ideal-customer profile is a written rubric, not a black box. A completeness check runs first, so a malformed lead never reaches the scorer at all.

the rubric the scorer is held to, verbatim from the code
Sells to other BUSINESSES (B2B), not consumers. This one is required.
Has, or needs to fill, an outbound sales motion.
Headcount 11 to 500: big enough for real deal value,
  small enough to outsource lead gen.
Deal size that justifies cold outreach (not low-ticket B2C).
A buying signal in their message raises the tier.

returns {"tier":"HOT|WARM|COLD","score":0-100,
         "reasoning":"one line citing a specific enrichment fact",
         "confidence":0.0-1.0,"missing_signals":[...]}
fixture 1 · intended HOT
Westbrook Analytics

"Our 3 AEs need more qualified demos booked and outbound is barely touched. Can you fill the calendar?"

enrichment · B2B SaaS / Analytics · headcount 62 · Series A · has sales team true · HubSpot, Outreach
in every band of the rubricHOT is the only tier that gets a drafted first touch
fixture 2 · intended COLD
Sunroom Yoga Studio
enrichment · Consumer / Fitness · headcount 2 · bootstrapped · has sales team false · Mindbody
consumer, not B2Bbelow the headcount bandno sales motion to fill
fixture 3 · the guardrail, decided in code before the model runs
tom@gmial · no company, unresolvable domain
if (!emailValid || !domainValid || !clean.company) {
  return { route: 'review',
    reason: `Held for human review: ${why}.` };
}
route: reviewnever reaches the scorer
Two more things are decided in code, not by the model: a confidence floor of 0.6 sends anything the scorer is unsure about to human review with the reason attached, and a scorer reply that is not valid JSON is held rather than guessed at. Only a HOT lead gets a drafted first touch, and a human still sends it. Fixtures are synthetic and use reserved example.com domains.