Pre-launch. Not deployable to production. This page states what the tests prove and what is missing — nothing else.

Lead scoring and routing you can explain a year later

Lead scoring, routing and assignment

People ask this as: lead scoring rules engine and automatic lead routing, open source

Enrichment, scoring and routing over a Lead record — all of it deterministic, none of it wired to a real data vendor. A score is a versioned weighted rule model that persists one contribution row per rule and the fingerprint of the model version that produced it; a routing decision persists one evaluation row per candidate target with the exact reason it was in or out. The enrichment provider is a checked-in fixture, and routing targets are application identifiers rather than authenticated users, so an assignment is a record and not an authorization.

Where this stops

Read this before the rest of the page. Every line below is a thing this does not do.

  • Enrichment calls a checked-in fixture provider. No Clearbit, Apollo, ZoomInfo or any other data source is wired, no credential ships, and no lead in this repository has ever been enriched from the internet.
  • Scoring is a deterministic weighted rule model. Nothing trains, fits, backtests or learns; there is no model artifact, no training set and no accuracy figure to quote.
  • Routing targets are trusted application identifiers, not authenticated users. Assigning a lead to one records a decision; it grants nobody access to anything, because there is no access control to grant.
  • There is no manual reassignment action. The assignment record already carries source, previousAssignmentId and reason so a future override lands as data, but no override exists and no manager permission may be claimed before authentication does.
  • Re-routing an assigned lead is refused, not supported. The only path is assign once; changing your mind is deferred work, not a feature with a rough edge.
  • A scored lead carries a score field and nothing sorts it. There is no prioritized list surface, no saved view, no bulk edit and no global search.
  • The Lead model belongs to the starter, not to the framework core. Copy the starter and you own it; there is no built-in Lead module to upgrade.

No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network. Every claim and every limitation is on one page.

What it models

A lead is captured, then moves through five explicit actions: record-signal appends an immutable behavioral signal deduped by source key; enrich calls a provider and stores an immutable snapshot with its provenance; score runs a versioned model and writes a ScoreRun plus one contribution per rule; route runs a versioned policy and writes a RoutingRun, one evaluation row per candidate, and an assignment. Qualification and conversion then turn the lead into a Company, a Contact and an Opportunity.

Everything the run touched stays readable afterwards. The snapshot, the signals, the score run and its contributions, the routing run and its per-candidate evaluations, and the assignment history are all separate records with get and list only — there is no public create or update on any of them, so no client can write a score or forge an assignment. Traces are queryable by workflow name (lead.enrich, lead.score, lead.route) and every mutation leaves an audit event.

Eligibility for a routing target is active status, a score band and capacity, where current load is the exact indexed count of active leads assigned to that target. Converting or disqualifying a lead releases its slot. Ties break by priority descending, then load ascending, then key — never by randomness, so the same inputs produce the same assignment every time.

How a coding agent builds it

Providers, scoring models, routing policies and targets are checked-in JavaScript definitions registered through one generated index file. The repository ships that file empty; the starter writes it. Each {name, version} pair persists a deterministic declared-definition fingerprint at startup, in a single transaction, so concurrent boots serialize.

Versions are published, never edited. Editing the source of a registered version stops the application on the next boot rather than silently changing what an old score meant. Rollback is publishing a new version derived from an earlier definition — v3 carrying v1's rules — because git history is not runtime policy versioning.

The fingerprint captures a definition's own source plus its declared config, which is passed frozen to every evaluation. A handler that closes over a mutable outer variable or an out-of-file helper is not captured, so every threshold and tunable belongs in config. Unsupported values there — Date, Map, Set, class instances, BigInt, symbols, NaN, cycles — fail fingerprinting loudly rather than producing a fingerprint that does not describe the definition.

The agent-facing instructions are in the build-lead-intelligence skill, mirrored for other harnesses as docs/LEAD_INTELLIGENCE.md.

What the framework refuses

The provider call happens in the action's prepare phase, outside the write transaction. A provider outage, timeout or out-of-contract payload becomes a stable PROVIDER_FAILED, PROVIDER_TIMEOUT or PROVIDER_INVALID with nothing persisted and an honest failed trace — never a partial snapshot and never an invented value.

Routing refuses an unscored lead with 409 LEAD_NOT_SCORED and an already-assigned one with 409 ALREADY_ASSIGNED. When no target is eligible it falls to the single declared fallback queue with a recorded reason, and if there is no fallback it refuses with 409 NO_ELIGIBLE_TARGET rather than picking someone. Under concurrency across connections exactly one final assignment survives; a race for the last capacity slot never oversubscribes a target.

Enrich, score and record-signal run only from the new and qualified states; route runs only from new. Converted and disqualified leads sit outside the intelligence lifecycle, enforced server-side by declared from-states and merely mirrored by the Admin.

What proves it

tests/lead-intelligence-e2e.test.js drives capture through assignment, including the provider failure modes, the concurrency race for a last capacity slot, and the reproduction of a historical routing decision after the target data has changed. tests/intelligence-contract.test.js holds the definition and fingerprint contract, and the qualification and conversion suites cover the lead's own lifecycle.

The starter application runs three leads end to end and lands them on Enterprise Italy, Spain Sales and the fallback queue respectively — a composed run, not a fixture assertion.

Where it stops

There is no real data vendor, no ranked work queue, no manual reassignment and no capacity calendar. The parts that would make any of those safe — authentication, tenancy and role enforcement — are Production Spine work that has not been done, and the boundary is stated here rather than left for a reader to discover after they have loaded real people into it.

The evidence this page rests on

Claims and limitations are printed from site/claims.json word for word. Job statuses come from docs/benchmarks/jobs.json; a job with no page of its own is listed with its status rather than linked.

Claims

  • C-06 A lead is captured, scored, routed, qualified and converted into Company, Contact and Opportunity through explicit actions, each one atomic and audited.

    LimitEnrichment runs against a fixture provider — no real external data source is wired. The Lead model is the starter's, not a built-in core module.

  • C-07 Scoring is explainable and versioned: every score carries the fingerprint of the model version that produced it, so a number from last quarter can still be accounted for.

    LimitDeterministic weighted rules, not a machine-learning model. Nothing trains, fits or backtests.

  • C-16 Every mutation goes through a module service or a named workflow, and leaves an audit event and a step-level trace behind it.

    LimitAudit records what the process did under an asserted actor. It is not a tamper-evident or externally attestable log, and it is not a compliance control.

Limitations

  • L-01 No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network.
  • L-06 No import, export, dedupe, merge, bulk edit, saved views or global search. Table stakes in every commercial CRM, and none of them has a milestone yet. This is recorded deliberately rather than left for you to discover.
  • L-09 You cannot put real customer data in this yet. A CRM is a personal-data system by definition, and this one has no authentication, no tenancy, no export and no erasure path — so there is no way to service a data-subject access or deletion request with it. Data governance is designed and unimplemented. The one thing that does hold today: lead scoring is deterministic, versioned and explainable, not a model's judgement about a person.

Jobs it covers