//ARCHITECTURE
How Verdict works
Eleven stages, run in this order for a reason: identity resolution before spend, extraction before verification, verification before scoring, scoring before a draft is ever written.
Validate + idempotency
00Ordinary code. Required fields, email/domain normalization. Replaying a submission_id returns the existing result — never reprocesses.
Identity resolution
01Runs BEFORE enrichment/scoring, deliberately. Exact email or domain match only counts as "confident" — name similarity, even a perfect score, never auto-merges.
Classify message
02Haiku labels the message into approved categories. Vendor pitches, job applications, and student research hard-disqualify here, before any research spend.
Find research source
03Looks up a domain in the seeded fixture corpus. An ambiguous or missing identity means no safe source to pick — enrichment correctly returns nothing rather than guessing.
Extract facts
04Haiku reads the source page and extracts only explicitly stated facts into a fixed field schema, with a verbatim supporting quote for each. There is no field for a score, a discount, or an instruction — structurally, nothing else can land here.
Verify facts
05Two independent checks per fact: a deterministic quote-in-source substring check (free, catches fabricated quotes), then a batched Haiku pass judging entailment and flagging quotes that read as instructions rather than facts. Only what survives both becomes "verified".
Evidence-sufficiency gate
06Ordinary code. Counts resolved ICP criteria against a floor. Below the floor: no score is ever computed — the lead returns the specific questions that would unblock it.
Score (if the gate is open)
07Deterministic points-based scoring across 7 criteria, each with cited evidence. A veto on B2B-fit or region disqualifies regardless of the numeric total.
Propose CRM change set
08A diff, never a write. Possible identity matches propose nothing — a human resolves the merge. Confident matches propose an update; new leads propose a create.
Draft outreach (gated)
09Only for qualified bands, and only using the lead's own message plus verified facts. Every claim the draft makes is independently re-verified against that same grounding text — never trusted from the drafting call's self-report.
Audit + jobs
10Every stage above writes a started/completed/failed audit event with duration, committed immediately so a poller sees stages land one at a time. A Postgres jobs table tracks attempts — the durable queue, not an external service.
//THE STACK
Boring where it should be boring
Extension point: workflow automation
A sales-ready qualification can fire a webhook to an external automation platform
(n8n) to notify a rep, enrich a CRM record, or kick off a sequence — the pipeline
already exposes a fire-and-forget hook (verdict/domain/notify.py) for exactly this,
bounded and non-blocking so a downstream outage never affects a demo visitor.
Deliberately not wired to a live workflow in the public demo — that stays inactive
by design, not by omission.